/*
 * Composants de base (DESIGN.md §3-§7, v2). L'app est posée sur un canvas
 * gris chaud ; les surfaces de travail flottent dessus avec une ombre douce.
 * La couleur reste une information ; les actions sont des pilules.
 */

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

/*
 * 🔴 `[hidden]` gagne toujours.
 *
 * Le navigateur applique `display: none` aux éléments `[hidden]` via sa
 * feuille de style par défaut — donc **n'importe quelle** règle à nous qui
 * pose un `display` l'écrase, sans avertissement. Le défaut s'est produit
 * deux fois : toutes les fiches client s'affichaient d'un coup
 * (`display: grid`), et la barre « Ordre modifié — non enregistré »
 * restait affichée en permanence (`display: flex`), annonçant un
 * changement qui n'avait pas eu lieu.
 *
 * Le `!important` est ici la règle et non l'exception : `hidden` est une
 * affirmation sur l'état, pas une suggestion de mise en page. Un élément
 * qu'on masque en JavaScript doit disparaître, quel que soit le
 * `display` que son composant lui donne par ailleurs.
 */
[hidden] { display: none !important; }

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

body {
  margin: 0;
  background: var(--bg-app);
  color: var(--text-primary);
  font-family: var(--font-sans);
  font-size: 15px;            /* corps : un cran au-dessus du SaaS standard */
  line-height: 1.5;
}

.lien-evitement {
  position: fixed;
  top: var(--space-2);
  left: var(--space-2);
  z-index: 300;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-control);
  background: var(--accent);
  color: var(--accent-contrast);
  font-weight: 600;
  transform: translateY(calc(-100% - var(--space-3)));
  transition: transform var(--transition);
}

.lien-evitement:focus {
  color: var(--accent-contrast);
  text-decoration: none;
  transform: translateY(0);
}

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

h1, .titre-page {
  font-size: 22px;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.2;
  margin: 0 0 var(--space-4);
}

h2, .titre-section {
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.01em;
  margin: 0 0 var(--space-3);
}

.texte-secondaire { color: var(--text-secondary); }
/* Un mot en rouge dans une phrase par ailleurs neutre — le marqueur ⚠ des
   allergies au téléphone. La couleur est une information (DESIGN.md §1). */
.texte-danger { color: var(--danger); }
.meta { font-size: 12px; color: var(--text-secondary); }

/* Les trois lignes « Ses droits » de la fiche (RGPD.md §6 lot 5). Elles
   ne peuvent pas être des `<p>` — un `<form>` ne vit pas dans un
   paragraphe : le navigateur le ferme avant lui, et le geste tombe sous
   sa phrase au lieu de la terminer. Un `div` n'a pas la marge d'un
   paragraphe, elle se repose donc ici, pour que les trois lignes gardent
   le rythme vertical du reste de la carte. */
.liste-droits__ligne { margin: 0 0 var(--space-2); }

.chiffre-cle {
  font-size: 28px;
  font-weight: 600;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
}

.montant { font-variant-numeric: tabular-nums; text-align: right; }

/* Un chiffre qui appelle une action, pas seulement une information. */
.chiffre-cle--alerte { color: var(--warning); }

/* Fin du bleu navigateur (DESIGN.md §6) : un lien est du texte encre un peu
   plus affirmé, souligné au survol. Le bleu `--info` redevient une couleur
   sémantique, jamais un style d'hyperlien. */
a { color: var(--text-primary); font-weight: 500; text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }

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

/* ---------- Layout back-office (DESIGN.md §5.1) ---------- */

/* Le liséré extérieur (demande du porteur, 23 août 2026) est une **ombre
   interne** et non une bordure : sur un élément déjà large de 100 % et
   haut de 100vh, deux pixels de bordure suffiraient à faire défiler la
   page dans les deux sens. Le rendu est le même, le coût de mise en page
   nul. */
.app {
  display: flex;
  min-height: 100vh;
  box-shadow: inset 0 0 0 1px var(--border);
}

.sidebar-mobile__barre,
.sidebar-mobile__entete,
.sidebar-mobile__voile { display: none; }

/* Les menus se détachent du plan de travail (demande du porteur, 23 août
   2026, qui remplace la règle « la sidebar vit sur le canvas » de
   DESIGN.md §5.1) : barre latérale et topbar prennent la surface, la zone
   de travail garde le canvas. Le trait de séparation dit où finit la
   navigation et où commence le contenu. */
.sidebar {
  width: var(--sidebar-width);
  flex-shrink: 0;
  background: var(--bg-surface);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  padding: var(--space-3);
  gap: var(--space-1);
}

@media (max-width: 800px) {
  .app { display: block; }

  .sidebar-mobile__barre {
    min-height: 52px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-4);
    border-bottom: 1px solid var(--border);
    background: var(--bg-surface);
  }

  .sidebar-mobile__ouvrir,
  .sidebar-mobile__fermer {
    min-width: 44px;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    padding: var(--space-2);
    border: 0;
    border-radius: var(--radius-control);
    background: transparent;
    color: var(--text-primary);
    font: inherit;
    font-weight: 500;
    cursor: pointer;
  }

  .sidebar-mobile__ouvrir:hover,
  .sidebar-mobile__fermer:hover { background: var(--bg-raised); }

  .sidebar-mobile__marque {
    overflow: hidden;
    color: var(--text-secondary);
    font-size: 13px;
    font-weight: 600;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .sidebar-mobile__entete {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: var(--space-2);
    padding-left: var(--space-3);
    font-size: 13px;
    font-weight: 600;
  }

  .sidebar {
    width: min(86vw, 320px);
    max-height: 100dvh;
    overflow-y: auto;
    /* Le tiroir mobile recouvre le contenu : il lui faut un fond plein,
       contrairement à la sidebar posée sur le canvas au bureau. */
    background: var(--bg-app);
    box-shadow: var(--shadow-float);
  }

  .js .sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    z-index: 40;
    transform: translateX(-101%);
    transition: transform var(--transition);
  }

  .js .sidebar-mobile__voile {
    position: fixed;
    inset: 0;
    z-index: 30;
    width: 100%;
    height: 100%;
    border: 0;
    background: rgb(0 0 0 / 0.42);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--transition);
  }

  .js.sidebar-mobile-ouverte .sidebar { transform: translateX(0); }

  .js.sidebar-mobile-ouverte .sidebar-mobile__voile {
    display: block;
    opacity: 1;
    pointer-events: auto;
  }

  .sidebar-mobile-ouverte { overflow: hidden; }
  .topbar { overflow-x: auto; padding: 0 var(--space-4); white-space: nowrap; }
  .contenu { padding: var(--space-6) var(--space-4); }
}

@media (prefers-reduced-motion: reduce) {
  .js .sidebar,
  .js .sidebar-mobile__voile { transition: none; }
}

.sidebar__marque {
  font-weight: 600;
  padding: var(--space-2) var(--space-3);
  margin-bottom: var(--space-3);
}

/* Intitulés des trois groupes de navigation (DESIGN.md §14 Q10) :
   petites capitales discrètes, ils nomment sans se cliquer. */
.sidebar__groupe-titre {
  margin: var(--space-4) 0 var(--space-1);
  padding: 0 var(--space-3);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-tertiary);
}

.sidebar__groupe-titre:first-of-type { margin-top: var(--space-2); }

/*
 * Menu d'agence — remplace `.sidebar__marque` seule dès qu'il y a un choix
 * réel à faire (plusieurs agences, ou le droit d'en créer une). `.menu-agence`
 * porte le positionnement du panneau flottant ; l'ouverture/fermeture est
 * gérée par static/js/menu.js (data-menu). Chaque item reste un bouton de
 * `<form method="post">` : sans JavaScript, cliquer soumet directement le
 * choix au lieu de rester inerte.
 */
.menu-agence { position: relative; margin-bottom: var(--space-3); }

.menu-agence__actuelle {
  width: 100%;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border: none;
  border-radius: var(--radius-control);
  background: transparent;
  font: inherit;
  font-weight: 600;
  color: var(--text-primary);
  cursor: pointer;
  transition: background var(--transition);
}

.menu-agence__actuelle:hover { background: var(--bg-raised); }

.menu-agence__nom {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.menu-agence__logo {
  max-height: 20px;
  max-width: 28px;
  object-fit: contain;
  flex-shrink: 0;
}

.menu-agence__chevron { margin-left: auto; color: var(--text-tertiary); }

.menu-agence__panneau {
  position: absolute;
  top: calc(100% + var(--space-1));
  left: 0;
  right: 0;
  z-index: 10;
  background: var(--bg-surface);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-float);
  padding: var(--space-2);
  animation: surgissement 160ms ease-out;
  transform-origin: top center;
}

/* Les surfaces flottantes arrivent, elles n'apparaissent pas (DESIGN.md §7). */
@keyframes surgissement {
  from { opacity: 0; transform: translateY(4px) scale(0.98); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .menu-agence__panneau, .menu-filtres__panneau, .menu-actions__panneau {
    animation: none;
  }
}

.menu-agence__forme { display: contents; }

.menu-agence__item {
  width: 100%;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-2);
  border: none;
  border-radius: var(--radius-control);
  background: transparent;
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background var(--transition);
}

.menu-agence__item:hover { background: var(--bg-raised); }

.menu-agence__item--actif { background: var(--bg-raised); }

.menu-agence__pastille {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-control);
  background: var(--bg-inset);
  color: var(--text-secondary);
  font-size: 12px;
  font-weight: 600;
}

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

.menu-agence__item-nom {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--text-primary);
  font-size: 13px;
  font-weight: 500;
}

.menu-agence__item-ville { color: var(--text-secondary); font-size: 12px; }

.menu-agence__ajouter {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  margin-top: var(--space-2);
  padding: var(--space-2) var(--space-2);
  border-top: 1px solid var(--border);
  border-radius: 0 0 var(--radius-control) var(--radius-control);
  color: var(--text-secondary);
  font-size: 13px;
  font-weight: 500;
}

.menu-agence__ajouter:hover { background: var(--bg-raised); text-decoration: none; }

.sidebar__lien {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-control);
  color: var(--text-secondary);
  font-size: 13px;
  font-weight: 500;
  transition: background var(--transition), color var(--transition);
}

.sidebar__lien:hover { background: var(--bg-raised); text-decoration: none; }

/* `.sidebar__lien` sert aussi sur un <button> (bascule de thème) : sans ce
   reset, les styles natifs du navigateur (bordure, fond, alignement)
   passent devant ceux, déjà posés ci-dessus, qui l'habillent en lien de
   sidebar. */
button.sidebar__lien {
  width: 100%;
  border: 0;
  background: transparent;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

/* L'élément actif prend la couleur du **canvas**, celle de la zone de
   travail (23 août 2026, depuis que la barre latérale a son propre fond) :
   la pilule active se creuse au lieu de flotter, et le sens est le même
   dans les deux thèmes — l'entrée ouverte est celle qui laisse voir la
   page. Une surface blanche + ombre, comme avant, aurait disparu sur une
   barre désormais blanche elle aussi. */
.sidebar__lien--actif {
  background: var(--bg-app);
  color: var(--text-primary);
  font-weight: 600;
}

.sidebar__ligne {
  display: flex;
  align-items: center;
  gap: var(--space-1);
}

.sidebar__ligne .sidebar__lien { flex: 1; min-width: 0; }

.sidebar__sous-menu-declencheur {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-control);
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
}

.sidebar__sous-menu-declencheur:hover { background: var(--bg-raised); color: var(--text-primary); }

.sidebar__sous-menu-declencheur:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

.sidebar__chevron {
  width: 16px;
  height: 16px;
  transition: transform var(--transition);
}

.sidebar__sous-menu-declencheur[aria-expanded="true"] .sidebar__chevron { transform: rotate(180deg); }

/* Fonctionnalité pas encore construite : visible dans le plan, pas
   cliquable. `pointer-events: none` neutralise à la fois le survol et le
   clic — sans lui un lien mort (href="#") a l'air identique aux liens qui
   marchent, ce qui trompe autant qu'un vrai bug. */
.sidebar__lien--desactive {
  color: var(--text-tertiary);
  pointer-events: none;
}

.sidebar__badge-bientot {
  margin-left: auto;
  font-size: 10px;
  letter-spacing: 0.03em;
  color: var(--text-tertiary);
  white-space: nowrap;
}

/*
 * Sous-menu d'une section (Tournées : Planification / Suivi de livraison /
 * Paramètres) — replié tant qu'on n'est pas dans la section, pour ne pas
 * allonger la sidebar de trois lignes sur tous les autres écrans de l'app.
 * Même famille visuelle que `.sidebar__lien`, en plus petit et sans icône :
 * ce sont des pages d'un même module, pas des sections au même niveau.
 */
.sidebar__sous-menu {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  margin: var(--space-1) 0 var(--space-2);
  padding-left: calc(var(--space-3) + 18px + var(--space-3));
}

.sidebar__sous-lien {
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-control);
  color: var(--text-secondary);
  font-size: 12px;
  font-weight: 500;
  transition: background var(--transition), color var(--transition);
}

.sidebar__sous-lien:hover { background: var(--bg-raised); text-decoration: none; }

.sidebar__sous-lien--actif {
  background: var(--bg-surface);
  box-shadow: var(--shadow-card);
  color: var(--text-primary);
  font-weight: 600;
}

/* Icônes de navigation : le contour hérite de la couleur du texte (actif/
   inactif/survol) via currentColor, déclaré sur chaque <symbol> du sprite
   — une seule source de vérité, pas de règle de couleur dupliquée par état. */
.icone {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
}

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

/*
 * Fil d'Ariane : chaque gabarit injecte directement son contenu dans
 * `.topbar` (aucun composant partagé — voir {% block fil_ariane %}), donc
 * le style tient entièrement à trois règles génériques. Les ancêtres sont
 * des `<a>`, la page courante est du texte nu : la couleur/graisse par
 * défaut du conteneur habille la page courante, `.topbar a` habille les
 * ancêtres. Le séparateur est le seul élément que les gabarits doivent
 * poser eux-mêmes (`<span class="fil-ariane__separateur">`).
 */
.topbar {
  height: var(--topbar-height);
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: 0 var(--space-6);
  /* Même surface que la barre latérale (demande du porteur, 23 août
     2026) : les deux menus forment un cadre, la zone de travail garde le
     canvas. */
  background: var(--bg-surface);
  border-bottom: 1px solid var(--border);
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary);
}

/* Replier la barre latérale (23 août 2026). Le bouton n'existe que là où
   la barre occupe une colonne : sous 800 px elle glisse déjà par-dessus la
   page (`sidebar-nav.js`) et « Menu » fait le même travail. Sans
   JavaScript il ne s'affiche pas non plus — il n'aurait rien à replier. */
.topbar__replier { display: none; flex-shrink: 0; }

.js .topbar__replier { display: inline-flex; }

.topbar__replier--ferme { display: none; }

.sidebar-repliee .topbar__replier--ouvert { display: none; }
.sidebar-repliee .topbar__replier--ferme { display: inline; }

.sidebar-repliee .sidebar {
  /* `width: 0` et non `display: none` : la barre garde sa place dans le
     flux, la transition a donc une largeur à animer, et le lecteur
     d'écran retrouve la navigation dans l'ordre du document dès qu'on la
     rouvre. */
  width: 0;
  padding-left: 0;
  padding-right: 0;
  border-right: 0;
  overflow: hidden;
}

@media (min-width: 801px) {
  .js .sidebar { transition: width var(--transition), padding var(--transition); }
}

@media (max-width: 800px) {
  /* Le pli ne vaut que sur la mise en page à deux colonnes : sur mobile la
     barre est un tiroir, et la replier la ferait disparaître pour de bon. */
  .topbar__replier,
  .js .topbar__replier { display: none; }

  .sidebar-repliee .sidebar {
    width: auto;
    padding: var(--space-3);
    overflow: visible;
  }
}

.topbar a {
  color: var(--text-tertiary);
  font-weight: 500;
}

.topbar a:hover { color: var(--text-secondary); }

.fil-ariane {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  /* Le fil cède la place avant de pousser le compte hors de l'écran : sur
     une topbar pleine (recherche, cloche, avatar), le nom de la fiche se
     tronque — il est de toute façon écrit en entier dans la page (Q2).
     Défaut mesuré sur la fiche bénéficiaire à 390 px (27 août 2026) :
     15 px de défilement horizontal de toute la page, que la pose de la
     cloche a rendu visible sans en être la cause. */
  flex: 1;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
}

/* Recherche globale (DESIGN.md §5.1) : une pilule qui flotte sur le canvas,
   premier objet de la topbar — le titre, lui, vit dans la page. */
.topbar-recherche {
  position: relative;
  display: flex;
  align-items: center;
  width: min(340px, 100%);
  flex-shrink: 1;
}

.topbar-recherche input {
  width: 100%;
  min-height: 36px;
  font: inherit;
  font-size: 13px;
  font-weight: 400;
  color: var(--text-primary);
  /* Creusée dans la barre plutôt que posée dessus : depuis que la topbar
     a le fond `--bg-surface`, une pilule blanche à ombre douce s'y serait
     confondue. Même retournement que la pilule active de la barre
     latérale. */
  background: var(--bg-app);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  padding: var(--space-2) 58px var(--space-2) calc(var(--space-3) * 2 + 14px);
}

.topbar-recherche input::placeholder { color: var(--text-faint); }

.topbar-recherche input:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

.topbar-recherche__icone {
  position: absolute;
  left: var(--space-3);
  width: 15px;
  height: 15px;
  color: var(--text-tertiary);
  pointer-events: none;
}

.topbar-recherche__raccourci {
  position: absolute;
  right: var(--space-2);
  padding: 2px 8px;
  border-radius: 6px;
  background: var(--bg-inset);
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--text-tertiary);
  pointer-events: none;
  white-space: nowrap;
}

.fil-ariane__separateur {
  color: var(--text-tertiary);
  font-weight: 400;
}

.fil-ariane__separateur::before { content: "/"; }

/* Fil d'Ariane sans aucun ancêtre cliquable (PLAN §11) : les segments
   parents ne sont pas des `<a>`, donc ils portent la classe explicitement. */
.fil-ariane__miette { color: var(--text-tertiary); font-weight: 500; }

.contenu {
  width: 100%;
  max-width: var(--content-max-width);
  margin: 0 auto;
  padding: var(--space-8) var(--space-6);
}

/* ---------- Boutons (5 états : défaut, hover, pressed, disabled, loading) ---------- */

/* Pilules (DESIGN.md §4, §6) : hauteur 40px, retour pressé immédiat. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  min-height: 40px;
  padding: var(--space-2) var(--space-5);
  border-radius: var(--radius-pill);
  border: 1px solid transparent;
  /* Une pilule ne se replie jamais : un libellé sur deux lignes en fait un
     ovale difforme — c'est la barre d'actions qui passe à la ligne, pas le
     texte du bouton. */
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--transition), border-color var(--transition),
    opacity var(--transition), transform var(--transition);
}

.btn:active { transform: scale(0.98); }

.btn--primaire {
  background: var(--accent);
  color: var(--accent-contrast);
}
.btn--primaire:hover { opacity: 0.88; }
.btn--primaire:active { opacity: 0.78; }

.btn--secondaire {
  background: var(--bg-surface);
  color: var(--text-primary);
  border-color: var(--border-strong);
  box-shadow: var(--shadow-card);
}
.btn--secondaire:hover { background: var(--bg-raised); }

.btn--ghost {
  background: transparent;
  color: var(--text-secondary);
}
.btn--ghost:hover { background: var(--bg-raised); color: var(--text-primary); }

/* Réservé au bouton qui déclenche vraiment une suppression définitive —
   celui de la boîte de dialogue de confirmation, jamais celui qui l'ouvre
   (DESIGN.md §1 : le rouge partout ne dit plus rien). Posé sur ce seul
   bouton, il apparaît exactement au moment où le geste va se produire. */
.btn--danger {
  background: var(--danger);
  color: var(--accent-contrast);
}
.btn--danger:hover { opacity: 0.88; }
.btn--danger:active { opacity: 0.78; }

.btn--actif,
.btn[aria-current="page"],
.btn[aria-pressed="true"] {
  background: var(--bg-raised);
  border-color: var(--border-strong);
  color: var(--text-primary);
  font-weight: 600;
}

.btn:disabled, .btn[aria-busy="true"] {
  opacity: 0.5;
  cursor: not-allowed;
}

.btn__spinner {
  width: 16px;
  height: 16px;
  border: 1.5px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: rotation 0.7s linear infinite;
}

@keyframes rotation { to { transform: rotate(360deg); } }

.chargement-htmx {
  position: fixed;
  top: var(--space-3);
  right: var(--space-3);
  z-index: 60;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  background: var(--bg-surface);
  box-shadow: var(--shadow-float);
  color: var(--text-secondary);
  font-size: 13px;
  opacity: 0;
  pointer-events: none;
  transform: translateY(-4px);
  transition: opacity var(--transition), transform var(--transition);
}

.htmx-en-cours .chargement-htmx {
  opacity: 1;
  transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
  .btn__spinner { animation: none; }
  .chargement-htmx { transition: none; }
  .btn:active { transform: none; }
}

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

.champ { margin-bottom: var(--space-4); }

.champ label {
  display: block;
  font-size: 13px;
  font-weight: 500;
  margin-bottom: var(--space-1);
}

/*
 * 🔴 Une case à cocher n'est pas un champ de saisie.
 *
 * Ce bloc (largeur 100 %, hauteur 40px, fond, bordure, rayon) s'appliquait
 * à **tous** les `input` du champ — cases et boutons radio compris, qui
 * devenaient des carrés arrondis de 40px détachés de leur libellé. Le
 * défaut a été vu sur « Jours de tournée », mais il touchait aussi les
 * radios de l'écran de validation du scan et de la page famille.
 *
 * Le `:not()` est la seule forme qui tienne : neutraliser ensuite les
 * propriétés une à une laisserait le navigateur rendre la case en mode
 * « stylée » plutôt qu'en natif, et le geste au doigt s'en ressent.
 */
/*
 * `:where()` autour de la négation : sans lui, chaque `:not([type=…])`
 * ajoutait sa spécificité, et cette règle (0,3,1) écrasait le
 * `padding-left` de `.champ--recherche input` (0,1,1) — la loupe se
 * retrouvait posée sur le texte du champ. `:where()` ne compte pour rien,
 * la règle retrouve donc la spécificité qu'elle avait toujours eue.
 */
.champ input:not(:where([type="checkbox"], [type="radio"])),
.champ select,
.champ textarea {
  width: 100%;
  min-height: 40px;
  font: inherit;
  font-size: 15px;
  color: var(--text-primary);
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-control);
  padding: var(--space-2) var(--space-3);
}

/* Rendu natif conservé, teinté par l'accent de la structure. Un cran plus
   grand que le défaut du navigateur : nos personas ont 55 ans et plus,
   et c'est le libellé qui porte la cible (DESIGN.md §8). */
input[type="checkbox"],
input[type="radio"] {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  accent-color: var(--accent);
  cursor: pointer;
}

.champ input::placeholder, .champ textarea::placeholder { color: var(--text-faint); }

.champ input:focus-visible, .champ select:focus-visible, .champ textarea:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

.champ--erreur input, .champ--erreur select, .champ--erreur textarea {
  border-color: var(--danger);
}

.champ__erreur {
  font-size: 12px;
  color: var(--danger);
  margin-top: var(--space-1);
}

/* ---------- Contrôles natifs habillés (DESIGN.md §6, §14) ----------
   Un « Choose File » nu ou une pastille de couleur brute est un défaut de
   recette. Le libellé du bouton de fichier reste celui du navigateur —
   donc en français chez nos utilisateurs — mais il s'habille en pilule
   secondaire, comme tout geste du produit. */

input[type="file"] {
  font: inherit;
  font-size: 13px;
  color: var(--text-secondary);
}

input[type="file"]::file-selector-button {
  margin-right: var(--space-3);
  min-height: 36px;
  padding: var(--space-1) var(--space-4);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  background: var(--bg-surface);
  box-shadow: var(--shadow-card);
  color: var(--text-primary);
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  transition: background var(--transition);
}

input[type="file"]::file-selector-button:hover { background: var(--bg-raised); }

.champ input[type="color"] {
  padding: 4px;
  cursor: pointer;
}

input[type="color"]::-webkit-color-swatch-wrapper { padding: 0; }
input[type="color"]::-webkit-color-swatch { border: none; border-radius: 7px; }
input[type="color"]::-moz-color-swatch { border: none; border-radius: 7px; }

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

.tableau { width: 100%; border-collapse: collapse; }

.tableau th {
  text-align: left;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-secondary);
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--border);
}

.tableau td {
  /* L'aération v2 des listes vient d'ici : 14px de vertical (DESIGN.md §6). */
  padding: 14px var(--space-3);
  border-bottom: 1px solid var(--border);
}

.tableau tbody tr:last-child td { border-bottom: 0; }

.tableau tbody tr { transition: background var(--transition); }
.tableau tbody tr:hover { background: var(--bg-raised); }

.tableau th.montant, .tableau td.montant { text-align: right; white-space: nowrap; }

.tri-colonne {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  color: inherit;
}

.tri-colonne:hover { color: var(--text-primary); text-decoration: none; }

.tri-colonne[aria-sort="ascending"]::after { content: "↑"; }
.tri-colonne[aria-sort="descending"]::after { content: "↓"; }

.pagination {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  margin-top: var(--space-4);
}

.pagination > .meta { margin: 0; }

.pagination__liens {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
}

/* Les numéros de page (checklist filtres, 28 août 2026) — premier,
   dernier et voisinage de la page courante, jamais tous d'un coup :
   `pages_elidees` a déjà fait le tri, ce rang n'affiche que ce qu'il
   reçoit. Même langage que `.jours-semaine__jour` — pastille compacte en
   creux, celle qui est retenue remonte en surface. */
.pagination__pages {
  display: flex;
  align-items: center;
  gap: 2px;
}

.pagination__page {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 32px;
  height: 32px;
  padding: 0 var(--space-1);
  border-radius: var(--radius-pill);
  font-size: 13px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  color: var(--text-secondary);
}

a.pagination__page:hover { background: var(--bg-raised); text-decoration: none; color: var(--text-primary); }

.pagination__page--actuelle {
  background: var(--accent);
  color: var(--accent-contrast);
  font-weight: 600;
}

.pagination__ellipse {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 32px;
  color: var(--text-faint);
  font-size: 13px;
}

@media (max-width: 600px) {
  .pagination { align-items: flex-start; flex-direction: column; }
  .pagination__liens { width: 100%; justify-content: center; }
}

/* ---------- Pastilles d'état : fond poudré + point + libellé (DESIGN.md §6).
   Jamais la couleur seule — le libellé reste obligatoire. ---------- */

.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 3px var(--space-3);
  border-radius: var(--radius-pill);
  /* En creux et non « raised » : sur une carte blanche, --bg-raised est
     invisible et la pastille neutre redeviendrait du texte nu. */
  background: var(--bg-inset);
  font-size: 12.5px;
  font-weight: 500;
  white-space: nowrap;
  color: var(--text-secondary);
}

.badge::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}

.badge--success { background: var(--success-surface); color: var(--success-texte); }
.badge--warning { background: var(--warning-surface); color: var(--warning-texte); }
.badge--danger { background: var(--danger-surface); color: var(--danger-texte); }
.badge--info { background: var(--info-surface); color: var(--info-texte); }

/* ---------- Cartes et tuiles KPI ---------- */

/* La carte flotte sur le canvas : ombre douce, plus de bordure — l'élévation
   se déclare une fois (DESIGN.md §4). */
.carte {
  background: var(--bg-surface);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  padding: var(--space-6);
}

.tuile-kpi { padding: var(--space-4) var(--space-6); }
.tuile-kpi .meta { margin-bottom: var(--space-1); }

/* Le prévu sous le réel, une fois la tournée terminée : plus discret que
   le chiffre-clé, mais lisible — c'est l'autre moitié de la comparaison,
   pas une note de bas de page. */
.tuile-kpi__prevu {
  margin: 2px 0 0;
  color: var(--text-tertiary);
  font-variant-numeric: tabular-nums;
}
.tuile-kpi__ecart { margin-top: var(--space-2); }

/* Le mandat SEPA, rappelé depuis l'onglet Administratif : il porte une
   pièce signée, une RUM et une séquence — on y renvoie plutôt que d'en
   afficher ici un reflet qui se périmerait. Surface d'information et non
   d'alerte : un mandat manquant se règle, ce n'est pas un incident
   (DESIGN.md §2). */
.encart-mandat {
  background: var(--info-surface);
  color: var(--info-texte);
  border-radius: var(--radius-card);
  padding: var(--space-4) var(--space-5);
  margin-bottom: var(--space-5);
}
.encart-mandat p { margin: 0 0 var(--space-3); font-size: 14px; line-height: 1.5; }

/* Un cycle de facturation qu'on ne peut pas ouvrir, et un brouillon qu'il ne
   faut pas prendre pour une facture (PLAN §44). Surface d'**avertissement**
   là où `encart-mandat` est une surface d'information : la différence n'est
   pas décorative — ici quelque chose est bloqué, ou n'a pas la valeur qu'on
   pourrait lui prêter (DESIGN.md §1, la couleur porte l'information).

   ⚠️ Trouvé à l'œil : ces deux encarts portaient `.alerte
   .alerte--attention`, une classe que **rien ne définit** — 43 usages dans
   les gabarits, zéro règle CSS. Ils rendaient donc des cartes blanches,
   indistinguables de l'état vide juste au-dessous. Le reste de ces usages
   morts reste à reprendre (voir §39.13). */
.encart-bloquant {
  background: var(--warning-surface);
  color: var(--warning-texte);
  border-radius: var(--radius-card);
  padding: var(--space-4) var(--space-5);
}
.encart-bloquant p { margin: 0 0 var(--space-3); font-size: 14px; line-height: 1.5; }
.encart-bloquant p:last-of-type { margin-bottom: 0; }
/* Le texte secondaire garde la couleur de la surface : le gris du thème
   passerait sous le seuil de contraste sur ce fond. */
.encart-bloquant .texte-secondaire { color: inherit; opacity: 0.85; }

/* Marquer un arrêt depuis le bureau : trois boutons côte à côte, un
   formulaire chacun (le POST porte l'état, il n'y a rien à saisir). Séparé
   des actions de consultation par un filet — poser une preuve de livraison
   n'est pas du même ordre qu'appeler le client. */
.marquage-bureau {
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
}
.marquage-bureau__boutons {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-2);
}

/* La même grille était recopiée en style inline sur trois pages : une
   valeur qui dérive d'un gabarit à l'autre casse l'alignement des tuiles
   entre les tableaux de bord. */
.grille-kpi {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--space-4);
  /* Le rythme vertical ne dépend pas du gabarit : sans cette marge, la
     grille collait à la carte suivante sur scan et tournées (0 px mesuré). */
  margin-bottom: var(--space-6);
}

/* ---------- Titre de section (hérité du tableau de bord d'avant « À
   traiter » — encore porté par l'écran de génération des factures) ------- */

.tableau-de-bord__titre-section {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
  margin-bottom: var(--space-3);
}
.tableau-de-bord__titre-section h2 { margin-bottom: var(--space-1); }
.tableau-de-bord__titre-section .meta { margin: 0; }

.lien-discret {
  flex-shrink: 0;
  font-size: 13px;
  font-weight: 500;
}

/* Le même lien discret porté par un `<button>`. Un geste qui **écrit**
   (effacer un menu retenu) reste un bouton dans un formulaire — c'est ce
   qu'attendent le clavier et les lecteurs d'écran — mais il n'a pas à en
   prendre l'habillage : sans ce retrait, le navigateur dessine son cadre
   gris par défaut, et un geste rare se met à peser autant qu'une commande
   principale (DESIGN.md §14 Q1). */
button.lien-discret {
  background: none;
  border: 0;
  padding: 0;
  color: var(--text-secondary);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
}
button.lien-discret:hover { color: var(--text-primary); }

@media (max-width: 700px) {
  .tableau-de-bord__titre-section {
    align-items: flex-start;
    flex-direction: column;
  }
}

/* ---------- Sélecteur de période (DESIGN.md §14 Q7) ----------
   Un réglage, une commande : flèches + valeur dans une seule pilule —
   même patron que `.planif-jours__selecteur`, réutilisable partout où
   l'on change de mois ou d'année. */
.selecteur-periode {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1);
  border-radius: var(--radius-pill);
  background: var(--bg-surface);
  box-shadow: var(--shadow-card);
}

.selecteur-periode input[type="month"],
.selecteur-periode select {
  border: none;
  background: transparent;
  font: inherit;
  font-weight: 500;
  color: inherit;
  padding: var(--space-1) var(--space-2);
}

/* ---------- Cartes de motifs (anomalies de facturation — DESIGN.md §6,
   choix du porteur : le système de cartes des références) ----------
   Une carte par motif : la pastille d'état colorée en tête (le rouge dit
   « une décision attend », et lui seul), le montant agrégé en face, le
   libellé avec son compteur, les personnes en pastilles. */
.grille-motifs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: var(--space-4);
  margin-top: var(--space-4);
}

.carte-motif {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-5);
}

.carte-motif__entete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

.carte-motif__montant {
  font-weight: 600;
  font-size: 15px;
}

.carte-motif__libelle {
  margin: 0;
  font-size: 15.5px;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.carte-motif__compte {
  display: inline-flex;
  align-items: center;
  margin-left: var(--space-1);
  padding: 1px 9px;
  border-radius: var(--radius-pill);
  background: var(--bg-inset);
  font-size: 12px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--text-secondary);
  vertical-align: 2px;
}

.carte-motif .pastilles-personnes { margin-top: var(--space-2); }

.pastilles-personnes {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.pastille-personne {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-pill);
  background: var(--bg-inset);
  font-size: 13px;
  font-weight: 500;
  color: var(--text-primary);
  transition: background var(--transition);
}

.pastille-personne:hover { background: var(--bg-raised); text-decoration: none; }

.pastille-personne span {
  font-size: 12px;
  font-weight: 400;
  font-variant-numeric: tabular-nums;
  color: var(--text-tertiary);
}

/* « + N autres » : le résumé est une pastille comme les autres ; ouvert,
   il laisse place aux pastilles restantes. */
.pastilles-personnes__reste { display: contents; }
.pastilles-personnes__reste summary { cursor: pointer; color: var(--text-secondary); }
.pastilles-personnes__reste[open] summary { display: none; }
.pastilles-personnes__reste .pastilles-personnes { margin-top: 0; display: contents; }

/* ---------- Motifs d'anomalie dépliables (facturation, 29 août 2026) ----------
   Remplace la grille de cartes à pastilles sur l'écran de génération : à
   cinq cents clients, une carte pouvait déployer deux cents pilules. La
   ligne annonce le motif, son compte et son enjeu ; le dépliage montre les
   personnes (pastilles jusqu'à huit, tableau borné au-delà).

   Le liseré gauche de 2 px reprend la « ligne d'anomalie de facturation »
   de DESIGN.md §6 — même vocabulaire visuel, même code couleur. */

.liste-motifs { display: grid; gap: var(--space-2); }

.motif {
  border: 1px solid var(--border);
  border-left: 2px solid var(--text-faint);
  border-radius: var(--radius-control);
  background: var(--bg-surface);
}
.motif--danger { border-left-color: var(--danger); }
.motif--warning { border-left-color: var(--warning); }

/* `summary` en grille : sans `list-style: none`, le triangle natif du
   navigateur s'ajoute au chevron et l'on en voit deux. */
.motif__ligne {
  display: grid;
  grid-template-columns: auto 1fr auto auto;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  cursor: pointer;
  list-style: none;
}
.motif__ligne::-webkit-details-marker { display: none; }
.motif__ligne:hover { background: var(--bg-raised); border-radius: var(--radius-control); }
.motif__texte { display: grid; gap: 2px; min-width: 0; }
.motif__texte strong { font-size: 14px; }
.motif__compte {
  font-size: 12px;
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.motif__fleche {
  width: 14px;
  height: 14px;
  color: var(--text-faint);
  transition: transform var(--transition);
}
.motif[open] > .motif__ligne > .motif__fleche { transform: rotate(180deg); }
.motif__detail {
  padding: 0 var(--space-4) var(--space-4);
  border-top: 1px solid var(--border);
  padding-top: var(--space-3);
}
.motif__detail .pastilles-personnes { margin-top: 0; }

@media (max-width: 640px) {
  /* À 390 px, quatre colonnes ne tiennent pas : le compte passe sous le
     libellé plutôt que d'écraser les deux en une lettre par ligne. */
  .motif__ligne { grid-template-columns: auto 1fr auto; }
  .motif__compte { grid-column: 2 / -1; }
}

/* ---------- Le cycle en trois étapes (facturation, 29 août 2026) ----------
   Calculer, relire, clôturer. L'empilement précédent ne disait pas où l'on
   en était : il fallait lire les boutons pour le deviner. */

.rail-cycle { display: grid; gap: var(--space-3); }

.etape {
  display: grid;
  grid-template-columns: 24px 1fr auto;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  background: var(--bg-surface);
}
.etape--faite { background: var(--bg-raised); }
.etape--active { border-color: var(--border-strong); box-shadow: var(--shadow-card); }
/* Une étape bloquée reste **lisible** : c'est elle qui porte le motif du
   blocage, l'estomper la rendrait illisible au moment où elle compte. */
.etape--bloquee { background: var(--bg-raised); }

.etape__pastille {
  width: 24px;
  height: 24px;
  border-radius: var(--radius-pill);
  display: grid;
  place-items: center;
  font-size: 11.5px;
  font-weight: 600;
  background: var(--bg-inset);
  color: var(--text-tertiary);
}
.etape--faite .etape__pastille { background: var(--success-surface); color: var(--success-texte); }
.etape--active .etape__pastille { background: var(--accent); color: var(--accent-contrast); }

.etape__titre { margin: 0; font-size: 14px; font-weight: 600; }
.etape__note { margin: 2px 0 0; font-size: 12.5px; color: var(--text-secondary); max-width: 64ch; }
.etape__actions { display: flex; flex-wrap: wrap; gap: var(--space-2); justify-content: flex-end; }

@media (max-width: 640px) {
  .etape { grid-template-columns: 24px 1fr; }
  .etape__actions { grid-column: 2 / -1; justify-content: flex-start; }
}

/* ---------- Ligne d'anomalie (facturation — DESIGN.md §6) ---------- */

.anomalie {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-3) var(--space-4);
  border-left: 2px solid var(--danger);
  background: var(--bg-surface);
  border-bottom: 1px solid var(--border);
}

.anomalie--warning { border-left-color: var(--warning); }

.anomalie__libelle { flex: 1; }

.anomalie__montant {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}

/* ---------- État vide : une phrase utile + l'action de départ ---------- */

.etat-vide {
  text-align: center;
  padding: var(--space-12) var(--space-6);
  color: var(--text-secondary);
}

/* L'état vide est une surface d'accueil (DESIGN.md §2, §14 Q9) — et le
   décor poudré ne s'exprime qu'en sombre : deux lavis sourds dans les
   coins, le texte reste l'information et dit le geste suivant. */
[data-theme="dark"] .etat-vide {
  background:
    radial-gradient(90% 120% at 8% 0%, var(--decor-peche) 0%, transparent 52%),
    radial-gradient(90% 120% at 94% 100%, var(--decor-ciel) 0%, transparent 52%),
    var(--bg-surface);
}

.etat-vide .btn { margin-top: var(--space-4); }

/* ---------- Squelettes de chargement ---------- */

.squelette {
  background: var(--bg-raised);
  border-radius: var(--radius-control);
  animation: pulsation 1.4s ease-in-out infinite;
}

@keyframes pulsation {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.55; }
}

@media (prefers-reduced-motion: reduce) {
  .squelette { animation: none; }
}

/* ---------- Gabarits de page ---------- */

.entete-page {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
  margin-bottom: var(--space-6);
}

.entete-page h1 { margin-bottom: 0; }

/* Quand le bloc qui suit est le sous-titre de l'écran — le résumé de
   période des Commandes, qui vit dans le fragment rechargé par HTMX et ne
   peut donc pas être posé dans l'en-tête —, celui-ci se resserre : les
   24px habituels décrochaient le sous-titre de son titre. */
.entete-page--serre { margin-bottom: var(--space-2); }

.entete-page__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--space-2);
}

.barre-filtres {
  display: flex;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

/* La rangée de lecture de la Commande traiteur : le sélecteur de semaine
   (Q7 — un réglage, une commande) à gauche, le segmenté Résumé | Détail à
   droite. Deux réglages de lecture, deux groupes, l'espace entre eux plus
   large qu'à l'intérieur (Q8). */
.barre-lecture {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-bottom: var(--space-4);
}

.barre-lecture__periode {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

/* La période au centre du sélecteur : c'est la valeur du réglage, elle se
   lit comme telle — demi-gras, chiffres tabulaires pour que « du 17 au
   23 » et « du 7 au 13 » gardent la même assiette. */
.selecteur-periode__valeur {
  padding: 0 var(--space-2);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* ---------- Puces de statut — écran Commandes (UX-OFFRE.md §9) ---------- */

.puces-statut {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}

.puce-statut {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-pill);
  background: var(--bg-surface);
  box-shadow: var(--shadow-card);
  color: var(--text-secondary);
  font-size: 13px;
  font-weight: 500;
  text-decoration: none;
  transition: background var(--transition), color var(--transition);
}

.puce-statut:hover { background: var(--bg-raised); text-decoration: none; }

/* La puce active s'inverse : c'est le filtre en vigueur, pas un candidat. */
.puce-statut--actif {
  background: var(--accent);
  color: var(--accent-contrast);
}

.puce-statut--actif:hover { background: var(--accent); opacity: 0.9; }

/* Une puce à zéro : la rangée reste complète — « aucun refus ce mois-ci »
   est une information —, mais celle-ci perd son fond et son relief pour
   que l'œil aille d'abord à ce qui porte quelque chose. Elle reste
   cliquable : on peut vouloir vérifier qu'il n'y a bien rien.
   Jamais posée en même temps que `--actif` (le gabarit choisit l'une ou
   l'autre) : le filtre en cours reste plein même à zéro, sinon il
   deviendrait invisible au moment précis où il faut pouvoir le défaire. */
.puce-statut--vide {
  background: transparent;
  box-shadow: none;
  color: var(--text-tertiary);
}

.puce-statut--vide:hover { background: var(--bg-raised); color: var(--text-secondary); }
.puce-statut--vide .pastille-etat { opacity: 0.4; }

.puce-statut__compte {
  font-variant-numeric: tabular-nums;
  color: var(--text-tertiary);
}

/* Le compte d'une puce vide ne s'affaiblit pas davantage que son libellé :
   `--text-faint` sur le fond de l'application tombe à 2,2:1 de contraste,
   et ce « 0 » est justement ce qu'on vient lire. */
.puce-statut--vide .puce-statut__compte { color: inherit; }
.puce-statut--actif .puce-statut__compte { color: inherit; opacity: 0.75; }

/* ---------- Barre d'outils Commandes — chercher, filtrer, agir (UX-OFFRE.md §9) ---------- */

.barre-outils-commandes {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

/* Sous 900 px, les trois gestes ne tiennent plus sur la ligne du titre :
   la recherche sortait de l'écran par la droite, coupée en plein mot. Ils
   passent alors sous le titre, sur toute la largeur, et le panneau de
   filtres, lui, reste ancré au bord droit de son bouton et se borne à la
   largeur de l'écran. */
@media (max-width: 900px) {
  .entete-page { flex-wrap: wrap; }

  .barre-outils-commandes {
    width: 100%;
    flex-wrap: wrap;
    justify-content: flex-start;
    margin-bottom: 0;
  }

  .barre-outils-commandes .champ--recherche { flex: 1 1 200px; width: auto; }

  .menu-filtres__panneau { width: min(288px, calc(100vw - 2 * var(--space-4))); }
}

/* Les filtres secondaires actifs, retirables un par un (checklist
   filtres, 28 août 2026). Chaque puce **est** le lien qui retire ce
   filtre : pas de bouton niché dans un lien, la croix n'est qu'un repère
   visuel. Teinte info — c'est une information sur ce qu'on regarde, pas
   un avertissement. */
.filtres-actifs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin: 0 0 var(--space-4);
}

.filtre-actif {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--info-surface);
  color: var(--info-texte);
  border-radius: var(--radius-pill);
  padding: 5px 8px 5px var(--space-3);
  font-size: 13px;
  font-weight: 500;
}

.filtre-actif:hover { text-decoration: none; opacity: 0.85; }

.filtre-actif .icone { width: 12px; height: 12px; }

.filtres-actifs__tout-effacer { color: var(--text-tertiary); font-size: 13px; }

.champ--recherche { position: relative; width: 260px; }

/* La recherche est une pilule, comme dans toutes les références : c'est le
   contrôle qu'on doit repérer en premier. */
.champ--recherche input {
  width: 100%;
  padding-left: calc(var(--space-3) * 2 + 14px);
  border-radius: var(--radius-pill);
}

.champ--recherche__icone {
  position: absolute;
  left: var(--space-3);
  top: 50%;
  transform: translateY(-50%);
  width: 14px;
  height: 14px;
  color: var(--text-tertiary);
  pointer-events: none;
}

/* Le bouton Filtre : même mécanique que .menu-agence (data-menu),
   panneau flottant aligné à droite plutôt qu'à gauche — il est déjà
   contre le bord de l'écran. */
.menu-filtres { position: relative; }

.puce-compte {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--accent);
  color: var(--accent-contrast, #fff);
  font-size: 11px;
  font-weight: 600;
}

.menu-filtres__panneau {
  position: absolute;
  top: calc(100% + var(--space-1));
  right: 0;
  z-index: 10;
  width: 288px;
  background: var(--bg-surface);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-float);
  padding: var(--space-4);
  animation: surgissement 160ms ease-out;
  transform-origin: top right;
}

.menu-filtres__panneau .champ:last-child { margin-bottom: 0; }

/* Le panneau pose deux questions — quand, et qui/où. Un intertitre les
   sépare : sans lui, huit champs empilés se lisent comme une liste de
   réglages et on les parcourt tous pour trouver le bon. */
.titre-groupe-filtres {
  margin: 0 0 var(--space-3);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--text-tertiary);
}

.titre-groupe-filtres + * { margin-top: 0; }

.titre-groupe-filtres:not(:first-child) {
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
}

/* Le menu à trois points d'une ligne du tableau — mêmes mécaniques que
   .menu-agence et .menu-filtres (data-menu), en plus petit. */
.menu-actions { position: relative; display: inline-block; }

/* 36px, comme l'écrit DESIGN.md §6 — pas 32. La différence ne se voyait
   pas isolément, mais posée à côté d'un bouton de ligne elle déséquilibrait
   chaque rangée du tableau, et la cible du pointeur y perdait 30 % de
   surface (mesuré le 16 août 2026). */
.btn-icone {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border: none;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
  transition: background var(--transition);
}

.btn-icone:hover { background: var(--bg-raised); }

.menu-actions__panneau {
  position: absolute;
  top: calc(100% + var(--space-1));
  right: 0;
  z-index: 10;
  min-width: 200px;
  background: var(--bg-surface);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-float);
  padding: var(--space-2);
  text-align: left;
  animation: surgissement 160ms ease-out;
  transform-origin: top right;
}

.menu-actions__item {
  display: block;
  width: 100%;
  padding: var(--space-2) var(--space-2);
  border: none;
  border-radius: var(--radius-control);
  background: transparent;
  color: var(--text-primary);
  font: inherit;
  font-size: 13px;
  text-align: left;
  cursor: pointer;
  transition: background var(--transition);
}

.menu-actions__item:hover { background: var(--bg-raised); text-decoration: none; }

.menu-actions__item--danger { color: var(--danger); }

.menu-actions__separateur {
  border: 0;
  border-top: 1px solid var(--border);
  margin: var(--space-1) var(--space-2);
}

/* Un formulaire court dans le panneau (le motif d'une épingle, 23 août
   2026) : le champ prend la largeur du menu plutôt que la sienne, sinon
   il déborde le panneau flottant qui l'accueille. */
.menu-actions__forme { display: block; }

.menu-actions__forme input[type="text"] {
  width: 100%;
  margin-bottom: var(--space-1);
  font-size: 13px;
}

.grille-champs {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: var(--space-4);
}

.grille-champs .champ--large { grid-column: 1 / -1; }

@media (max-width: 720px) {
  .grille-champs { grid-template-columns: 1fr; }
}

/* 🔴 `flex-wrap` est ici et non dans trois exceptions : sans lui, une barre
   trop large ne déborde pas, elle **sort par la gauche**. `justify-content:
   flex-end` pousse le premier bouton à une abscisse négative — mesuré à
   390px sur l'assistant de rattachement, « Annuler » à x = −87 : visible
   pour le navigateur, hors de l'écran pour la main. Le défaut avait déjà
   été corrigé deux fois au cas par cas (en-tête de page, barre de
   sélection) ; la troisième, il remonte à la règle. Se replier ne change
   rien quand tout tient. */
.barre-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--space-3);
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
}

/* Réutilisée dans un en-tête de page, la barre perd son trait de pied de
   formulaire — il dessinait une règle orpheline au-dessus des actions. */
.entete-page .barre-actions {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}

.colonnes-detail {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: var(--space-4);
  align-items: start;
}

@media (max-width: 960px) {
  .colonnes-detail { grid-template-columns: 1fr; }
}

/*
 * Onglets — découpage d'une fiche trop longue en sections consultées
 * séparément. Progressivement améliorés (static/js/onglets.js) : les liens
 * pointent vers l'ancre de chaque panneau, donc sans JavaScript un clic
 * fait défiler jusqu'à la section au lieu de rester inerte. À l'impression,
 * tous les panneaux redeviennent visibles (print.css) — un dossier imprimé
 * ne doit pas perdre les onglets non actifs au moment du clic.
 */
/* v2 : les onglets d'une fiche sont des pilules sur une piste en creux,
   même famille que `.bascule-vue` — un seul langage de bascule. */
.onglets__nav {
  position: relative;
  display: inline-flex;
  max-width: 100%;
  gap: 2px;
  padding: 3px;
  background: var(--bg-inset);
  border-radius: var(--radius-pill);
  margin-bottom: var(--space-4);
  overflow-x: auto;
}

.onglets__lien {
  display: inline-flex;
  align-items: center;
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-pill);
  color: var(--text-secondary);
  font-weight: 500;
  font-size: 13.5px;
  white-space: nowrap;
}

.onglets__lien:hover { color: var(--text-primary); text-decoration: none; }

/* Pression immédiate (même geste que .btn:active) : le retour précède l'activation. */
.onglets__lien:active { opacity: 0.78; }

.onglets__lien--actif {
  background: var(--bg-surface);
  box-shadow: var(--shadow-card);
  color: var(--text-primary);
  font-weight: 600;
}

/* v1 : barre glissante sous l'onglet actif. La pilule v2 porte l'état
   elle-même — l'indicateur ne doit plus jamais se peindre (onglets.js le
   positionne encore ; neutralisé ici plutôt qu'en JS pour que la feuille
   reste la seule vérité visuelle). */
.onglets__indicateur { display: none; }

.onglets__panneau[hidden] { display: none; }

.onglets__panneau {
  opacity: 1;
  transition: opacity var(--transition);
}

/*
 * Second niveau d'onglets (Paramètres — DESIGN.md §14 Q5, 31 août 2026).
 *
 * Il doit se voir **subordonné** au premier d'un coup d'œil, sans quoi les
 * deux rangées de pilules se lisent comme un seul jeu de huit onglets. D'où
 * l'abandon du rail creux : le premier niveau flotte sur `--bg-inset`, le
 * second est posé à plat sur la page, plus petit et sans piste.
 */
.onglets--second { margin-top: calc(-1 * var(--space-1)); }

.onglets--second > .onglets__nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  padding: 0;
  background: none;
  border-radius: 0;
  margin-bottom: var(--space-5);
  overflow-x: visible;
}

.onglets--second > .onglets__nav .onglets__lien {
  padding: 6px var(--space-3);
  font-size: 13px;
}

.onglets--second > .onglets__nav .onglets__lien:hover { background: var(--bg-raised); }

/*
 * Le signal d'un onglet qui contient une valeur refusée. Il existe parce
 * que le bouton d'enregistrement est unique : l'erreur peut vivre derrière
 * deux clics, et « corrigez le champ en rouge » ne sert à rien quand le
 * champ rouge est dans un onglet fermé.
 *
 * 🔴 La couleur ne porte pas seule (DESIGN.md §1 et §8) : le libellé est
 * lu par les lecteurs d'écran, le point rond le montre à l'œil, et le
 * `title` du lien le donne au survol.
 */
.onglets__signal {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.onglets__lien:has(.onglets__signal)::after {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-left: var(--space-2);
  border-radius: 50%;
  background: var(--danger);
  vertical-align: 1px;
}

/*
 * La barre d'enregistrement des Paramètres (décision du porteur, 31 août
 * 2026 : un seul bouton pour tout l'écran).
 *
 * Elle colle au bas de la fenêtre parce qu'un bouton unique qu'il faut
 * aller chercher n'est unique que sur le papier : quel que soit l'onglet
 * ouvert et la longueur de son contenu, le geste reste sous la main.
 * `--shadow-float` et non `--shadow-card` : elle flotte au-dessus du
 * contenu qui défile dessous, elle n'est pas posée dans le flux.
 */
.barre-enregistrer {
  position: sticky;
  bottom: var(--space-4);
  z-index: 20;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-3);
  margin-top: var(--space-6);
  padding: var(--space-3) var(--space-4);
  background: var(--bg-surface);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-float);
}

.barre-enregistrer__note {
  margin: 0;
  margin-right: auto;
  font-size: 13px;
  color: var(--text-secondary);
}

@media (max-width: 600px) {
  /* À 390 px la note passerait sous le bouton et pousserait la barre à
     deux étages, qui mangeraient un tiers de l'écran. Le bouton suffit :
     il est seul, c'est déjà ce que la note explique. */
  .barre-enregistrer__note { display: none; }
  .barre-enregistrer { justify-content: stretch; }
  .barre-enregistrer .btn { flex: 1; justify-content: center; }
}

.liste-infos { margin: 0; }

.liste-infos dt {
  font-size: 12px;
  color: var(--text-secondary);
  margin-top: var(--space-3);
}

.liste-infos dt:first-child { margin-top: 0; }

.liste-infos dd { margin: 2px 0 0; }

/* ---------- Le compte, en haut à droite ----------
   Remonté du pied de la barre latérale le 23 août 2026 (demande du
   porteur) : neuf entrées sous la navigation en faisaient une seconde
   navigation, alors que rien de tout cela ne se visite au fil de la
   journée. Trois objets, du plus courant au plus rare — l'apparence, les
   réglages, puis le compte et son menu. */
.topbar-compte {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  margin-left: auto;
  flex-shrink: 0;
}

/* Les deux icônes sont posées d'avance dans le HTML ; seule l'une des
   deux s'affiche, selon `[data-theme]` sur <html> — rien à recalculer en
   JavaScript à l'ouverture de la page, donc pas de scintillement au
   premier rendu. */
.bouton-theme__clair { display: none; }

[data-theme="dark"] .bouton-theme__sombre { display: none; }

[data-theme="dark"] .bouton-theme__clair { display: inline-flex; }

.topbar-compte__avatar {
  display: inline-flex;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  cursor: pointer;
  line-height: 0;
}

.topbar-compte__avatar:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

/* Qui l'on est, en tête du menu : l'avatar seul ne le dit pas, et le pied
   de barre latérale l'écrivait jusqu'ici en clair. */
.topbar-compte__identite {
  display: flex;
  flex-direction: column;
  gap: 1px;
  margin: 0 0 var(--space-1);
  padding: var(--space-2);
  border-bottom: 1px solid var(--border);
  font-weight: 400;
}

.topbar-compte__identite strong { font-size: 13px; font-weight: 600; }

/* `.topbar a` peint les liens du fil d'Ariane en `--text-tertiary` : une
   icône de commande n'est pas un lien de fil d'Ariane, elle reprend la
   teinte de `.btn-icone` et son survol. */
.topbar-compte a.btn-icone { color: var(--text-secondary); }
.topbar-compte a.btn-icone:hover { color: var(--text-primary); }

/* Le menu du compte porte des liens, et `.topbar a` les décolorerait
   aussi — ici c'est du texte de menu, pas du fil d'Ariane. */
.topbar-compte .menu-actions__item { color: var(--text-primary); font-weight: 400; }
.topbar-compte .menu-actions__item:hover { color: var(--text-primary); }

.avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
}

.avatar--grand { width: 72px; height: 72px; }

/* Vignette d'un camion (paramètres > Camions). Rectangulaire et non ronde :
   un véhicule se reconnaît à sa silhouette et à son marquage de flanc, qu'un
   masque circulaire couperait. La case vide garde sa place pour que la
   colonne « Camion » reste alignée d'une ligne à l'autre — une photo
   manquante ne doit pas décaler le tableau. */
.camion-vignette {
  width: 56px;
  height: 36px;
  border-radius: var(--radius-control);
  object-fit: cover;
  display: block;
  flex-shrink: 0;
  background: var(--bg-inset);
}

.camion-vignette--vide {
  border: 1px dashed var(--border);
  background: transparent;
}

.camion-vignette--grande { width: 128px; height: 82px; }

/* Un tableau plus large que l'écran défile dans sa carte, jamais la page
   entière : quand c'est le corps qui se décale, la colonne de gauche — celle
   qui porte le nom — sort du champ et la ligne devient illisible. Le
   conteneur est focalisable pour que ce défilement existe aussi au clavier
   (WCAG 2.1, régions défilantes). */
/* Un tableau trop large défile **dans son cadre**, jamais en poussant la
   page entière de côté (défaut mesuré le 22 août sur la liste des commandes).

   🔴 **Deux usages, deux sélecteurs — et c'est tout l'objet de ce bloc.**
   La classe a longtemps été déclarée à trois endroits du fichier, chacun
   écrit pour une situation différente, et la dernière déclaration gagnait
   pour tout le monde : ses marges négatives, faites pour un tableau posé
   *dans* une carte, tiraient aussi les cartes qui portent la classe
   elles-mêmes 24 px hors de la colonne. Dix-huit écrans étaient décalés,
   dont Facturation, Tournées et l'espace exploitant (mesuré au navigateur
   le 31 août 2026).

   - `.tableau-defilant` seul : le cadre défile, et rien ne bouge.
   - `.carte .tableau-defilant` : le tableau **vit dans une carte qui porte
     déjà son rembourrage**. Il va donc jusqu'aux bords — marges négatives,
     rembourrage compensatoire — pour que la rangée survolée touche le bord
     de la carte plutôt que de flotter à 24 px.

   `min-width: 0` n'est pas superflu : sans lui, une carte posée dans une
   grille refuse de se rétrécir sous la largeur de son contenu, et
   l'`overflow-x` ne clippe plus rien. */
.tableau-defilant { overflow-x: auto; min-width: 0; max-width: 100%; }

.carte .tableau-defilant {
  margin: var(--space-4) calc(-1 * var(--space-6)) 0;
  padding: 0 var(--space-6);
}

.tableau-defilant .tableau { margin-top: 0; }

/* Deux actions dans une cellule de tableau (PLAN §44).
   🔴 Le flex va sur un **enfant**, jamais sur la `<td>` elle-même : une
   cellule en `display: flex` quitte l'algorithme de tableau, et ses boutons
   se détachent de la rangée — visible à 390 px, où ils sortaient carrément
   de la carte. Le patron des factures d'organismes portait ce défaut depuis
   le 27 août ; il est repris ici plutôt que recopié. */
.cellule-actions {
  display: flex;
  gap: var(--space-1);
  justify-content: flex-end;
  align-items: center;
}

/* <details> utilisé comme révélateur de formulaire secondaire */
details > summary { list-style: none; cursor: pointer; }
details > summary::-webkit-details-marker { display: none; }

/* 🔴 `left: 0` n'est pas décoratif (trouvé à l'œil le 1er septembre 2026).
   Sans lui, l'étiquette reste à sa place statique — dans un tableau large
   qui défile, à 800 px du bord —, et comme aucun parent positionné ne la
   retient, elle **agrandit la page entière** : la Collecte se poussait de
   400 px de côté à 390 px, sur du vide. Posée à gauche, elle ne pousse
   plus rien ; elle est de toute façon découpée à un pixel, et les lecteurs
   d'écran la lisent dans l'ordre du HTML, pas à sa position. */
.visuellement-cache {
  position: absolute;
  left: 0;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* ---------- Page de connexion ---------- */

/* Les pages contractuelles publiques — CGU et annexe de sous-traitance
   (RGPD.md §6 lot 6). Une **colonne de lecture**, pas une carte : ce sont
   des textes longs qu'on lit en entier, parfois en les imprimant, et une
   ligne de 120 caractères se relit deux fois. 68ch est la largeur qui
   sert déjà les notes du produit (`.etape__note`, 64ch), un peu élargie
   pour un texte de fond. */
.page-legale {
  max-width: 68ch;
  margin: 0 auto;
  padding: var(--space-6) var(--space-5) var(--space-8);
  background: var(--bg-app);
  min-height: 100vh;
}

.texte-legal h2 {
  font-size: 17px;
  margin: var(--space-6) 0 var(--space-2);
}

.texte-legal h3 {
  font-size: 15px;
  margin: var(--space-4) 0 var(--space-1);
}

.texte-legal p,
.texte-legal li { line-height: 1.6; }

/* ⚠️ **Les liens d'un texte contractuel se soulignent.** Sans cette
   règle ils héritaient de la couleur du texte et se lisaient comme du
   gras — or « l'annexe » et « en PDF » sont précisément ce qu'un
   acheteur public vient chercher (vu à l'écran, pas au test). Le
   soulignement discret est celui de `.lien-action`, déjà employé par le
   produit pour un lien dans une phrase. */
.page-legale a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--border-strong);
}

.page-legale a:hover { text-decoration-color: var(--text-primary); }

.texte-legal ul { padding-left: 1.2em; }

.texte-legal li { margin-bottom: var(--space-2); }

.page-connexion {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-6);
  background: var(--bg-app);
}

/* Le décor poudré ne s'exprime qu'en sombre (décision du porteur,
   15 août 2026 — DESIGN.md §2) : en clair, le canvas chaud suffit ; en
   sombre, les lavis sourds donnent la profondeur que le noir mange. */
[data-theme="dark"] .page-connexion {
  background:
    radial-gradient(60% 80% at 12% 6%, var(--decor-peche) 0%, transparent 55%),
    radial-gradient(55% 75% at 90% 94%, var(--decor-sauge) 0%, transparent 55%),
    var(--bg-app);
}

/* La carte de connexion flotte davantage que les cartes de travail : c'est
   la première chose que voit une structure, elle est posée seule au centre. */
.carte-connexion {
  width: 100%;
  max-width: 380px;
  border-radius: var(--radius-modal);
  box-shadow: var(--shadow-float);
}

.marque-connexion {
  display: block;
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: var(--space-6);
}

.marque-connexion:hover { text-decoration: none; }

/* Séparateur « ou » entre les modes de connexion */
.separateur-ou {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: var(--space-4) 0;
  color: var(--text-tertiary);
  font-size: 12px;
}

.separateur-ou::before,
.separateur-ou::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border);
}

.pile-boutons {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-top: var(--space-2);
}

/* Variante à deux panneaux (connexion) : le formulaire à gauche, une photo
   à droite dans un cadre à marge fine — DESIGN.md §1, la photo est un
   support d'accueil, jamais un décor qui prend le pas sur la tâche. */
.page-connexion--divisee { padding: var(--space-8); }

.carte-connexion--divisee {
  display: flex;
  align-items: stretch;
  width: 100%;
  max-width: 960px;
  padding: 0;
  overflow: hidden;
}

.carte-connexion__panneau {
  flex: 1 1 420px;
  min-width: 0;
  padding: var(--space-8);
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.carte-connexion__panneau .marque-connexion { margin-bottom: var(--space-8); }

.carte-connexion__photo {
  flex: 1 1 480px;
  /* La marge fine autour de l'image, jamais collée au bord de la carte —
     c'est elle qui distingue « une photo posée dans un cadre » d'un simple
     fond plein cadre. */
  padding: var(--space-3);
}

/* En sombre seulement, la photo se pose sur un support chaud (§2). */
[data-theme="dark"] .carte-connexion__photo {
  background: linear-gradient(165deg, var(--decor-peche), var(--decor-sauge));
}

.carte-connexion__photo picture {
  display: block;
  width: 100%;
  height: 100%;
}

.carte-connexion__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: var(--radius-modal);
  display: block;
}

@media (max-width: 960px) {
  /* Le portrait cède la place plutôt que de s'empiler au-dessus du
     formulaire : sur un petit écran, une gestionnaire vient se connecter,
     pas admirer une photo. */
  .page-connexion--divisee { padding: var(--space-4); }
  .carte-connexion--divisee { max-width: 380px; }
  .carte-connexion__photo { display: none; }
  .carte-connexion__panneau { padding: var(--space-6); }
}

/* ---------- Commandes ---------- */

.filtre-preparation {
  display: flex;
  align-items: flex-end;
  gap: var(--space-4);
  margin-bottom: var(--space-6);
}

/* ---------- Écart au téléphone ---------- */

.jour-telephone { margin-bottom: var(--space-3); }

.jour-telephone__entete {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-3);
}


/* La ligne d'un jour est un `<summary>` : sans ce reset, le navigateur
   pose son propre triangle et refuse la mise en page flex ci-dessus
   (`display: list-item` par défaut). */
summary.jour-telephone__entete {
  cursor: pointer;
  list-style: none;
  user-select: none;
}

summary.jour-telephone__entete::-webkit-details-marker { display: none; }

summary.jour-telephone__entete:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 4px;
  border-radius: var(--radius-control);
}

/* Le résumé est le **seul** élément à pousser, et il pousse tout le reste
   à droite. Deux `margin-left: auto` sur la même ligne se partageraient
   l'espace libre au lieu de l'absorber, et les résumés des sept jours ne
   s'alignaient plus les uns sous les autres. Il est donc toujours rendu,
   même vide (un jour absent : la pastille dit déjà tout). */
.jour-telephone__resume {
  margin-left: auto;
  text-align: right;
  min-width: 0;
}

.jour-telephone__chevron {
  flex-shrink: 0;
  margin-left: var(--space-2);
  color: var(--text-tertiary);
  transition: transform var(--transition);
}

/* Sur un écran étroit, le résumé coincé entre la pastille et le chevron
   tombait en colonne d'un mot par ligne (« Pas / encore / décidé »), ce qui
   rendait un jour replié plus haut qu'un jour ouvert — l'inverse du but. Il
   prend donc la ligne suivante, à gauche, tandis que la date accepte de se
   replier sur deux lignes pour garder la pastille et le chevron à côté
   d'elle. Après les règles de base : à spécificité égale, c'est la
   dernière écrite qui l'emporte. */
@media (max-width: 560px) {
  summary.jour-telephone__entete {
    position: relative;
    padding-right: calc(18px + var(--space-2));
  }

  /* Sorti du flux : en flex, une ligne pleine fait **passer** le chevron à
     la ligne plutôt que rétrécir la date, et il se retrouvait seul en bas à
     gauche, là où il ne veut rien dire. Ancré en haut à droite, il tient sa
     place quelle que soit la longueur de la date et de la pastille. */
  .jour-telephone__chevron {
    position: absolute;
    top: 0;
    right: 0;
    margin: 0;
  }

  .jour-telephone__resume {
    order: 1;
    flex-basis: 100%;
    margin-left: 0;
    text-align: left;
  }
}

details[open] > .jour-telephone__entete .jour-telephone__chevron { transform: rotate(180deg); }

/* Un jour sans rien à décider garde la même ligne, sans l'affordance : pas
   de curseur main, pas de chevron, rien qui promette une ouverture. */
.jour-telephone--inerte .jour-telephone__date { color: var(--text-secondary); }

.jour-telephone__assiette {
  margin: var(--space-3) 0 0;
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
}

.jour-telephone__assiette + .jour-telephone__actions {
  margin-top: var(--space-3);
  padding-top: 0;
  border-top: 0;
}

.jour-telephone__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
}

/* Trois familles se suivent dans un jour ouvert — le menu (une seule
   formule à la fois : elle remplace le repas), les options (cumulables :
   elles ajoutent trente centimes), la présence (elle annule les deux).
   Rendues à plat sur une ligne, elles se lisaient comme la même bascule.
   L'intitulé tient dans une colonne fixe pour que les boutons s'alignent
   d'un groupe à l'autre. */
.jour-telephone__actions .groupe-actions {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  width: 100%;
}

.groupe-actions__titre {
  flex: 0 0 5.5rem;
  font-size: 11px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-tertiary);
}

.groupe-actions__boutons {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
}

/* Sur un écran étroit, l'intitulé passe au-dessus de ses boutons plutôt
   que de leur voler la moitié de la largeur. */
@media (max-width: 560px) {
  .jour-telephone__actions .groupe-actions {
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-1);
  }

  .groupe-actions__titre { flex: none; }
}

.jour-telephone__actions select {
  font: inherit;
  font-size: 13px;
  color: var(--text-primary);
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-control);
  padding: var(--space-1) var(--space-2);
}

/* Un supplément déjà ajouté se distingue du même bouton qui l'ajouterait —
   sans quoi rien ne dit qu'un second clic le retirerait. */
.btn--actif {
  background: var(--bg-raised);
  border-color: var(--border-strong);
  font-weight: 600;
}

/* ---------- Filtres actifs (menus, en attendant les autres listes) ---------- */

.filtres-actifs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}

/* Chaque puce est son propre lien de retrait : la croix n'est pas un
   deuxième geste séparé du texte, toute la puce se clique (DESIGN.md §1 —
   une cible plus grande qu'un glyphe de 8px). */
.filtre-actif {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: 12px;
  color: var(--text-secondary);
}

.filtre-actif:hover { background: var(--bg-raised); color: var(--text-primary); text-decoration: none; }

/* « Réinitialiser » n'est pas un filtre parmi d'autres : pas de contour,
   pour qu'elle se lise comme l'action qui efface les puces plutôt que
   comme une puce de plus à retirer. */
.filtre-actif--tout {
  border-color: transparent;
  text-decoration: underline;
}

/* Barre d'actions en lot (liste des menus) : masquée par défaut
   (`[hidden]`, `selection_lot.js` la révèle dès la première case cochée). Un
   fond relevé la distingue des filtres au-dessus — ceux-ci changent ce que
   montre le tableau, celle-ci agit sur ce qu'on y a coché.

   Collée en haut de la fenêtre, même geste et même raison que
   `.barre-ordre` : le tableau des commandes affiche cent lignes, et cocher
   la soixantième faisait disparaître le bouton qui agit dessus (vu à
   l'écran, 26 août 2026). Le `sticky` ne mord que tant que le formulaire
   qui l'englobe est à l'écran — la barre s'en va avec son tableau. */
.barre-actions-lot {
  position: sticky;
  top: var(--space-2);
  z-index: 3;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  margin-bottom: var(--space-3);
  background: var(--bg-inset);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-float);
}

/* Le compte de la sélection, et le geste qui l'étend à tout ce que les
   filtres montrent (checklist filtres, 28 août 2026) — groupés pour que
   `justify-content: space-between` du parent ne les sépare pas du bouton
   « Actions », qui doit rester seul à l'autre bout. */
.barre-actions-lot__compte {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3);
  font-size: 13px;
}

.barre-actions-lot__compte .lien-action { color: var(--text-secondary); }
.barre-actions-lot__compte .lien-action:hover { color: var(--text-primary); }

/* ---------- Menus ---------- */

/* Le calendrier des menus réutilise la grille `.calendrier` du calendrier
   des repas : deux mois côte à côte dans le produit ne doivent pas se lire
   de deux façons différentes. Seules les trois règles propres aux menus
   vivent ici. */

/* La case entière est cliquable, pas seulement le numéro : viser un chiffre
   de 12 px dans une grille de trente cases est un exercice d'adresse. */
.calendrier__lien {
  display: block;
  margin: calc(-1 * var(--space-2));
  padding: var(--space-2);
  min-height: 4.5rem;
  color: var(--text-primary);
}
.calendrier__lien:hover { background: var(--bg-raised); text-decoration: none; }

.calendrier__formule {
  display: block;
  margin-top: 2px;
  font-size: 12px;
  font-weight: 500;
  line-height: 1.3;
}

/* « + menu » ne s'annonce qu'au survol, comme les actions du calendrier des
   repas : trente invitations à ajouter feraient de la grille un mur de
   liens. La case reste cliquable sans lui. */
.calendrier__ajout {
  display: block;
  margin-top: var(--space-1);
  font-size: 11px;
  color: var(--text-tertiary);
}

@media (hover: hover) {
  .calendrier__jour .calendrier__ajout { opacity: 0; }
  .calendrier__jour:hover .calendrier__ajout,
  .calendrier__jour:focus-within .calendrier__ajout { opacity: 1; }
}

/* Un jour d'un mois voisin, montré pour ne pas couper la semaine en deux :
   lisible et cliquable, mais en retrait — sinon on croirait le mois plus
   long qu'il n'est. */
.calendrier__jour--hors-mois { background: var(--bg-raised); }
.calendrier__jour--hors-mois .calendrier__numero,
.calendrier__jour--hors-mois .calendrier__formule { color: var(--text-tertiary); }

/* Calendrier ↔ liste : deux vues sur les mêmes menus. Collées l'une à
   l'autre, elles se lisent comme un seul contrôle à deux positions — deux
   boutons espacés se liraient comme deux actions sans rapport. */
/* Segmented control (DESIGN.md §6) : piste en creux, le segment actif
   flotte. (v1 : `var(--radius)` inexistant — rayon nul silencieux.) */
.bascule-vue {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  background: var(--bg-inset);
  border-radius: var(--radius-pill);
}

.bascule-vue .btn {
  border: 0;
  min-height: 34px;
  padding: 0 var(--space-4);
  border-radius: var(--radius-pill);
  font-size: 13px;
  color: var(--text-secondary);
}

/* Pas de soulignement de lien dans un segmenté : le segment est une
   position d'un contrôle, pas un hyperlien dans une phrase. */
.bascule-vue .btn:hover {
  background: transparent;
  color: var(--text-primary);
  text-decoration: none;
}

.bascule-vue .btn--actif,
.bascule-vue .btn[aria-current="page"],
.bascule-vue .btn[aria-pressed="true"] {
  background: var(--bg-surface);
  box-shadow: var(--shadow-card);
  border: 0;
  color: var(--text-primary);
  font-weight: 600;
}

/* Le type d'un composant, devant ses plats dans la colonne « Composants ».
   En petites capitales : il classe la valeur, il n'est pas la valeur — et
   la ligne reste lisible d'un balayage sans que le type prenne le pas. */
.menu-ligne__type {
  font-size: 10px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-tertiary);
  margin-right: 2px;
}

.bloc-composants { margin-bottom: var(--space-4); }

.bloc-composants__titre {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-secondary);
  margin: 0 0 var(--space-2);
  padding-bottom: var(--space-1);
  border-bottom: 1px solid var(--border);
}

.liste-composants { list-style: none; margin: 0; padding: 0; }

.liste-composants li {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) 0;
}

.liste-composants li .meta { margin-left: auto; }
.liste-composants li form { margin-left: var(--space-2); }
.liste-composants li .btn { padding: var(--space-1) var(--space-2); }

.ajout-composant {
  display: flex;
  align-items: flex-end;
  gap: var(--space-3);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
}

/* Le même jeu de champs, mais posé *dans* une ligne existante pour la
   corriger : ni filet ni respiration au-dessus, sinon chaque plat de la
   liste porterait une séparation qui ne sépare rien. Centré et non aligné
   sur le bas, ses libellés étant masqués — il n'y a pas de ligne de base
   commune à retrouver. */
.ajout-composant--inline {
  padding-top: 0;
  border-top: 0;
  align-items: center;
}

/* ---------- Production ---------- */

/* Trois boutons texte plutôt qu'iconiques : à 390px ils ne tiennent plus
   sur une ligne (mesuré : 220px de débordement sur la prévision de
   production). Elle se replie au lieu de pousser la page de côté — même
   geste que `.barre-actions-lot .barre-actions`. */
/* ---------- Tournées ---------- */

.filtre-jour {
  display: flex;
  align-items: flex-end;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
  font-size: 13px;
  color: var(--text-secondary);
}

.filtre-jour input {
  font: inherit;
  font-size: 15px;
  color: var(--text-primary);
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-control);
  padding: var(--space-2) var(--space-3);
}

.actions-arret {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  justify-content: flex-end;
}

/* Les flèches sont des boutons carrés : le texte du bouton fait une seule
   glyphe, sans le padding horizontal d'un libellé. */
.actions-arret .btn {
  min-width: 44px;
  min-height: 44px;
  padding: var(--space-2);
}

/* Trois gestes qui tiennent sur une ligne (fiche de tournée, 23 août
   2026) : ↑ ↓ et le menu « ⋯ ». Le retour à la ligne recréerait la
   rangée haute qu'on vient de corriger, pour un pixel manquant. */
.actions-arret--serrees { flex-wrap: nowrap; align-items: center; }

/* Ligne qui reste affichée mais ne compte pas ce jour-là : bénéficiaire
   absent, jour de fermeture. La masquer ferait croire à une erreur de
   saisie ; l'atténuation dit « prévu, mais pas ce jour-là ». */
.ligne-attenuee td:not(:last-child) { opacity: 0.55; }

.ajout-arret {
  display: flex;
  align-items: flex-start;
  gap: var(--space-4);
  margin-top: var(--space-6);
}

.ajout-arret .btn { margin-top: 22px; }

/* Sous 30rem, le bouton passe sous le champ plutôt que de déborder la
   page de cinq pixels — un défilement horizontal de la page entière pour
   un bouton, c'est le pire échange possible. `min-width: 0` : sans lui,
   le champ flexible refuse de rétrécir sous la largeur de son contenu. */
.ajout-arret { flex-wrap: wrap; }
.ajout-arret > .champ { min-width: 0; }

@media (max-width: 30rem) {
  .ajout-arret .btn { margin-top: 0; }
}

/* Trois champs côte à côte (formule, traiteur, régime) ne tiennent pas sur
   un téléphone : chacun rétrécissait jusqu'à ne montrer que « Déj… » et un
   sélecteur de traiteur large de deux caractères (constaté à 390 px le
   28 août 2026). Sous 45rem ils s'empilent, chacun sur toute la largeur —
   la barre reste une barre au bureau, et devient une pile au doigt. */
@media (max-width: 45rem) {
  .ajout-arret { flex-direction: column; align-items: stretch; }
  .ajout-arret > .champ { flex: none; width: 100%; }
  .ajout-arret .btn { margin-top: 0; width: 100%; }
}

/* ---------- Double authentification ---------- */

.carte-connexion--large { max-width: 480px; }

.etapes-mfa {
  margin: var(--space-5) 0;
  padding-left: var(--space-5);
  color: var(--text-secondary);
}

.etapes-mfa > li { margin-bottom: var(--space-4); }

/* Fond blanc explicite : en thème sombre, un QR code noir sur transparent
   devient illisible — et une caméra ne lit pas un code inversé. */
.qr-mfa {
  display: block;
  width: 180px;
  height: 180px;
  margin: var(--space-3) 0;
  padding: var(--space-3);
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
}

.etapes-mfa summary { cursor: pointer; }

.cle-mfa {
  display: inline-block;
  margin-top: var(--space-2);
  padding: var(--space-2) var(--space-3);
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: 0.06em;
  color: var(--text-primary);
  user-select: all;
}

/* Deux colonnes : dix codes tiennent sans faire défiler la page, et la
   grille aide l'œil à vérifier qu'aucun n'a été oublié à la recopie. */
.codes-secours {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-2);
  margin: var(--space-4) 0;
  padding: var(--space-4);
  list-style: none;
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: 0.04em;
  user-select: all;
}

.bloc-mfa {
  padding: var(--space-4) 0;
  border-top: 1px solid var(--border);
}

.bloc-mfa__titre { font-weight: 600; margin-bottom: var(--space-1); }

.bloc-mfa .btn { margin-top: var(--space-3); }

/* ---------- Bandeau d'emprunt d'identité ---------- */

.bandeau-emprunt {
  position: sticky;
  top: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-2) var(--space-6);
  background: var(--warning);
  color: #18181B;
  font-size: 13px;
}

.bandeau-emprunt .btn {
  min-height: 36px;
  padding: var(--space-1) var(--space-3);
  border-color: rgb(24 24 27 / 0.35);
  background: rgb(255 255 255 / 0.75);
  color: #18181B;
}

/* Réacceptation des conditions (RGPD.md §6 lot 6). Même forme que le
   bandeau d'emprunt ci-dessus — collant, pleine largeur, impossible à
   manquer —, mais **sans le jaune** : le contrat n'est pas à jour, ce
   qui appelle un geste, pas une alerte de sécurité. Le fond surélevé
   avec un trait d'accent dit « lis-moi » sans crier (DESIGN.md §1 : la
   couleur est une information, et l'ambre est déjà prise par
   l'emprunt d'identité, autrement plus grave). */
.bandeau-conditions {
  position: sticky;
  top: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
  padding: var(--space-2) var(--space-6);
  background: var(--bg-raised);
  border-bottom: 2px solid var(--accent);
  font-size: 13px;
}

.bandeau-conditions .btn { min-height: 36px; padding: var(--space-1) var(--space-3); }

/* ---------- Toasts (coin bas-droit, sobres — DESIGN.md §6) ---------- */

.toasts {
  position: fixed;
  bottom: var(--space-6);
  right: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  width: min(400px, calc(100vw - (2 * var(--space-6))));
  max-height: min(70vh, 32rem);
  overflow-y: auto;
  z-index: 100;
  /* La pile est posée en permanence, pour que les écritures partielles
     (HTMX, panneau métier) aient où déposer leur message. Vide, elle
     couvrirait 400 px du coin de l'écran et avalerait les clics du
     contenu en dessous — d'où la transparence aux événements, rendue aux
     toasts eux-mêmes pour que leur croix reste cliquable. */
  pointer-events: none;
}

.toasts > * { pointer-events: auto; }

/* Le toast neutre s'inverse (DESIGN.md §6) : une confirmation passe et
   disparaît, l'encre pleine la rend lisible sans repeindre l'écran. */
.toast {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  background: var(--accent);
  color: var(--accent-contrast);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-float);
  padding: var(--space-3);
  font-size: 13px;
}

.toast__message { flex: 1; padding: var(--space-1) 0; }

.toast__fermer {
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: var(--radius-pill);
  background: transparent;
  color: inherit;
  opacity: 0.8;
  cursor: pointer;
}

.toast__fermer:hover { background: rgb(127 127 127 / 0.25); opacity: 1; }

/* Un toast porte une issue — c'est le seul endroit où la teinte pleine se
   justifie : il apparaît seul, disparaît, et ne repeint donc jamais
   durablement l'écran. Le texte le dit toujours aussi (« Import terminé ») :
   la couleur ne fait que le confirmer avant qu'on ait lu. */
.toast--succes {
  background: var(--success-surface);
  color: var(--success-texte);
}

.toast--erreur {
  background: var(--danger-surface);
  color: var(--danger-texte);
}

/* La croix hérite de la teinte du toast, sinon elle reste grise sur fond
   coloré — le seul élément qui n'appartiendrait pas au bloc. */
.toast--succes .toast__fermer,
.toast--erreur .toast__fermer { color: inherit; }

.toast--succes .toast__fermer:hover,
.toast--erreur .toast__fermer:hover {
  background: rgb(127 127 127 / 0.18);
  color: inherit;
}

/* Le verdict d'un import : l'issue avant le chiffre. */
.verdict-import {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0 0 var(--space-3);
}

.verdict-import__icone {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  flex-shrink: 0;
  border-radius: 50%;
  font-size: 15px;
  line-height: 1;
}

.verdict-import--succes { color: var(--success-texte); }
.verdict-import--succes .verdict-import__icone {
  background: var(--success-surface);
  color: var(--success-texte);
}

.verdict-import--echec { color: var(--danger-texte); }
.verdict-import--echec .verdict-import__icone {
  background: var(--danger-surface);
  color: var(--danger-texte);
}

/*
 * Une ligne dépliable qui s'annonce comme une carte, pas comme un lien :
 * icône à gauche, titre + sous-titre au centre, chevron à droite qui
 * pivote à l'ouverture — le seul indice qu'on a besoin de voir avant même
 * de lire le texte pour comprendre qu'il y a plus dessous.
 *
 * `.divulgation--avertissement` teinte l'icône en orange pour une carte
 * qui attend une décision (un rapprochement à trancher) — jamais le fond
 * entier (DESIGN.md §1) : seule l'icône porte la couleur, le texte reste
 * lisible sur le fond neutre habituel.
 */
.divulgation {
  border-radius: var(--radius-card);
  background: var(--bg-surface);
  box-shadow: var(--shadow-card);
  overflow: hidden;
  margin: var(--space-4) 0;
}

.divulgation__resume {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  cursor: pointer;
  list-style: none;
}

.divulgation__resume::-webkit-details-marker { display: none; }
.divulgation__resume:hover { background: var(--bg-raised); }

.divulgation__icone {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--bg-raised);
  color: var(--text-secondary);
}

.divulgation__icone .icone { width: 16px; height: 16px; }

.divulgation--avertissement .divulgation__icone {
  background: var(--warning-surface);
  color: var(--warning-texte);
}

.divulgation__texte {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.divulgation__titre { font-size: 14px; font-weight: 600; color: var(--text-primary); }
.divulgation__sous-titre { margin: 0; }

.divulgation__chevron {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  color: var(--text-tertiary);
  transition: transform var(--transition);
}

.divulgation[open] .divulgation__chevron { transform: rotate(180deg); }

.divulgation__corps {
  padding: 0 var(--space-4) var(--space-4);
  border-top: 1px solid var(--border);
  padding-top: var(--space-3);
}

/* Une ligne du détail d'import qui se déplie sur ce qu'elle contient
   (potage, plat, dessert…) — le repli reste fermé par défaut, le tableau
   annonce déjà le compte de composants. */
.divulgation-ligne > summary { padding: 0; }

.divulgation-ligne__resume {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr 1fr auto;
  align-items: center;
  gap: var(--space-3);
  padding: 14px var(--space-3);
  cursor: pointer;
  list-style: none;
}
.divulgation-ligne__resume::-webkit-details-marker { display: none; }
.divulgation-ligne__resume:hover { background: var(--bg-raised); }
.divulgation-ligne__resume .montant { text-align: right; }
.divulgation-ligne__resume .icone { width: 16px; height: 16px; color: var(--text-tertiary); transition: transform var(--transition); flex-shrink: 0; }
.divulgation-ligne[open] .divulgation-ligne__resume .icone { transform: rotate(180deg); }

/* Sous 480px, la grille à cinq colonnes n'a plus la place : chaque valeur
   passe sur sa propre ligne, comme les tableaux `--rapprochements` et
   `--contrats` déjà empilés en mobile. */
@media (max-width: 480px) {
  .divulgation-ligne__resume {
    grid-template-columns: 1fr auto;
    grid-template-areas: "date chevron" "formule chevron" "plats effet";
    row-gap: 4px;
  }
  .divulgation-ligne__resume > span:nth-child(1) { grid-area: date; }
  .divulgation-ligne__resume > span:nth-child(2) { grid-area: formule; }
  .divulgation-ligne__resume > span:nth-child(3) { grid-area: plats; text-align: left; }
  .divulgation-ligne__resume > span:nth-child(4) { grid-area: effet; justify-self: start; }
  .divulgation-ligne__resume .icone { grid-area: chevron; align-self: center; }
}

.divulgation-ligne__corps {
  padding: 0 var(--space-3) var(--space-3) var(--space-3);
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.divulgation-ligne__composant {
  display: flex;
  gap: var(--space-2);
  font-size: 13px;
  padding: 4px var(--space-3);
  border-radius: var(--radius-control);
  background: var(--bg-raised);
}
.divulgation-ligne__composant .meta { min-width: 9em; }

/* Une paire de choix mutuellement exclusifs dans une cellule de tableau —
   la décision d'un rapprochement, ligne par ligne. Empilée et non côte à
   côte : chaque option porte une légende sous son libellé, et les deux
   ensemble ne tiennent plus sur une ligne. */
.champ-radios {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-width: 16rem;
}

.champ-radios label {
  display: inline-flex;
  align-items: flex-start;
  gap: var(--space-2);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
}

.champ-radios input[type="radio"] { margin-top: 2px; flex-shrink: 0; }
.champ-radios label > span { display: flex; flex-direction: column; gap: 2px; }
.champ-radios label .meta { font-weight: 400; white-space: normal; }

/* Une case seule dans un formulaire (checklist filtres, 28 août 2026) :
   le label complet est la zone cliquable, comme un `.champ-radios`, mais
   sur une seule ligne — pas de bloc `<span>` à empiler pour un intitulé
   qui tient déjà sur sa largeur. */
.champ-case {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: 13.5px;
  font-weight: 400;
  cursor: pointer;
}

/* Le jour de la semaine, en pastilles plutôt qu'en menu déroulant
   (checklist filtres, 28 août 2026) : sept valeurs à comparer d'un coup
   se scannent mieux en rangée. Même langage que `.bascule-vue` — fond en
   creux, la valeur retenue remonte en surface —, mais avec de vraies
   cases radio (accessibles, `role="radiogroup"`) plutôt que des boutons
   JavaScript. */
.jours-semaine {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 2px;
  padding: 3px;
  margin-top: var(--space-1);
  background: var(--bg-inset);
  border-radius: var(--radius-pill);
}

.jours-semaine__jour {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 32px;
  height: 30px;
  padding: 0 var(--space-2);
  border-radius: var(--radius-pill);
  font-size: 12px;
  font-weight: 600;
  color: var(--text-secondary);
  cursor: pointer;
}

.jours-semaine__jour input {
  position: absolute;
  inset: 0;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.jours-semaine__jour:has(input:checked) {
  background: var(--bg-surface);
  box-shadow: var(--shadow-card);
  color: var(--text-primary);
}

.jours-semaine__jour input:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: var(--radius-pill);
}

/* ---------- Filtre à choix multiple en pastilles ----------
   Même langage que `.jours-semaine` — dont ce bloc est le cousin à cases à
   cocher — pour les valeurs qui ne s'excluent pas : les régimes d'une
   liste de menus, les allergènes. Une rangée se compare d'un coup d'œil
   là où un menu déroulant cache ce qu'on n'a pas coché.

   Pas de rail gris ici, contrairement aux jours : les libellés sont des
   mots entiers de longueur inégale et le fond continu, cassé sur deux ou
   trois lignes, se lisait comme un bloc plutôt que comme des choix. */
.cases-pastilles {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  margin-top: var(--space-1);
}

.cases-pastilles__case {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-height: 30px;
  padding: 0 var(--space-3);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  background: var(--bg-surface);
  font-size: 13px;
  color: var(--text-secondary);
  cursor: pointer;
}

.cases-pastilles__case input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

/* Cochée : l'accent en aplat, comme un bouton retenu. La couleur ne porte
   pas l'information seule — le libellé reste lisible et la case garde son
   état pour le lecteur d'écran (DESIGN.md §1). */
.cases-pastilles__case:has(input:checked) {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-contrast);
  font-weight: 500;
}

.cases-pastilles__case input:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: var(--radius-pill);
}

/* ---------- Cartes de choix (demande du porteur, 21 août 2026) ----------
   Un choix qui se prend en regardant, pas en lisant : une icône, un
   intitulé, la phrase qui dit ce que ça veut dire. Réservé aux **choix
   structurants** d'un parcours (la portée d'une optimisation, sa
   métrique) — un formulaire ordinaire garde ses `champ-radios`, qui
   tiennent plus de choix dans moins de place.

   Le rapport repos/choisi reprend celui du segmenté (`.bascule-vue`) :
   en creux au repos, remonté en surface une fois choisi. Il tient dans
   les deux thèmes, où `--bg-inset` est toujours plus enfoncé que
   `--bg-surface`. */
.cartes-choix {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.carte-choix {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: 1px solid transparent;
  border-radius: var(--radius-control);
  background: var(--bg-inset);
  cursor: pointer;
  transition: background var(--transition), border-color var(--transition);
}

/* Le natif reste dans le flux et reçoit le focus — il est seulement
   invisible. Le retirer de l'accessibilité (`display: none`) sortirait le
   choix du clavier et des lecteurs d'écran. */
.carte-choix input[type="radio"] {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  opacity: 0;
  pointer-events: none;
}

.carte-choix:hover { background: var(--bg-raised); }

.carte-choix__icone {
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  color: var(--text-secondary);
}

.carte-choix__texte {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.carte-choix__texte strong { font-size: 14px; font-weight: 600; }
.carte-choix__texte .meta { white-space: normal; }

/* La marque de choix : un anneau qui se remplit. Elle ne dépend pas de
   `:has()` — sans lui la carte ne remonte pas, mais l'état reste visible,
   et un état invisible serait le vrai défaut. */
.carte-choix__marque {
  flex-shrink: 0;
  margin-left: auto;
  width: 1.15rem;
  height: 1.15rem;
  border-radius: 50%;
  border: 1.5px solid var(--border-strong);
  background: var(--bg-surface);
  transition: border-color var(--transition), background var(--transition);
}

.carte-choix input[type="radio"]:checked ~ .carte-choix__marque {
  border-color: var(--accent);
  background: var(--accent);
  /* Le point central, dessiné en creux : une pastille pleine seule se
     confondrait avec un bouton désactivé. */
  box-shadow: inset 0 0 0 3.5px var(--bg-surface), inset 0 0 0 10px var(--accent);
}

.carte-choix:has(input[type="radio"]:checked) {
  background: var(--bg-surface);
  border-color: var(--accent);
  box-shadow: var(--shadow-card);
}

.carte-choix:has(input[type="radio"]:checked) .carte-choix__icone { color: var(--text-primary); }

.carte-choix:has(input[type="radio"]:focus-visible) {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

/* Un choix indisponible se voit sans être caché : la raison est écrite
   dans sa description (« aucune tournée ne roule ce jour-là »). */
.carte-choix:has(input[type="radio"]:disabled) {
  cursor: not-allowed;
  opacity: 0.55;
}

.carte-choix:has(input[type="radio"]:disabled):hover { background: var(--bg-inset); }

/* Ce qui dépend d'un choix se range sous lui, décalé de la largeur de son
   icône : la dépendance se lit dans la mise en page. */
.cartes-choix__precision {
  margin: calc(-1 * var(--space-1)) 0 0 var(--space-4);
  padding-left: var(--space-4);
  border-left: 2px solid var(--border);
}

/* Le tableau des rapprochements devient une pile de cartes sous 640px :
   ce que porte la colonne « Décision » — la seule qui compte vraiment —
   sortait de l'écran derrière un défilement horizontal qu'on ne pense pas
   toujours à chercher, precisément là où cette décision se prend le plus
   souvent, entre deux allers-retours à la cuisine. */
@media (max-width: 640px) {
  .tableau--rapprochements thead { display: none; }

  .tableau--rapprochements,
  .tableau--rapprochements tbody,
  .tableau--rapprochements tr,
  .tableau--rapprochements td {
    display: block;
    width: 100%;
  }

  .tableau--rapprochements tr {
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
    margin-bottom: var(--space-3);
    padding: var(--space-3);
  }

  .tableau--rapprochements tr:last-child { margin-bottom: 0; }

  .tableau--rapprochements td {
    border: 0;
    padding: var(--space-2) 0;
  }

  .tableau--rapprochements td:first-child { padding-top: 0; }
  .tableau--rapprochements td:last-child { padding-bottom: 0; }

  .tableau--rapprochements td::before {
    content: attr(data-label);
    display: block;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-tertiary);
    margin-bottom: 2px;
  }
}

/* Les tableaux des contrats et des prises en charge, sur la fiche,
   deviennent une pile de cartes sous 640px, même raison que les
   rapprochements ci-dessus : la répartition en toutes lettres (« le
   passage du lundi emporte… ») répétée un jour par ligne rendait la
   période et le tarif illisibles derrière un défilement horizontal
   (BENEFICIAIRES.md §7.2, trouvé au navigateur le 24 août 2026 — la
   seconde table, juste en dessous de la première dans le même onglet,
   portait le même défaut). */
@media (max-width: 640px) {
  .tableau--contrats thead,
  .tableau--pec thead {
    display: none;
  }

  .tableau--contrats,
  .tableau--contrats tbody,
  .tableau--contrats tr,
  .tableau--contrats td,
  .tableau--pec,
  .tableau--pec tbody,
  .tableau--pec tr,
  .tableau--pec td {
    display: block;
    width: 100%;
  }

  .tableau--contrats tr,
  .tableau--pec tr {
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
    margin-bottom: var(--space-3);
    padding: var(--space-3);
  }

  .tableau--contrats tr:last-child,
  .tableau--pec tr:last-child {
    margin-bottom: 0;
  }

  .tableau--contrats td,
  .tableau--pec td {
    border: 0;
    padding: var(--space-2) 0;
  }

  .tableau--contrats td:first-child,
  .tableau--pec td:first-child {
    padding-top: 0;
  }

  .tableau--contrats td:last-child,
  .tableau--pec td:last-child {
    padding-bottom: 0;
  }

  .tableau--contrats td::before,
  .tableau--pec td::before {
    content: attr(data-label);
    display: block;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-tertiary);
    margin-bottom: 2px;
  }

  .tableau--contrats td[data-label="Modifier"]::before,
  .tableau--pec td[data-label="Modifier"]::before {
    content: none;
  }
}

@media (max-width: 600px) {
  .toasts {
    right: var(--space-4);
    bottom: var(--space-4);
    width: calc(100vw - (2 * var(--space-4)));
  }

  .bandeau-emprunt {
    align-items: flex-start;
    padding: var(--space-2) var(--space-4);
  }
}

/* ---------- Parcours « bénéficiaire en 90 secondes » ---------- */

.parcours__colonnes {
  display: grid;
  grid-template-columns: 1fr 320px;
  gap: var(--space-4);
  align-items: start;
}

@media (max-width: 960px) {
  .parcours__colonnes { grid-template-columns: 1fr; }
}

.parcours__etape { margin-bottom: var(--space-4); }

.parcours__etape h2 {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

.parcours__numero {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  flex-shrink: 0;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  font-size: 12px;
  font-weight: 500;
  color: var(--text-secondary);
}

/* Le reste à charge suit la saisie : c'est le chiffre qu'on annonce à la
   famille au téléphone, il ne doit jamais sortir de l'écran. */
.parcours__resume { position: sticky; top: var(--space-4); }

/* Groupe de cases à cocher (jours de livraison) : en ligne, pas empilé.
   Django rend <div id="…"><div><label><input>…</label></div>…</div> — le
   conteneur intermédiaire est la boîte flex. */
/*
 * Choix multiples (jours de tournée, jours de repas, agences d'une
 * invitation, tournées d'un accès livreur) : chaque option est une pilule
 * cliquable qui **garde sa case visible** — choix du porteur, 15 août
 * 2026, sur le modèle de ses références.
 *
 * La cible, c'est la pilule entière et non le carré de 18px : viser une
 * case de 5 mm à la souris est un exercice d'adresse, et nos personas ont
 * 55 ans et plus (DESIGN.md §8).
 */
.cases-en-ligne > div {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.cases-en-ligne label {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 40px;
  padding: var(--space-2) var(--space-4) var(--space-2) var(--space-3);
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-strong);
  background: var(--bg-surface);
  box-shadow: var(--shadow-card);
  font-size: 14px;
  font-weight: 500;
  margin: 0;
  cursor: pointer;
  transition: background var(--transition), border-color var(--transition);
}

.cases-en-ligne label:hover { background: var(--bg-raised); }

/* Retenue, la pilule s'enfonce d'un ton et son libellé s'affirme : deux
   canaux plutôt qu'un seul, une case cochée se lisant mal en biais. Sans
   `:has()`, la case native reste cochée — rien ne se perd. */
.cases-en-ligne label:has(input:checked) {
  background: var(--bg-inset);
  border-color: transparent;
  font-weight: 600;
}

/* Couleur de tournée (25 août 2026) : une pastille par teinte de la
   palette catégorielle — pas une liste de « Couleur 3 », c'est la teinte
   elle-même qu'on reconnaît. La case native reste cliquable et couvre
   toute la pastille (cible ≥40px, DESIGN.md §8) ; son anneau de focus
   suffit à indiquer le clavier sans rien dessiner de plus. */
.couleur-tournee-choix {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

.couleur-tournee-option {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  margin: 0;
  cursor: pointer;
}

.couleur-tournee-option input {
  position: absolute;
  inset: 0;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.couleur-tournee-pastille {
  display: block;
  width: 28px;
  height: 28px;
  border-radius: var(--radius-pill);
  border: 2px solid transparent;
  box-shadow: 0 0 0 1px var(--border-strong);
  transition: box-shadow var(--transition), transform var(--transition);
}

.couleur-tournee-option--auto .couleur-tournee-pastille {
  background: var(--bg-inset);
  border-style: dashed;
  border-color: var(--text-tertiary);
  box-shadow: none;
}
.couleur-tournee-option--c1 .couleur-tournee-pastille { background: var(--categorielle-1); }
.couleur-tournee-option--c2 .couleur-tournee-pastille { background: var(--categorielle-2); }
.couleur-tournee-option--c3 .couleur-tournee-pastille { background: var(--categorielle-3); }
.couleur-tournee-option--c4 .couleur-tournee-pastille { background: var(--categorielle-4); }
.couleur-tournee-option--c5 .couleur-tournee-pastille { background: var(--categorielle-5); }
.couleur-tournee-option--c6 .couleur-tournee-pastille { background: var(--categorielle-6); }
.couleur-tournee-option--c7 .couleur-tournee-pastille { background: var(--categorielle-7); }
.couleur-tournee-option--c8 .couleur-tournee-pastille { background: var(--categorielle-8); }

.couleur-tournee-option:has(input:checked) .couleur-tournee-pastille {
  box-shadow: 0 0 0 2px var(--bg-surface), 0 0 0 4px var(--text-primary);
  transform: scale(1.08);
}

.couleur-tournee-option:has(input:focus-visible) .couleur-tournee-pastille {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

/* En sombre, le creux est presque noir : la pilule retenue s'y enfoncerait
   comme un trou, quand les autres ressortiraient en relief — exactement
   l'inverse de ce qu'on veut dire. Elle se relève donc d'un ton au lieu de
   se creuser ; la direction change, le principe (« la retenue se démarque
   du fond ») ne bouge pas. */
[data-theme="dark"] .cases-en-ligne label:has(input:checked) {
  background: var(--bg-raised);
}

/* L'anneau de focus entoure la pilule, pas la case qu'elle contient. */
.cases-en-ligne label:has(input:focus-visible) {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

/*
 * La case d'une pilule est un **rond qui se remplit** (choix du porteur,
 * 15 août 2026) : anneau vide au repos, aplat d'accent et coche blanche
 * une fois retenu. Le carré natif reste la règle partout ailleurs (une
 * ligne de tableau, un interrupteur seul) — ici la pilule est un composant
 * à part entière, et la forme ronde va avec.
 *
 * La coche est dessinée en CSS (deux bords tournés) plutôt qu'en image :
 * elle prend ainsi la couleur du thème, quand une SVG en dur resterait
 * blanche sur l'accent clair du mode sombre.
 */
.cases-en-ligne input[type="checkbox"] {
  position: relative;
  appearance: none;
  -webkit-appearance: none;
  width: 20px;
  height: 20px;
  border: 1.5px solid var(--border-strong);
  border-radius: 50%;
  background: var(--bg-surface);
  transition: background var(--transition), border-color var(--transition);
}

.cases-en-ligne input[type="checkbox"]:checked {
  background: var(--accent);
  border-color: var(--accent);
}

.cases-en-ligne input[type="checkbox"]::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 47%;
  width: 5px;
  height: 9px;
  border: solid var(--accent-contrast);
  border-width: 0 2px 2px 0;
  transform: translate(-50%, -50%) rotate(45deg);
  opacity: 0;
  transition: opacity var(--transition);
}

.cases-en-ligne input[type="checkbox"]:checked::after { opacity: 1; }

/* `width: auto` contrait le `width: 100%` des champs de saisie ; celui-ci
   ne s'applique plus aux cases (voir le `:not()` plus haut), et la règle
   écrasait alors leur taille de 18px. */
.cases-en-ligne input[type="checkbox"] { margin: 0; }

/* ---------- Mini-carte (confirmation d'adresse — DESIGN.md §6) ---------- */

.mini-carte {
  height: 200px;
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  overflow: hidden;
}

/* Fond en niveaux de gris : la carte est un support, pas un décor. La
   désaturation est posée sur la couche raster du style (static/js/fond_carte.js)
   et non ici : en CSS, elle décolorerait aussi ce que MapLibre dessine
   par-dessus les tuiles. */

/* ---------- Vue carte des tournées (DESIGN.md §6, deux volets) ---------- */

/* Liste à gauche, carte à droite : le gestionnaire lit une liste ordonnée
   et vérifie le tracé, deux gestes qui doivent tenir dans le même écran. */
.carte-tournee {
  display: grid;
  grid-template-columns: 22rem 1fr;
  gap: var(--space-6);
  align-items: start;
}

@media (max-width: 60rem) {
  .carte-tournee { grid-template-columns: 1fr; }
}

.carte-tournee__plan {
  height: 32rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  overflow: hidden;
  position: sticky;
  top: var(--space-4);
}

.carte-tournee__liste { padding: 0; max-height: 32rem; overflow-y: auto; }

/* ---------- Carte de comparaison — écran « Optimiser » ---------- */

/* ---------- Écran « Optimiser » — le verdict d'abord ----------
   Maquette (variante A) validée par le porteur le 20 août 2026 : le gain
   en chiffre-clé ouvre la page, la carte montre un seul circuit à la fois
   (bascule Avant | Après), et seule la liste de ce qui bouge est dépliée. */

.optimiser-verdict,
.optimiser-circuit,
.optimiser-bouges-carte {
  margin-bottom: var(--space-4);
}

.optimiser-verdict {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--space-4) var(--space-8);
}

.optimiser-verdict__gain { flex: 1 1 260px; }

/* Un cran au-dessus du chiffre-clé (28px) : c'est LE chiffre de l'écran,
   celui qui répond à « ça vaut le coup ? » avant toute lecture. */
.optimiser-verdict__chiffre {
  margin: 0;
  font-size: 34px;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
  color: var(--success);
}

/* Un gain de temps négatif se voit, il ne se déguise pas : optimiser les
   kilomètres peut coûter des minutes (DESIGN.md §1 — la couleur est une
   information). */
.optimiser-verdict__chiffre--attention { color: var(--warning); }
.optimiser-verdict__chiffre--muet { color: var(--text-tertiary); }

.optimiser-verdict__phrase {
  margin: var(--space-2) 0 0;
  max-width: 44ch;
}

.optimiser-comparaison {
  border-collapse: collapse;
  font-variant-numeric: tabular-nums;
  font-size: 14px;
}

.optimiser-comparaison th,
.optimiser-comparaison td {
  padding: var(--space-1) 0 var(--space-1) var(--space-6);
  text-align: right;
}

.optimiser-comparaison thead th {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-tertiary);
}

.optimiser-comparaison tbody th {
  padding-left: 0;
  text-align: left;
  font-weight: 400;
  color: var(--text-secondary);
}

/* Le côté « Proposé » porte le poids : c'est lui qu'on évalue. La teinte
   catégorielle est celle de son tracé sur la carte — même information,
   même couleur. */
.optimiser-comparaison thead th.optimiser-comparaison__propose { color: var(--categorielle-1); }
.optimiser-comparaison tbody td.optimiser-comparaison__propose { font-weight: 600; }

.optimiser-verdict__reserve {
  flex-basis: 100%;
  margin: 0;
}

.optimiser-circuit__entete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-bottom: var(--space-3);
}

.optimiser-carte__plan {
  height: 24rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  overflow: hidden;
}

.optimiser-circuit__legende {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: var(--space-2) 0 0;
}

.optimiser-carte__cle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: 13px;
  color: var(--text-secondary);
}

/* Le dépôt est un point, pas un trajet : un carré plein, comme son repère
   sur la carte — jamais un trait qui laisserait croire à un tracé. */
.optimiser-carte__cle--depot::before {
  content: "";
  display: block;
  width: 0.625rem;
  height: 0.625rem;
  border-radius: 3px;
  background: var(--text-primary);
}

.optimiser-bouges {
  list-style: none;
  margin: 0;
  padding: 0;
}

.optimiser-bouges li {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--border);
}

.optimiser-bouges li:last-child { border-bottom: 0; }

.optimiser-bouges__nom { flex: 1; min-width: 0; }

.optimiser-bouges__origine { font-variant-numeric: tabular-nums; }

/* « était 4 » est une provenance, pas un état : le point de pastille des
   badges annoncerait un statut qui n'existe pas. */
.optimiser-bouges__origine::before { content: none; }

/* Même rond en creux que les numéros d'arrêt de la planification : un
   rang est un rang, quel que soit l'écran. */
.optimiser-rang {
  flex-shrink: 0;
  width: 1.6rem;
  height: 1.6rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--bg-inset);
  color: var(--text-secondary);
  font-size: 12px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.optimiser-inchanges { margin-top: var(--space-2); }

.optimiser-inchanges summary {
  cursor: pointer;
  padding: var(--space-2) 0;
  font-size: 13px;
  color: var(--text-secondary);
}

.optimiser-inchanges summary:hover { color: var(--text-primary); }

/* ---------- Optimisation de la journée entière ----------
   Le même verdict que l'écran d'une tournée (classes `.optimiser-verdict`
   réutilisées telles quelles — c'est le même composant et la même
   promesse), puis une liste de tournées triée par ce qu'il y a à y gagner.
   Refondue le 21 août 2026 : la première version alignait des cartes de
   même poids, sans dire par quoi commencer. */

/* Empilés et alignés à droite : le décompte fait contrepoids au chiffre du
   verdict, là où l'écran d'une tournée met sa comparaison avant/après.
   En ligne, il flottait seul en haut d'un grand vide. */
.optim-jour__etats {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--space-1);
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: 13px;
  color: var(--text-secondary);
}

.optim-jour__etats strong {
  font-variant-numeric: tabular-nums;
  color: var(--text-primary);
}

.optim-jour__etats--bloque strong { color: var(--warning); }

.optim-jour__geste { flex-basis: 100%; }

.optim-jour {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Quatre colonnes au bureau — identité, avant/après, gain, action — pour
   que les chiffres s'alignent d'une ligne à l'autre : c'est ce qui rend la
   liste comparable d'un coup d'œil, et le tri lisible. */
.optim-jour__ligne {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto auto;
  align-items: center;
  gap: var(--space-2) var(--space-5);
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--border);
}

.optim-jour__ligne:last-child { border-bottom: 0; }

/* Une tournée qu'on ne peut pas comparer se retire d'un cran : elle reste
   lisible et comptée, elle ne dispute pas l'attention aux gains. */
.optim-jour__ligne--bloquee .optim-jour__nom { color: var(--text-secondary); }

.optim-jour__identite { min-width: 0; }

.optim-jour__nom {
  margin: 0;
  font-weight: 600;
}

.optim-jour__identite .meta { margin: 2px 0 0; }

.optim-jour__evolution,
.optim-jour__gain,
.optim-jour__etat {
  margin: 0;
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.optim-jour__avant { color: var(--text-secondary); }
.optim-jour__fleche { color: var(--text-tertiary); }
.optim-jour__apres { font-weight: 600; }

.optim-jour__gain { justify-content: flex-end; }

.optim-jour__gain-chiffre {
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--success);
}

/* Optimiser les kilomètres peut coûter des minutes : la ligne le dit au
   lieu de le cacher derrière un gain d'une autre unité. */
.optim-jour__gain-chiffre--attention { color: var(--warning); }

.optim-jour__etat {
  grid-column: 2 / -1;
  justify-content: flex-end;
  flex-wrap: wrap;
  white-space: normal;
}

.optim-jour__geste-ligne { text-align: right; }

@media (max-width: 47.9375rem) {
  /* Deux colonnes en mobilité : l'identité et le gain restent côte à côte
     — c'est la comparaison qu'on est venu faire —, le reste passe dessous
     plutôt que de comprimer les chiffres. */
  .optim-jour__ligne {
    grid-template-columns: minmax(0, 1fr) auto;
    gap: var(--space-2) var(--space-3);
  }

  .optim-jour__evolution { grid-column: 1; }
  .optim-jour__gain { grid-row: 1; grid-column: 2; }
  .optim-jour__action { grid-column: 2; justify-self: end; }
  .optim-jour__etat { grid-column: 1 / -1; justify-content: flex-start; }
  .optim-jour__geste-ligne { text-align: left; }

  /* Le décompte revient à gauche : aligné à droite sous une phrase alignée
     à gauche, il laissait un bord déchiqueté au milieu de la carte. */
  .optim-jour__etats { align-items: flex-start; }
}

/* Surlignage croisé liste ↔ carte : un survol ou un clic d'un côté doit se
   voir de l'autre, sinon les deux volets ne racontent pas la même tournée. */
.carte-tournee__arret {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  border-bottom: 1px solid var(--border);
  border-left: 2px solid transparent;
  background: none;
}

/* La sélection (surlignage croisé) est un bouton distinct des flèches de
   déplacement : une ligne ne peut pas être elle-même un bouton et en
   contenir d'autres. */
.carte-tournee__selection {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  flex: 1;
  min-width: 0;
  padding: var(--space-3) var(--space-4);
  cursor: pointer;
  text-align: left;
  background: none;
  border: 0;
  color: inherit;
  font: inherit;
}

.carte-tournee__arret:hover { background: var(--bg-raised); }

.carte-tournee__poignees {
  display: flex;
  flex: none;
  gap: var(--space-1);
  padding-right: var(--space-3);
}

/* Pendant le glissé : la ligne saisie s'efface pour que l'œil suive le
   trou qu'elle laisse, qui est l'information utile (où va-t-elle tomber). */
.carte-tournee__arret--saisi { opacity: 0.4; }
.carte-tournee__arret--cible { border-top: 2px solid var(--accent); }

.carte-tournee__arret[draggable="true"] { cursor: grab; }

/* Barre de validation : n'apparaît qu'une fois l'ordre modifié. Collée en
   bas de la fenêtre, parce que la liste défile et que le bouton ne doit pas
   partir hors de vue avec elle. */
.barre-ordre {
  position: sticky;
  bottom: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-top: var(--space-4);
  padding: var(--space-3) var(--space-4);
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-float);
}

.barre-ordre p { margin: 0; font-weight: 500; }

/* `display: flex` ci-dessus écrase l'attribut `hidden` du HTML, qui n'agit
   que par le `display: none` de la feuille du navigateur. Sans cette ligne,
   la barre « ordre modifié » s'affiche dès le chargement, avant tout
   changement — piège classique, trouvé en regardant la page. */
.barre-ordre[hidden] { display: none; }

.carte-tournee__arret--actif {
  background: var(--bg-raised);
  border-left-color: var(--accent);
}

/* Absent du jour : grisé, jamais retiré — l'arrêt reste une information. */
.carte-tournee__arret--absent { opacity: 0.5; }

.carte-tournee__numero {
  flex: none;
  min-width: 1.6rem;
  height: 1.6rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border-strong);
  border-radius: 50%;
  background: var(--bg-surface);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}

/* Pastille numérotée posée sur la carte — même forme que dans la liste,
   pour que l'œil fasse le lien sans légende. */
.repere-arret {
  width: 1.6rem;
  height: 1.6rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border-strong);
  border-radius: 50%;
  background: var(--bg-surface);
  color: var(--text-primary);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
}

.repere-arret--actif {
  background: var(--accent);
  color: var(--accent-contrast, #fff);
  border-color: var(--accent);
}

.repere-arret--absent { opacity: 0.5; }

/* Le dépôt dans la liste de planification — encadre les arrêts sans en
   être un. Retrait à gauche aligné sur les numéros d'arrêt. */
.planif-depot {
  margin: var(--space-2) 0;
  padding-left: var(--space-2);
  border-left: 2px solid var(--border-strong);
}

/* Le dépôt — d'où le camion part et où il revient. Carré et plein là où
   les arrêts sont ronds et numérotés : ce n'est pas une livraison, et la
   forme le dit sans dépendre de la couleur (DESIGN.md §1). */
.repere-arret--depot {
  border-radius: var(--radius-control);
  cursor: default;
  border-color: transparent;
}

/*
 * Statuts de livraison (Suivi de livraison) — mêmes classes `.carte-tournee`
 * que la carte d'une tournée, avec un liseré de couleur en plus. Jamais la
 * seule couleur : le badge texte de la ligne porte toujours le sens
 * (DESIGN.md §1) ; le liseré n'est qu'un repère au survol de la liste.
 * `--absent` existe déjà (opacité, ci-dessus) pour l'arrêt sans passage
 * prévu — ici le même mot désigne un passage **constaté** sans personne à
 * la porte (`Passage.Etat.ABSENT`), d'où le liseré rouge en plus.
 */
.carte-tournee__arret--livre { border-left-color: var(--success); }
.carte-tournee__arret--absent { border-left-color: var(--danger); }
.carte-tournee__arret--refuse { border-left-color: var(--warning); }

/* L'arrêt vers lequel le véhicule se dirige — le seul mis en évidence
   parmi ceux qui attendent encore, pour qu'on le retrouve d'un coup
   d'œil dans une liste de vingt lignes. */
.carte-tournee__arret--en-cours {
  border-left-color: var(--accent);
  background: var(--bg-raised);
}

.carte-tournee__statut {
  margin-left: auto;
  flex-shrink: 0;
  text-align: right;
}

/* Repère véhicule — une estimation, jamais un GPS réel (voir
   `apps.livreur.suivi._position_estimee`). Forme distincte des pastilles
   numérotées pour ne jamais se lire comme un énième arrêt. */
.repere-vehicule {
  width: 1.8rem;
  height: 1.8rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--accent);
  color: var(--accent-contrast, #fff);
  border: 2px solid var(--bg-surface);
  box-shadow: var(--shadow-float);
  font-size: 14px;
  pointer-events: none;
}

/*
 * Barre de progression — Suivi de livraison. Le remplissage seul ne dit
 * rien sans le chiffre à côté (« X/N livraisons traitées », posé par le
 * gabarit juste en dessous) : deux canaux, jamais un seul (DESIGN.md §1).
 */
.barre-progression {
  height: 0.5rem;
  border-radius: 999px;
  background: var(--bg-inset);
  overflow: hidden;
}

.barre-progression__remplissage {
  height: 100%;
  background: var(--accent);
  border-radius: 999px;
  transition: width var(--transition);
}

/*
 * Utilisation de Madelaine — la page « où j'en suis, où ça part ».
 *
 * Une pile de lignes, chacune : un libellé à gauche, sa mesure à droite,
 * la barre dessous, le détail en dessous encore. La séparation entre deux
 * lignes est un filet, pas une carte de plus — deux cartes pour deux
 * mesures du même mois se liraient comme deux sujets.
 */
/* Un titre de section qui suit une carte a besoin d'air : collé, il se lit
   comme la légende de la carte du dessus plutôt que comme l'annonce de
   celle du dessous. */
.utilisation + h2 { margin-top: var(--space-6); }

.utilisation__ligne {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-2);
}

.utilisation__ligne--secondaire {
  margin-top: var(--space-5);
  padding-top: var(--space-5);
  border-top: 1px solid var(--border);
}

.utilisation__quoi { font-weight: 500; }
/* La mesure est chiffrée, donc alignée en chiffres tabulaires : deux
   lignes empilées doivent se comparer d'un coup d'œil. */
.utilisation__part {
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.utilisation__detail { margin: var(--space-2) 0 0; }

/* Plusieurs compteurs empilés dans une même carte — « Abonnement &
   facturation », où bénéficiaires, sièges et crédits se suivent. Sans
   séparation, l'aide d'une ligne se lit comme le sous-titre de la
   suivante : constaté à la capture du 31 août 2026, où « Les comptes
   actifs de votre équipe » semblait qualifier « Crédits Madelaine ». */
.compteur + .compteur {
  margin-top: var(--space-5);
  padding-top: var(--space-5);
  border-top: 1px solid var(--border);
}

.utilisation__note {
  margin: var(--space-5) 0 0;
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
}

/* Le point qu'on est en train d'insérer. Il se distingue par le remplissage
   **et** par un anneau : la seule couleur ne suffirait pas à le retrouver
   parmi vingt pastilles, et c'est précisément lui qu'on vient chercher. */
.repere-arret--nouveau {
  background: var(--accent);
  color: var(--accent-contrast, #fff);
  border-color: var(--accent);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 25%, transparent);
}

/* Voir note sur `.barre-ordre[hidden]` : une règle `display` d'auteur
   l'emporte sur l'attribut `hidden`, qui n'agit que par la feuille du
   navigateur. */
.carte[hidden] { display: none; }

/* ---------- Zone en creux (dépôt de fichiers) ---------- */

/*
 * La zone est un `<label>` : tout le rectangle ouvre le sélecteur, sans
 * JavaScript. Le champ lui-même est masqué à l'œil mais pas au clavier
 * (`.visuellement-cache`), donc le focus atteint toujours la zone.
 */
.zone-depot {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-10) var(--space-6);
  background: var(--bg-raised);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-card);
  text-align: center;
  color: var(--text-secondary);
  cursor: pointer;
  transition: background var(--transition), border-color var(--transition);
}

.zone-depot:hover { background: var(--bg-inset); }

/* Pendant le survol d'un fichier glissé : la zone s'affirme, sans changer
   de taille — un rectangle qui grandit sous le curseur fait rater la cible. */
.zone-depot--survolee {
  background: var(--bg-inset);
  border-color: var(--accent);
  border-style: solid;
}

/* Le champ est masqué visuellement, jamais retiré du flux clavier : c'est
   lui qui reçoit le focus, et l'anneau se dessine autour de la zone. */
.zone-depot:has(input:focus-visible) {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

.zone-depot__icone {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  margin-bottom: var(--space-1);
  border-radius: var(--radius-control);
  background: var(--bg-surface);
  box-shadow: var(--shadow-card);
  color: var(--text-primary);
}

.zone-depot__titre {
  color: var(--text-primary);
  font-size: 15px;
  font-weight: 500;
}

/* Souligné comme un lien : c'est le mot sur lequel on clique quand on ne
   sait pas qu'on peut glisser un fichier. */
.zone-depot__lien { text-decoration: underline; text-underline-offset: 3px; }

.zone-depot__aide { font-size: 13px; color: var(--text-tertiary); }

/* ---------- Fichiers choisis, sous la zone de dépôt ---------- */

.fichiers-choisis {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-top: var(--space-4);
}

.fichiers-choisis__titre {
  font-size: 13px;
  font-weight: 500;
  color: var(--text-secondary);
}

.fichier-choisi {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-control);
  background: var(--bg-surface);
}

/* L'extension en pastille : elle dit le format sans qu'on lise la fin du
   nom, souvent tronquée. */
.fichier-choisi__type {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 40px;
  flex-shrink: 0;
  border-radius: var(--radius-control);
  background: var(--bg-inset);
  color: var(--text-secondary);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.fichier-choisi__texte {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.fichier-choisi__nom {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 14px;
  font-weight: 500;
}

.fichier-choisi__taille {
  font-size: 13px;
  color: var(--text-tertiary);
  font-variant-numeric: tabular-nums;
}

/* Un fichier refusé (trop lourd) se dit à côté des retenus — jamais
   silencieux, sinon on le redépose en croyant à un oubli de clic. */
.fichier-choisi__erreur {
  font-size: 13px;
  color: var(--danger);
  margin: 0;
}

/* Progression réelle de l'envoi — jamais une animation décorative : la
   barre ne paraît que si le script mesure vraiment le téléversement
   (DESIGN.md §1, aucun chiffre qu'on ne sait pas tenir). */
/* `display: block` sur les deux : ce sont des `<span>`, et ni la hauteur ni
   la largeur ne s'appliquent à un élément inline — la piste se peignait
   sur la hauteur de ligne et le remplissage restait invisible. */
.fichier-choisi__progression {
  display: block;
  height: 4px;
  margin-top: var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--bg-inset);
  overflow: hidden;
}

.fichier-choisi__remplissage {
  display: block;
  height: 100%;
  width: 0;
  border-radius: var(--radius-pill);
  background: var(--accent);
  transition: width 200ms linear;
}

/* ---------- Page famille (accès sans compte — PRD §6.2) ---------- */

/* Lue sur le téléphone d'un proche, souvent d'un certain âge : une colonne,
   des cibles larges, aucune navigation. Elle n'emprunte rien à la coque de
   l'application, dont la barre latérale n'aurait ici aucun sens. */
.famille {
  max-width: 40rem;
  margin: 0 auto;
  padding: var(--space-6) var(--space-4) var(--space-10);
}

/* 🔴 **Le nom de la structure porte sa couleur, en pastille** (demande du
   porteur, 31 août 2026). C'est la page où la marque sert le plus : un lien
   reçu par SMS et qui n'affiche rien d'identifiable ressemble à un
   hameçonnage, et une famille qui hésite n'ouvre pas (DESIGN.md §12). Le
   nom était gris, de la taille d'une légende, au-dessus du prénom de la
   personne — l'expéditeur pesait moins que le sujet.

   Une pastille et non du texte coloré : une couleur de marque n'est validée
   qu'à 3:1 contre le fond de page (seuil « composant »), quand du texte de
   14 px en demande 4,5:1. En aplat, le texte est déduit (noir ou blanc) et
   ne descend jamais sous 4,6:1 — c'est le même raisonnement que la bande
   d'en-tête du bon de commande (PLAN §54).

   Sans couleur réglée, la pastille prend l'encre du thème : noire en clair,
   claire en sombre. Elle se voit dans tous les cas — c'est bien l'objet. */
.famille__structure {
  display: inline-block;
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  background: var(--marque);
  color: var(--marque-contrast);
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-pill);
  margin: 0 0 var(--space-3);
}

.famille__entete { margin-bottom: var(--space-6); }

/* La page aidant est une surface d'accueil (DESIGN.md §5.3) : en sombre,
   l'en-tête porte un lavis — c'est la première chose qu'ouvre un proche
   depuis un SMS. En clair, il reste nu (§2). */
[data-theme="dark"] .famille__entete {
  padding: var(--space-5) var(--space-4);
  border-radius: var(--radius-card);
  background:
    radial-gradient(110% 150% at 6% 0%, var(--decor-peche) 0%, transparent 60%),
    radial-gradient(110% 150% at 96% 100%, var(--decor-ciel) 0%, transparent 60%),
    var(--bg-surface);
}
.famille__entete h1 { margin: 0 0 var(--space-3); }

.famille__message {
  border: 1px solid var(--border);
  border-left-width: 3px;
  border-radius: var(--radius-card);
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-4);
}

.famille__message--success { border-left-color: var(--success); }
.famille__message--error { border-left-color: var(--danger); }

.famille__semaine { margin-bottom: var(--space-8); }
.famille__semaine h2 { margin: 0 0 var(--space-3); font-size: 1.125rem; }

.famille__gele {
  color: var(--text-secondary);
  font-size: 0.9375rem;
  margin: 0 0 var(--space-3);
}

/* La date limite de la semaine affichée : une information à retenir, pas un
   avertissement — l'accent sémantique reste au verrou (`famille__gele`). */
.famille__limite {
  color: var(--text-secondary);
  font-size: 0.9375rem;
  margin: 0 0 var(--space-3);
}
.famille__limite strong { color: var(--text-primary); }

/* ---------- Navigation de semaines ---------- */

/* Toutes les semaines visibles d'un geste, chacune disant combien de jours
   y attendent une réponse — c'est ce qui empêche d'en « louper » une
   derrière celle qu'on regarde (demande du porteur, 19 août 2026). */
.famille__semaines {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-5);
}

.famille__onglet {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 36px;
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  background: var(--bg-surface);
  color: var(--text-primary);
  font-size: 13px;
  font-weight: 500;
  white-space: nowrap;
}

.famille__onglet:hover { background: var(--bg-raised); text-decoration: none; }

.famille__onglet--courant {
  background: var(--text-primary);
  border-color: var(--text-primary);
  color: var(--bg-surface);
}

/* Le compteur reprend la couleur d'avertissement du reste du produit :
   « il reste quelque chose à trancher », jamais une décoration (§1). */
.famille__onglet__reste {
  min-width: 1.25rem;
  height: 1.25rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 4px;
  border-radius: var(--radius-pill);
  background: var(--warning);
  color: var(--accent-contrast);
  font-size: 11px;
  font-weight: 600;
}

.famille__jours { list-style: none; margin: 0; padding: 0; }

.famille__jour {
  border-top: 1px solid var(--border);
  padding: var(--space-4) 0;
}

/* Un jour gelé reste visible, atténué : le masquer ferait croire à un oubli
   du calendrier, et la famille chercherait ce qu'elle ne trouve pas. */
.famille__jour--gele { opacity: 0.55; }

.famille__date {
  font-weight: 600;
  margin: 0 0 var(--space-2);
}

/* « À choisir » : le même signal que le compteur de la navigation, posé
   sur le jour lui-même. */
.famille__a-choisir {
  margin-left: var(--space-2);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--warning);
}

/* Une ligne par menu (choix du porteur, 19 août 2026) : le plat en gras —
   c'est lui qu'une famille compare —, le reste du menu dessous, le nom de
   la formule en repère à droite. La cible entière est cliquable : sur un
   téléphone tenu d'une main, viser un cercle de 3 mm est un échec. */
.famille__option {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  background: var(--bg-surface);
  padding: var(--space-3);
  margin-bottom: var(--space-2);
  cursor: pointer;
}

.famille__option--retenue {
  /* v1 : `var(--text)` inexistant — le contour du choix retenu ne se
     distinguait pas des autres options. */
  border-color: var(--text-primary);
  border-width: 2px;
  padding: calc(var(--space-3) - 1px);
}

/* Le contour suit la case cochée **en direct** : avant, seule la classe
   posée par le serveur le portait, et cliquer un autre menu laissait le
   contour sur l'ancien jusqu'à l'enregistrement. Repli sans `:has()` : le
   rond se remplit quand même, rien ne se perd. */
.famille__option:has(input:checked) {
  border-color: var(--text-primary);
  border-width: 2px;
  padding: calc(var(--space-3) - 1px);
}

.famille__option:has(input:focus-visible) {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

/* Un menu contraire à une allergie déclarée : visible mais non choisissable
   — masqué, la famille le croirait supprimé. La raison s'écrit en rouge
   sous le plat ; la couleur est une information (DESIGN.md §1). */
.famille__option--allergene { cursor: not-allowed; }
.famille__option--allergene .famille__option__plat,
.famille__option--allergene .famille__option__detail { color: var(--text-faint); }
.famille__option__allergene {
  display: block;
  margin-top: var(--space-1);
  font-size: 14px;
  color: var(--danger);
}

/* « Absent » ne dit pas quel menu, il dit qu'il n'y en aura pas : le
   pointillé le détache de la liste des menus sans l'en éloigner. */
.famille__option--absent { border-style: dashed; margin-top: var(--space-3); }

.famille__option--absent:has(input:checked),
.famille__option--absent.famille__option--retenue { border-style: solid; }

.famille__option--absent .famille__option__plat {
  font-weight: 400;
  color: var(--text-secondary);
}

/* Le même rond qui se remplit que les pilules de l'offre
   (`.cases-en-ligne`) : anneau vide au repos, aplat d'accent et coche
   blanche une fois retenu — un seul langage pour « choisir » dans tout le
   produit, page publique comprise. */
.famille__option input[type="radio"] {
  position: relative;
  appearance: none;
  -webkit-appearance: none;
  flex-shrink: 0;
  align-self: center;
  width: 20px;
  height: 20px;
  margin: 0;
  border: 1.5px solid var(--border-strong);
  border-radius: 50%;
  background: var(--bg-surface);
  transition: background var(--transition), border-color var(--transition);
}

.famille__option input[type="radio"]:checked {
  background: var(--accent);
  border-color: var(--accent);
}

.famille__option input[type="radio"]::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 47%;
  width: 5px;
  height: 9px;
  border: solid var(--accent-contrast);
  border-width: 0 2px 2px 0;
  transform: translate(-50%, -50%) rotate(45deg);
  opacity: 0;
  transition: opacity var(--transition);
}

.famille__option input[type="radio"]:checked::after { opacity: 1; }

.famille__option__menu {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.famille__option__plat { font-weight: 500; }

.famille__option__detail {
  color: var(--text-secondary);
  font-size: 0.875rem;
  line-height: 1.4;
}

.famille__option__nom {
  margin-left: auto;
  flex-shrink: 0;
  color: var(--text-secondary);
  font-size: 13px;
  text-align: right;
}

.famille__actions {
  position: sticky;
  bottom: 0;
  /* v1 : `var(--bg)` inexistant — la barre collante était transparente et
     le texte défilait au travers. */
  background: var(--bg-app);
  border-top: 1px solid var(--border);
  padding: var(--space-4) 0;
}

.famille__pied {
  margin-top: var(--space-8);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
}

/* ---------- Écran de validation du scan (PRD §6.2) ---------- */

/* L'image de la feuille et la lecture côte à côte : corriger une case en
   ayant le scan sous les yeux, jamais de mémoire. Sous 1100px la colonne
   se replie — l'image passe au-dessus, puisque c'est elle qui fait foi. */
.grille-scan {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
  gap: var(--space-6);
  align-items: start;
}

@media (max-width: 1100px) {
  .grille-scan { grid-template-columns: 1fr; }
}

/* L'image et ses repères partagent le même repère de coordonnées : les
   rectangles sont posés en pourcentage, donc ils suivent l'image à toutes
   les tailles d'affichage, sans une ligne de JavaScript. */
.scan-cadre {
  position: relative;
  display: block;
  line-height: 0;
}

.scan-image {
  width: 100%;
  height: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  background: #fff;
}

/* Un cadre, jamais un aplat : le repère doit prouver qu'on a regardé au bon
   endroit, donc il ne doit pas cacher ce qu'il désigne — c'est la marque au
   stylo que la gestionnaire vient vérifier. */
.scan-repere {
  position: absolute;
  border: 2px solid var(--text-secondary);
  border-radius: 2px;
  pointer-events: auto;
}

.scan-repere--cochee { border-color: var(--success); box-shadow: 0 0 0 1px var(--success); }
.scan-repere--doute { border-color: var(--warning); box-shadow: 0 0 0 1px var(--warning); }
/* Regardée sans rien trouver : présente mais discrète — c'est le cas le plus
   fréquent, et il ne doit pas noyer les deux autres. */
.scan-repere--vide { border-color: var(--border); border-style: dashed; }

.scan-legende { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }

.scan-puce {
  display: inline-block;
  width: 0.75rem;
  height: 0.75rem;
  border: 2px solid var(--border);
  border-radius: 2px;
  margin-left: var(--space-3);
}

.scan-puce--cochee { border-color: var(--success); }
.scan-puce--doute { border-color: var(--warning); }
.scan-puce--vide { border-style: dashed; }

/* Cibles larges : cet écran se manipule vite, souvent au trackpad, et une
   erreur de clic y écrit une commande. */
.choix-scan {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin-right: var(--space-4);
  padding: var(--space-1) 0;
  white-space: nowrap;
}

/* 🔴 Le même choix, mais **empilé** et non aligné en pastilles (réglages des
   tournées). `.choix-scan` porte `white-space: nowrap`, juste pour des
   libellés courts posés côte à côte sur l'écran de scan — appliqué à une
   description de deux lignes, il la forçait sur une seule et faisait
   défiler tout l'écran de 177 px à 390 px (mesuré le 27 août 2026, défaut
   préexistant trouvé en posant le réglage des relevés).

   `min-width: 0` sur l'enfant : sans lui, un enfant de flex refuse de
   descendre sous la largeur de son contenu, et le retour à la ligne ne
   suffit pas. */
.choix-scan--empile {
  display: flex;
  align-items: flex-start;
  white-space: normal;
  margin-right: 0;
  padding: var(--space-2) 0;
}

.choix-scan--empile > span { min-width: 0; }

/* Un groupe de plats d'un même type, en mode « par composant ». */
.choix-scan-groupe {
  margin-bottom: var(--space-3);
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--border);
}

.choix-scan-groupe:last-of-type { border-bottom: none; }

.choix-scan-groupe .meta { display: block; }

/* ---------- Marque de la structure ---------- */

/* Le logo remplace le nom en toutes lettres quand il existe : les deux
   côte à côte encombrent une barre latérale qui doit surtout laisser voir
   la navigation. Hauteur bornée — un fichier mal cadré ne doit pas pousser
   les liens hors de l'écran. */
.sidebar__logo {
  max-height: 32px;
  max-width: 100%;
  object-fit: contain;
  display: block;
}

/* Sur la page famille, le logo passe avant le nom : c'est lui qui est
   reconnu en un coup d'œil quand le lien arrive par SMS. */
.famille__logo {
  max-height: 48px;
  max-width: 200px;
  object-fit: contain;
  margin-bottom: var(--space-3);
}

/* Découpage des tournées : les lots côte à côte, pour que la comparaison
   des charges se fasse d'un coup d'œil et non en faisant défiler. */
.grille-lots {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--space-4);
}

/* Un arrêt dont la place est imposée : le motif se lit au survol, mais le
   repère doit se voir sans interaction — sinon personne ne comprend
   pourquoi l'optimisation « n'a pas marché » sur cette ligne. */
.badge-epingle {
  font-size: 12px;
  color: var(--text-secondary);
  white-space: nowrap;
}

/* Carte portant une alerte de conformité (durée de tournée hors liaison
   froide) : le liseré gauche, jamais un fond coloré — DESIGN.md §1, la
   couleur signale, elle ne décore pas. */
.carte--alerte { border-left: 2px solid var(--danger); }

/* Suggestion proactive (meilleur ordre, dérive du découpage). Liseré
   `--accent` et non `--warning` : ce n'est ni une alerte ni une erreur,
   c'est une occasion. DESIGN.md §1 — la couleur dit de quoi il s'agit. */
.suggestion { border-left: 2px solid var(--accent); }

/* Colonne « Servi » de la fiche de tournée (23 août 2026) : une pastille
   par jour où le camion roule, pleine quand il s'arrête chez cette
   personne. Les jours de la tournée servent de gabarit — la même semaine
   d'une ligne à l'autre, sinon la colonne ne se compare pas.

   La pastille ne porte pas l'information seule (DESIGN.md §1) : elle
   affiche l'initiale du jour, son `title` l'écrit en toutes lettres, et
   une ligne sans aucune pastille pleine est remplacée par la phrase
   « jamais servi ici » plutôt que par cinq pastilles vides. */
.jours-servis { display: inline-flex; gap: 3px; }

.jour-pastille {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: var(--radius-pill);
  font-size: 11px;
  font-weight: 600;
  background: var(--bg-inset);
  color: var(--text-faint);
}

.jour-pastille--servi { background: var(--accent); color: var(--accent-contrast); }

/* Notes du livreur sur la fiche bénéficiaire (PRD §7.2 [D]) */
.liste-notes { list-style: none; margin: 0; padding: 0; }
.liste-notes li {
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--border);
}
.liste-notes li:last-child { border-bottom: 0; }
.liste-notes p { margin: 0 0 var(--space-1); }
/* Traitée : estompée, jamais retirée — la note dit d'où vient une consigne,
   et sa disparition ferait perdre qui l'a signalée et quand. */
.note--traitee { opacity: 0.55; }

/* --- Dialogue de confirmation (DESIGN.md §13) ------------------------- */
/* Réservé aux gestes irréversibles. Le message nomme ce qui disparaît ;
   c'est lui qui porte l'information, pas la couleur du bouton. */
.dialogue-confirmation {
  max-width: 42ch;
  padding: var(--space-6);
  border: 0;
  border-radius: var(--radius-modal);
  box-shadow: var(--shadow-float);
  background: var(--bg-surface);
  color: var(--text-primary);
}

.dialogue-confirmation::backdrop {
  background: rgb(23 20 14 / 0.4);
}

.dialogue-confirmation__titre {
  margin: 0 0 var(--space-2);
}

.dialogue-confirmation__message {
  margin: 0 0 var(--space-5);
  color: var(--text-secondary);
}

.dialogue-confirmation .barre-actions {
  margin: 0;
  justify-content: flex-end;
}

/* --- Dialogues métier ------------------------------------------------- */
/*
 * Une saisie courte peut s'ouvrir sans quitter son écran d'origine. Le lien
 * garde toujours un vrai `href` : sans JavaScript, la page dédiée reste
 * disponible. Les formulaires riches (carte, import, grand tableau) n'utilisent
 * volontairement pas ce composant.
 */
.dialogue-metier {
  width: min(680px, calc(100vw - (2 * var(--space-6))));
  max-width: none;
  max-height: min(88dvh, 860px);
  margin: auto;
  padding: 0;
  overflow: hidden;
  border: 0;
  border-radius: var(--radius-modal);
  background: var(--bg-surface);
  color: var(--text-primary);
  box-shadow: var(--shadow-float);
}

.dialogue-metier[open] {
  display: flex;
  flex-direction: column;
}

.dialogue-metier::backdrop { background: rgb(23 20 14 / 0.42); }

.dialogue-metier--panneau {
  width: min(760px, 100vw);
  height: 100dvh;
  max-height: 100dvh;
  margin: 0 0 0 auto;
  border-radius: var(--radius-modal) 0 0 var(--radius-modal);
}

.dialogue-metier__entete {
  min-height: 60px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-3) var(--space-6);
  border-bottom: 1px solid var(--border);
}

.dialogue-metier__entete h2 { margin: 0; }

.dialogue-metier__fermer {
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: var(--radius-control);
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
}

.dialogue-metier__fermer:hover {
  background: var(--bg-raised);
  color: var(--text-primary);
}

.dialogue-metier__corps {
  min-height: 160px;
  padding: var(--space-6);
  overflow: auto;
}

.dialogue-metier__corps[aria-busy="true"] { opacity: 0.65; }

.dialogue-metier__corps > .carte {
  max-width: none !important;
  padding: 0;
  border: 0;
}

.dialogue-metier__corps > .entete-page:first-child { display: none; }

.dialogue-metier__chargement,
.dialogue-metier__erreur {
  min-height: 120px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  color: var(--text-secondary);
  text-align: center;
}

.dialogue-metier__erreur {
  color: var(--danger);
}

.dialogue-etapes__navigation {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
}

.dialogue-etapes__navigation [data-etape-precedente] { justify-self: start; }
.dialogue-etapes__navigation [data-etape-suivante] { justify-self: end; }

@media (max-width: 700px) {
  .dialogue-metier,
  .dialogue-metier--panneau {
    width: 100vw;
    height: 100dvh;
    max-height: 100dvh;
    margin: 0;
    border: 0;
    border-radius: 0;
  }

  .dialogue-metier__entete,
  .dialogue-metier__corps { padding-left: var(--space-4); padding-right: var(--space-4); }
}

/* ---------- Planification des tournées (écran d'entrée, DESIGN.md §6) ---------- */

/*
 * Un plan de travail, pas un tableau de bord : la liste des tournées à
 * gauche, la carte à droite, et les deux racontent la même journée. Même
 * partition que `.carte-tournee`, mais pour **plusieurs** tournées — d'où
 * une grille propre plutôt qu'une variante, les deux écrans n'ayant ni le
 * même contenu ni les mêmes gestes.
 */
.planif {
  display: grid;
  grid-template-columns: 26rem 1fr;
  gap: var(--space-4);
  align-items: start;
}

/* Vue « Liste » : la carte n'est pas rendue du tout, le panneau prend
   toute la largeur — le détail (`planif-detail`, désormais dans cette
   grille) s'y range dessous, en pleine largeur lui aussi. */
.planif--liste { grid-template-columns: 1fr; }

/* Le détail rejoint la carte en troisième colonne (demande du porteur,
   22 août 2026) : un panneau qu'il fallait défiler pour voir en bas de
   page n'était ni visible ni intuitif. Fermé, la carte récupère sa place —
   `data-planif-detail-bascule` (`planification_disposition.js`) bascule
   cette classe. */
.planif--detail-ouvert { grid-template-columns: 26rem 1fr 22rem; }

/* Fermé (le bouton bascule a retiré `--detail-ouvert`), le panneau
   disparaît complètement plutôt que de laisser un enfant sans colonne
   dédiée retomber sur une ligne à lui : la carte récupère vraiment sa
   largeur, pas seulement l'espace qu'occupait la troisième colonne. */
.planif:not(.planif--detail-ouvert):not(.planif--liste) .planif-detail { display: none; }

/* La carte en grand écran (même demande) : liste et détail s'effacent, la
   carte prend toute la largeur et gagne en hauteur. */
.planif--carte-pleine { grid-template-columns: 1fr; }
.planif--carte-pleine .planif__panneau,
.planif--carte-pleine .planif-detail { display: none; }
.planif--carte-pleine .planif__plan { height: calc(100vh - 14rem); }

/* Sous 64rem, liste, carte et détail s'empilent : côte à côte, aucun des
   trois ne serait lisible. */
@media (max-width: 64rem) {
  .planif,
  .planif--detail-ouvert,
  .planif--carte-pleine { grid-template-columns: 1fr; }
}

.planif__panneau {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  max-height: 40rem;
  overflow-y: auto;
}

.planif--liste .planif__panneau { max-height: none; overflow: visible; }

/* La zone, et non la carte elle-même, porte la position collante : les
   deux boutons en survol (plein écran, rouvrir le détail) doivent coller
   avec elle, et c'est aussi elle qui sert de repère aux positions
   absolues de ces boutons — `position: sticky` établit ce repère aussi
   bien que `relative`. */
.planif__plan-zone {
  position: sticky;
  top: var(--space-4);
}

.planif__plan {
  height: 40rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  overflow: hidden;
}

/* Bouton icône seul, posé sur ce qu'il change plutôt que dans la barre
   d'outils (demande du porteur, 23 août 2026) : le survol de la carte
   pour l'agrandir, le coin du panneau pour le refermer. */
.planif__survol {
  position: absolute;
  z-index: 5;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-strong);
  background: var(--bg-surface);
  box-shadow: var(--shadow-card);
  color: var(--text-secondary);
  cursor: pointer;
  transition: background var(--transition), color var(--transition);
}

.planif__survol:hover { background: var(--bg-raised); color: var(--text-primary); }
.planif__survol .icone { width: 18px; height: 18px; }

.planif__survol--plein-ecran { top: var(--space-3); left: var(--space-3); }

/* Le bouton qui rouvre le détail vit sur la carte, à l'opposé du contrôle
   de zoom de MapLibre (haut-droite) — il ne s'affiche que si le panneau
   est effectivement fermé ; sinon c'est le bouton posé sur le panneau
   lui-même (`--detail-fermer`) qui fait ce travail. */
.planif__survol--detail-ouvrir { top: var(--space-3); right: var(--space-3); }
.planif--detail-ouvert .planif__survol--detail-ouvrir { display: none; }

/* `.planif-detail` est déjà `position: sticky` (plus bas) : ce n'est pas
   `static`, donc c'est déjà un repère valable pour ce bouton absolu — pas
   besoin d'y ajouter `relative`, qui écraserait le collant. */
.planif__survol--detail-fermer { top: var(--space-3); right: var(--space-3); }

/* Navigation par jour : la date en toutes lettres à côté des flèches —
   « ← → » sans date oblige à compter les clics pour savoir où l'on est. */
.planif-jours {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-bottom: var(--space-4);
}

/* Un seul groupe : « ← [date] Voir → ». Les quatre commandes règlent le
   même réglage, elles se touchent — deux îlots séparés obligeaient à
   chercher lequel servait à quoi. */
.planif-jours__selecteur {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1);
  border-radius: var(--radius-pill);
  background: var(--bg-surface);
  box-shadow: var(--shadow-card);
}

.planif-jours__selecteur input[type="date"] {
  border: none;
  background: transparent;
  font: inherit;
  color: inherit;
  padding: 0 var(--space-1);
}

/* La date est le titre de la journée qu'on regarde : elle a le corps d'un
   titre, pas d'une légende. */
.planif-jours__date { margin: 0; font-size: 16px; font-weight: 600; }

/* « Aujourd'hui » ne s'affiche que hors du jour courant : le pousser à
   droite l'empêche de décaler la date à chaque changement de jour. */
.planif-jours__saut { margin-left: auto; }

/* Encore portée par le suivi de livraison et l'écran de proposition —
   la planification, elle, est passée au segmenté `.bascule-vue`. */
.planif-bascule {
  display: flex;
  gap: var(--space-1);
  margin: var(--space-4) 0 var(--space-2);
}

/* ---------- Le tableau des passages (page « Tournées ») ----------
   Six colonnes dont une de dates : sous 640 px elles s'empilent en
   cartes, comme le tableau des rapprochements — la colonne « Repas
   emportés », celle qu'on vient lire, sortait sinon de l'écran derrière
   un défilement horizontal qu'on ne pense pas toujours à chercher. */
.tableau--passages .meta { display: block; }

.tableau--passages__annulee td { opacity: 0.7; }

@media (max-width: 640px) {
  .tableau--passages thead { display: none; }

  .tableau--passages,
  .tableau--passages tbody,
  .tableau--passages tr,
  .tableau--passages td {
    display: block;
    width: 100%;
  }

  .tableau--passages tr {
    border-radius: var(--radius-card);
    background: var(--bg-raised);
    margin-bottom: var(--space-3);
    padding: var(--space-3);
  }

  .tableau--passages tr:last-child { margin-bottom: 0; }

  .tableau--passages td {
    border: 0;
    padding: var(--space-1) 0;
  }

  /* La date porte son propre libellé sur la première ligne d'un jour et
     reste vide sur les suivantes : empilée, une cellule vide laisserait
     un « Date » orphelin. */
  .tableau--passages td:empty { display: none; }

  .tableau--passages td::before {
    content: attr(data-label);
    display: block;
    font-size: 11px;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-tertiary);
  }

  .tableau--passages td[data-label="Date"]::before { content: none; }

  /* Le ⋯ se nomme tout seul : un libellé « Actions » au-dessus n'ajoute
     rien et met une étiquette de plus dans une carte qui en a déjà cinq. */
  .tableau--passages td[data-label="Actions"]::before { content: none; }

  /* Le menu remonte en haut à droite de la carte, à hauteur de la date —
     là où on le cherche, plutôt qu'en sixième ligne d'une liste. */
  .tableau--passages tr { position: relative; }

  .tableau--passages td[data-label="Actions"] {
    position: absolute;
    top: var(--space-2);
    right: var(--space-2);
    width: auto;
    padding: 0;
  }
}

.planif-alertes { margin-bottom: var(--space-4); }

/* ---------- Ligne d'outils de la planification ----------
   La bascule Carte | Liste à gauche, la recherche « où est ce
   bénéficiaire ? » à droite (demande du porteur, 21 août 2026). */
.planif-outils {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin: var(--space-4) 0 var(--space-2);
}

.planif-recherche {
  position: relative;
  flex: 0 1 22rem;
  min-width: 15rem;
}

.planif-recherche__icone {
  position: absolute;
  left: var(--space-3);
  top: 50%;
  transform: translateY(-50%);
  width: 16px;
  height: 16px;
  color: var(--text-tertiary);
  pointer-events: none;
}

/* La même pilule que la recherche globale du haut : un seul langage de
   champ de recherche dans le produit. */
.planif-recherche__champ {
  width: 100%;
  min-height: 38px;
  padding: 0 var(--space-4) 0 calc(var(--space-3) + 16px + var(--space-2));
  border: 0;
  border-radius: var(--radius-pill);
  background: var(--bg-surface);
  box-shadow: var(--shadow-card);
  color: var(--text-primary);
  font-size: 14px;
}

.planif-recherche__champ::placeholder { color: var(--text-faint); }

.planif-recherche__champ:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

.planif-recherche__resultats {
  position: absolute;
  top: calc(100% + var(--space-2));
  left: 0;
  right: 0;
  z-index: 60;
  padding: var(--space-2);
  border-radius: var(--radius-card);
  background: var(--bg-surface);
  box-shadow: var(--shadow-float);
}

.planif-recherche__resultat {
  display: flex;
  flex-direction: column;
  gap: 2px;
  width: 100%;
  padding: var(--space-2) var(--space-3);
  border: 0;
  border-radius: var(--radius-control);
  background: none;
  text-align: left;
  font: inherit;
  color: var(--text-primary);
  cursor: pointer;
}

.planif-recherche__resultat:hover,
.planif-recherche__resultat:focus-visible {
  background: var(--bg-raised);
  outline: none;
}

.planif-recherche__resultat strong { font-size: 14px; font-weight: 600; }
.planif-recherche__resultat .meta { white-space: normal; }

/* « Dans aucune tournée » est une alerte, et le texte le dit déjà — la
   couleur double le sens, elle ne le porte pas seule (DESIGN.md §1). */
.planif-recherche__situation--alerte { color: var(--danger); }

/* Un résultat qu'on ne peut pas ajouter reste un résultat : c'est la
   réponse à « pourquoi je ne le trouve pas ». Il ne se clique pas, et son
   curseur le dit avant l'essai. */
.planif-recherche__resultat--inerte { cursor: default; }
.planif-recherche__resultat--inerte:hover { background: none; }

/* La situation d'un bénéficiaire face à cette tournée (fiche de tournée,
   23 août 2026). Trois états, trois teintes — chacune posée sur un texte
   qui dit déjà la même chose (DESIGN.md §1) :
   - `aucune` en vert : le cas simple, rien ne s'oppose à l'ajout ;
   - `ailleurs` en orange : servi par une autre tournée, l'ajout reste
     possible (midi/soir a besoin des deux) mais se voit avant d'être fait ;
   - `ici` en neutre : déjà là, il n'y a rien à décider. */
.badge--situation-aucune { background: var(--success-surface); color: var(--success-texte); }
.badge--situation-ailleurs { background: var(--warning-surface); color: var(--warning-texte); }
.badge--situation-ici { background: var(--bg-inset); color: var(--text-secondary); }

/* Le résultat est une colonne : sans cela la pastille s'étirerait sur
   toute la largeur et cesserait de se lire comme une pastille. */
.planif-recherche__resultat .badge { align-self: flex-start; }

/* La recherche prend toute la largeur du champ qui l'accueille — le
   sélecteur qu'elle remplace faisait de même. */
.recherche-arret .planif-recherche { width: 100%; flex: 1 1 auto; }

/* ⚠️ La recherche vit ici **dans** un `.champ`, dont la règle générique
   (`.champ input`, spécificité 0-2-0) écrase le retrait de gauche de
   `.planif-recherche__champ` (0-1-0) : la loupe se retrouvait posée sur
   les premières lettres tapées. On rétablit le retrait à spécificité
   égale plutôt qu'avec un `!important`. */
.recherche-arret .planif-recherche__champ {
  min-height: 40px;
  padding: var(--space-2) var(--space-4) var(--space-2)
    calc(var(--space-3) + 16px + var(--space-2));
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
}

.planif-recherche__vide {
  margin: 0;
  padding: var(--space-2) var(--space-3);
  font-size: 13px;
  color: var(--text-secondary);
}

.planif-alertes__titre {
  margin: 0 0 var(--space-3);
  font-size: 15px;
  font-variant-numeric: tabular-nums;
}

/* Le geste à faire, dans la ligne même : une alerte qui ne dit pas quoi
   faire laisse chercher, et c'est le matin qu'on n'a pas le temps. */
.planif-alerte__geste { color: var(--text-secondary); }

/* Liseré et non fond coloré (DESIGN.md §1) : l'alerte se signale, elle ne
   repeint pas l'écran. */
.planif-alerte {
  margin: 0 0 var(--space-2);
  padding-left: var(--space-3);
  border-left: 2px solid var(--border-strong);
}

.planif-alerte--danger { border-left-color: var(--danger); }
.planif-alerte--warning { border-left-color: var(--warning); }

/*
 * Une tournée = un dépliant. Le liséré gauche porte la couleur de la
 * tournée : c'est le même repère que le tracé sur la carte, et c'est lui
 * qui fait le lien entre les deux volets. La couleur ne dit jamais rien
 * que le nom écrit à côté ne dise déjà.
 */
.planif-tournee {
  /* Flottante comme les autres cartes v2 : l'élévation vient de l'ombre,
     plus d'une bordure 1px (maquette du 20 août 2026). Le liseré reste :
     il est une information — quelle tournée — pas un trait de cadre. */
  border-left: 3px solid var(--couleur-tournee, var(--border-strong));
  border-radius: var(--radius-card);
  background: var(--bg-surface);
  box-shadow: var(--shadow-card);
}

.planif-tournee--c1 { --couleur-tournee: var(--categorielle-1); }
.planif-tournee--c2 { --couleur-tournee: var(--categorielle-2); }
.planif-tournee--c3 { --couleur-tournee: var(--categorielle-3); }
.planif-tournee--c4 { --couleur-tournee: var(--categorielle-4); }
.planif-tournee--c5 { --couleur-tournee: var(--categorielle-5); }
.planif-tournee--c6 { --couleur-tournee: var(--categorielle-6); }
.planif-tournee--c7 { --couleur-tournee: var(--categorielle-7); }
.planif-tournee--c8 { --couleur-tournee: var(--categorielle-8); }

.planif-tournee__entete {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
  cursor: pointer;
  list-style: none;
  border-radius: var(--radius-card);
  transition: background var(--transition);
}

.planif-tournee__entete::-webkit-details-marker { display: none; }
.planif-tournee__entete:hover { background: var(--bg-raised); }

/* Ouvert, l'en-tête ne garde que ses coins hauts : le survol ne doit pas
   déborder en arrondi au milieu de la carte. */
.planif-tournee[open] > .planif-tournee__entete {
  border-radius: var(--radius-card) var(--radius-card) 0 0;
}

/* Pastille de rappel : redondante avec le liséré, et c'est voulu — elle
   survit à l'impression en noir et blanc, pas le liséré fin. */
.planif-tournee__pastille {
  width: 10px;
  height: 10px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--couleur-tournee, var(--text-tertiary));
}

.planif-tournee__titre { display: flex; flex-direction: column; min-width: 0; }
.planif-tournee__chiffres { margin-left: auto; text-align: right; white-space: nowrap; }

/* 🔴 `white-space: nowrap` sur une ligne qui grossit avec la journée
   (« 08:30 → 08:48 · 3 livraisons · 1 absent · 18 min ») pousse la page
   entière de côté à 390 px — trouvé à l'œil le 23 août 2026, comme le
   tableau de la conformité avant lui (PLAN §8.1). Sur téléphone, l'en-tête
   se replie : les chiffres passent sous le nom de la tournée et se lisent
   à gauche, plutôt que de faire défiler tout l'écran pour les atteindre.
   Le `nowrap` reste utile à l'intérieur des chiffres — c'est chaque
   segment qui ne doit pas se couper, pas la ligne qui doit rester une. */
@media (max-width: 640px) {
  .planif-tournee__entete { flex-wrap: wrap; }
  .planif-tournee__chiffres {
    margin-left: 0;
    text-align: left;
    white-space: normal;
    flex-basis: 100%;
  }
}

/* 🔴 Le même repli quand la colonne est étroite mais la fenêtre large
   (trouvé à l'œil le 1er septembre 2026). La liste vit dans une colonne
   de 26rem : l'en-tête d'une tournée y est aussi à l'étroit que sur un
   téléphone, alors qu'aucune requête de largeur d'écran ne se déclenche.
   Les chiffres, en `nowrap` et poussés à droite, se posaient alors
   par-dessus le nom du livreur — illisible sur tout écran de bureau. Le
   repli se décide donc sur la disposition, pas sur la largeur de la
   fenêtre : en vue « Liste » et en carte pleine, le panneau reprend
   toute la largeur et l'en-tête tient sur une ligne. */
.planif:not(.planif--liste):not(.planif--carte-pleine) .planif-tournee__entete {
  flex-wrap: wrap;
}
.planif:not(.planif--liste):not(.planif--carte-pleine) .planif-tournee__chiffres {
  margin-left: 0;
  text-align: left;
  white-space: normal;
  flex-basis: 100%;
}

/* Le compte des absents se lit sans être une alerte : personne n'a rien
   fait de mal, mais le chiffre ne doit pas se fondre dans le total. */
.planif-absents { color: var(--warning); }

.planif-tournee__vide { padding: 0 var(--space-3) var(--space-3); margin: 0; }

.planif-arrets {
  list-style: none;
  margin: 0;
  padding: 0 var(--space-3) var(--space-2);
}

.planif-arret {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-2);
  border-top: 1px solid var(--border);
  border-radius: var(--radius-control);
  cursor: pointer;
  transition: background var(--transition);
}

.planif-arret:hover { background: var(--bg-raised); }
.planif-arret--actif { background: var(--bg-raised); }

/* Même forme que la pastille de la carte : c'est elle qui fait le lien
   entre la ligne et le point, sans légende (DESIGN.md §6). */
.planif-arret__numero {
  flex-shrink: 0;
  width: 1.6rem;
  height: 1.6rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--bg-inset);
  color: var(--text-secondary);
  font-size: 11px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* Le tableau des passages porte huit colonnes. Seule la date est tenue
   d'une pièce : coupée en trois lignes (« mar / 25 / août »), elle
   devenait illisible dans un tableau qui ne parle que de dates. Les noms,
   eux, se replient — les élargir aussi poussait la colonne d'actions hors
   du cadre, et un menu qu'il faut aller chercher au défilement latéral
   n'existe pas. */
.tableau--passages td[data-label="Date"] > strong { white-space: nowrap; }

/* La pastille d'état du tableau des tournées, devenue un bouton (demande
   du porteur, 25 août 2026) : on clôt une journée là où on lit où elle en
   est. Le badge garde son apparence — c'est toujours un état ; seul le
   chevron dit qu'il s'ouvre. */
.btn-etat {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 2px;
  border: 0;
  border-radius: var(--radius-pill);
  background: transparent;
  font: inherit;
  cursor: pointer;
}
.btn-etat:hover { background: var(--bg-raised); }
.btn-etat .icone--menu { width: 12px; height: 12px; color: var(--text-tertiary); }

/* Le détail de ce qu'on dépose, sur le suivi de livraison (25 août 2026) :
   déplié d'emblée, contrairement à la planification — c'est l'information
   qu'on vient chercher quand une famille appelle pendant la tournée. */
.suivi-menus { margin-top: var(--space-3); }
.suivi-menus > .meta { margin-bottom: var(--space-1); }
.suivi-menus__jour {
  margin: 0 0 var(--space-1);
  padding: var(--space-2) var(--space-3);
  background: var(--bg-raised);
  border-radius: var(--radius-control);
  font-size: 13.5px;
}
.suivi-menus__jour:last-child { margin-bottom: 0; }

/* ---------- L'assistant de rattrapage (26 août 2026) ----------
   Une carte par tournée à créer. La case à cocher et le titre forment un
   seul libellé cliquable : on coche en visant la ligne, pas un carré de
   18 px. La raison de la date est écrite sous le titre — une date sans
   explication se discute, une date expliquée se vérifie. */

.rattrapage-proposition {
  padding: var(--space-4) var(--space-5);
  margin-bottom: var(--space-3);
}

.rattrapage-proposition__entete {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  cursor: pointer;
}
.rattrapage-proposition__entete input[type="checkbox"] {
  width: 18px;
  height: 18px;
  margin-top: 2px;
  flex: none;
}
.rattrapage-proposition__titre {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  min-width: 0;
}
.rattrapage-proposition__titre strong { font-size: 15px; font-weight: 600; }

.rattrapage-proposition__raison {
  margin: var(--space-2) 0 0 30px;
  font-size: 13px;
  color: var(--text-secondary);
}
.rattrapage-proposition__equipe { margin: var(--space-1) 0 0 30px; }

.rattrapage-proposition__alerte {
  margin: var(--space-2) 0 0 30px;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-control);
  background: var(--warning-surface);
  color: var(--warning-texte);
  font-size: 13px;
}

.rattrapage-proposition__date { margin: var(--space-3) 0 0 30px; max-width: 22rem; }
.rattrapage-proposition__date label { font-size: 13px; }

.rattrapage-proposition__gens { margin: var(--space-2) 0 0 30px; }
.rattrapage-proposition__gens summary {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  min-height: 24px;
  font-size: 12.5px;
  color: var(--text-secondary);
  cursor: pointer;
  list-style: none;
}
.rattrapage-proposition__gens summary::-webkit-details-marker { display: none; }
.rattrapage-proposition__gens summary::before { content: "▸"; font-size: 10px; }
.rattrapage-proposition__gens[open] summary::before { content: "▾"; }
.rattrapage-proposition__gens ul {
  margin: var(--space-2) 0 0;
  padding-left: var(--space-5);
  font-size: 13.5px;
}
.rattrapage-proposition__gens li { margin-bottom: var(--space-1); }
.rattrapage-proposition__gens .meta { margin-left: var(--space-2); }

/* ---------- Déclarer une fermeture (option C, 25 août 2026) ----------
   Trois traitements posés côte à côte, chacun avec sa conséquence écrite
   sous son libellé : un choix par défaut invisible déciderait à la place
   de la structure si quelqu'un mange le 1er mai. La carte retenue porte
   ce qu'elle demande de plus — le passage qui emporte, l'avertissement. */

.fermeture-impact {
  margin: var(--space-4) 0;
  padding: var(--space-3) var(--space-4);
  background: var(--bg-raised);
  border-radius: var(--radius-control);
}
.fermeture-impact p { margin: 0 0 var(--space-1); }
.fermeture-impact p:last-child { margin-bottom: 0; }
.fermeture-impact--attente { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
.fermeture-impact--attente p { flex: 1; min-width: 16rem; }

.fermeture-options { border: 0; margin: 0; padding: 0; }
.fermeture-options legend {
  padding: 0;
  font-size: 13px;
  font-weight: 500;
  margin-bottom: var(--space-2);
}

.fermeture-option {
  display: flex;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-card);
  margin-bottom: var(--space-2);
  cursor: pointer;
}
.fermeture-option:hover { background: var(--bg-raised); }
/* La carte choisie se distingue par son cadre, jamais par la couleur
   seule : le bouton radio reste la source de vérité (DESIGN.md §1). */
.fermeture-option:has(input:checked) {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent);
}
.fermeture-option:has(input:focus-visible) { outline: 3px solid var(--focus-ring); }
.fermeture-option input[type="radio"] { margin-top: 3px; flex: none; }
.fermeture-option__corps { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.fermeture-option__corps strong { font-size: 14px; font-weight: 600; }
/* Le sélecteur de passage porte les styles des champs du produit —
   posé nu, il gardait l'apparence par défaut du navigateur au milieu de
   champs arrondis (signalé par le porteur, 26 août 2026). `.champ select`
   ne l'attrapait pas : il ne vit pas dans un `.champ` mais dans la carte
   de l'option, où il doit rester collé à sa description. */
.fermeture-option__champ { margin-top: var(--space-2); display: block; }
.fermeture-option__champ select {
  width: 100%;
  min-height: 40px;
  padding: var(--space-2) var(--space-3);
  font: inherit;
  font-size: 14px;
  color: var(--text-primary);
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-control);
  /* Une seule ligne, quitte à couper : « La veille — jeudi 27 août
     (T2026-000413, T2026-000415) » ne doit pas replier la carte. */
  text-overflow: ellipsis;
}
.fermeture-option__champ select:focus-visible {
  outline: 3px solid var(--focus-ring);
  border-color: var(--accent);
}
.fermeture-option__alerte {
  margin-top: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-control);
  background: var(--warning-surface);
  color: var(--warning-texte);
  font-size: 13px;
}

.planif-arret__corps { min-width: 0; flex: 1; }
.planif-arret__repas { white-space: nowrap; }

/* Le détail des menus par personne (25 août 2026) : la ligne se déplie sur
   place. Le résumé se lit comme une commande discrète, pas comme un lien —
   et le contenu reste dans la colonne, jamais en panneau par-dessus. */
.planif-menus { margin-top: var(--space-1); }
.planif-menus summary {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-size: 12px;
  color: var(--text-secondary);
  cursor: pointer;
  list-style: none;
  min-height: 24px;
}
.planif-menus summary::-webkit-details-marker { display: none; }
.planif-menus summary::before { content: "▸"; font-size: 10px; }
.planif-menus[open] summary::before { content: "▾"; }
.planif-menus summary:hover { color: var(--text-primary); }
.planif-menus__jour {
  margin: var(--space-1) 0 0;
  padding: var(--space-2) var(--space-3);
  background: var(--bg-raised);
  border-radius: var(--radius-control);
  font-size: 12.5px;
}
.planif-menus__jour strong { font-weight: 600; }

/* Absent : grisé, jamais retiré — l'arrêt reste une information, quelqu'un
   attend un repas (DESIGN.md §6, §13). Le libellé « Rien à déposer » écrit
   à côté porte le sens ; l'opacité ne fait que le rappeler. */
.planif-arret--absent .planif-arret__corps,
.planif-arret--absent .planif-arret__numero { opacity: 0.55; }

.planif-tournee__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  padding: var(--space-2) var(--space-3) var(--space-3);
  border-top: 1px solid var(--border);
}

/* Réutilisé dans `.planif-detail__actions` (empilement vertical, deux
   groupes séparés par un espace plus large que celui qui tient les
   boutons d'un même groupe ensemble) et potentiellement ailleurs — la
   règle est générique, seul le conteneur parent choisit l'axe. */
.planif-tournee__actions-groupe,
.planif-detail__actions-groupe {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
}

/* Heure estimée de passage. Chasse fixe pour que la colonne s'aligne d'un
   arrêt à l'autre — une liste d'heures qui zigzague se lit mal. */
.planif-arret__heure {
  font-variant-numeric: tabular-nums;
  color: var(--text-secondary);
  margin-right: var(--space-2);
}

/* ---------- Panneau de détail de la planification ---------- */

/*
 * Sous la carte : la tournée sélectionnée, puis l'arrêt sélectionné. Une
 * seule zone pour les deux — l'un est le contexte de l'autre, et deux
 * panneaux concurrents obligeraient à choisir lequel regarder.
 */
/* Troisième colonne de la grille `.planif` en vue Carte, rangée sous la
   liste en vue Liste — `gap` porte l'espacement dans les deux cas, pas
   une marge locale qui le doublerait en grille. Position `sticky` comme
   la carte : sur un écran de tournées, on la veut sous les yeux pendant
   qu'on défile la liste, pas perdue plus bas. */
.planif-detail {
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  background: var(--bg-surface);
  position: sticky;
  top: var(--space-4);
  max-height: 40rem;
  overflow-y: auto;
}

.planif--liste .planif-detail { position: static; max-height: none; overflow: visible; }

/* De la place pour le bouton de fermeture (vue Carte seulement) : sans
   cette marge, le titre de la fiche passerait dessous. */
.planif:not(.planif--liste) .planif-detail { padding-right: calc(var(--space-4) + 2.25rem); }

.planif-detail__vide { margin: 0; }

/* Une seule colonne : le panneau est étroit (22rem), les trois blocs
   (détails, contrainte, actions) s'y empilent plutôt que de se disputer
   la largeur — `auto-fit` n'a plus de deuxième colonne à offrir ici. En
   vue Liste, le panneau retrouve sa pleine largeur et peut en reprendre
   trois. */
.planif-detail__tournee,
.planif-detail__arret {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-4);
}

.planif--liste .planif-detail__tournee,
.planif--liste .planif-detail__arret {
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--space-6);
}

.planif-detail h3 {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-secondary);
  margin: 0 0 var(--space-2);
}

.planif-detail__colonne p { margin: 0 0 var(--space-2); }

/* Étiquette au-dessus, valeur en dessous : sur trois colonnes étroites,
   une liste de définitions en vis-à-vis casse dès que le libellé passe à
   la ligne. */
.planif-detail__liste { margin: 0; }
.planif-detail__liste dt {
  font-size: 12px;
  color: var(--text-secondary);
  margin-top: var(--space-2);
}
.planif-detail__liste dt:first-child { margin-top: 0; }
.planif-detail__liste dd { margin: 0; }

.planif-detail__actions {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-4);
}

.planif-detail__actions-groupe {
  flex-direction: column;
  align-items: flex-start;
}

/* Le sélecteur de tournée cible et son bouton vont ensemble : les
   séparer sur deux lignes ferait passer le sélecteur pour un champ de
   filtre plutôt que pour la moitié d'un geste. */
.planif-detail__deplacer {
  display: flex;
  gap: var(--space-1);
}

/* Contrainte réellement vérifiée. Liseré et non fond coloré (DESIGN.md §1),
   et `--danger` seulement quand elle est rompue : la liaison froide est une
   conformité sanitaire, pas un indicateur de performance. */
.planif-detail__contrainte {
  padding-left: var(--space-3);
  border-left: 2px solid var(--success);
}

.planif-detail__contrainte--rompue { border-left-color: var(--danger); }

/* Le créneau imposé et le point de vigilance sont des consignes que
   personne ne fait respecter automatiquement : ils se voient, sinon ils
   ne servent à rien. */
.planif-detail__creneau,
.planif-detail__vigilance {
  padding-left: var(--space-2);
  border-left: 2px solid var(--warning);
}

/* Réordonnancement sur l'écran de planification. */
.planif-arret__poignees { display: flex; gap: var(--space-1); flex-shrink: 0; }

/* Monter/descendre : boutons ronds à chevron (maquette du 20 août 2026),
   36px — la taille des boutons-icônes du produit, pas moins (personas). */
.poignee {
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: var(--radius-pill);
  background: var(--bg-surface);
  box-shadow: inset 0 0 0 1px var(--border-strong);
  color: var(--text-secondary);
  cursor: pointer;
  transition: background var(--transition), color var(--transition);
}

.poignee:hover { background: var(--bg-raised); color: var(--text-primary); }

.poignee .icone { width: 14px; height: 14px; }

.carte-tournee__poignees .btn {
  min-width: 44px;
  min-height: 44px;
  padding: var(--space-2);
}

.planif-arret--saisi { opacity: 0.4; }

.planif-ordre {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-top: 1px solid var(--border);
}

/* L'heure estimée devient fausse dès qu'un arrêt bouge. La laisser
   affichée serait pire que de la retirer : elle aurait l'air à jour. */
.planif-arrets--ordre-modifie .planif-arret__heure { display: none; }

/* ---------- Offre et tarifs (UX-OFFRE.md §2) ---------- */

/* Un bloc de champs qui va ensemble : la part repas, la part livraison.
   La légende porte le sens, la bordure ne fait que la rattacher — pas de
   cadre lourd (DESIGN.md §5). */
.bloc-champs {
  border: 0;
  border-top: 1px solid var(--border);
  padding: var(--space-5) 0 0;
  margin: var(--space-5) 0 0;
}

.bloc-champs legend {
  padding: 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-secondary);
}

.grille-formules {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr));
  gap: var(--space-4);
}

/* Vue compacte du catalogue (catalogue_offre.js) : une ligne par formule,
   sur le modèle du tableau des options juste en dessous — plutôt qu'une
   grille de cartes réduites, qui resterait haute de plusieurs lignes.
   Le nom, la composition (tronquée) et le prix restent : c'est ce qu'on
   compare, la décomposition se rouvre au besoin. */
.grille-formules--compacte {
  display: block;
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  overflow: hidden;
  background: var(--bg-surface);
}

.grille-formules--compacte .carte-formule {
  display: flex;
  align-items: baseline;
  gap: var(--space-4);
  padding: var(--space-2) var(--space-4);
  border: 0;
  border-radius: 0;
  border-bottom: 1px solid var(--border);
  background: transparent;
  transition: background var(--transition);
}

.grille-formules--compacte .carte-formule:last-child { border-bottom: 0; }
.grille-formules--compacte .carte-formule:hover { background: var(--bg-raised); }

.grille-formules--compacte .carte-formule__entete {
  flex: 1;
  min-width: 0;
  align-items: baseline;
}

.grille-formules--compacte .carte-formule__entete > div:first-child {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
}

.grille-formules--compacte .carte-formule__nom {
  font-size: 13px;
  margin: 0;
  white-space: nowrap;
  flex-shrink: 0;
}

/* La composition se tronque plutôt que de repousser le prix : c'est le nom
   et le prix qu'on compare ligne à ligne, la composition n'est là qu'en
   rappel. */
.grille-formules--compacte .carte-formule__entete .meta {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.grille-formules--compacte .carte-formule__prix {
  font-size: 13px;
  flex-shrink: 0;
}

.grille-formules--compacte .carte-formule__unite {
  /* « € / jour » répété sur chaque ligne d'une liste n'ajoute rien —
     à la différence de la carte isolée, où il évite l'ambiguïté. */
  display: none;
}

.grille-formules--compacte .carte-formule__calcul { display: none; }

.grille-formules--compacte .barre-actions {
  margin: 0;
  padding: 0;
  border-top: 0;
  flex-shrink: 0;
}

.grille-formules--compacte .barre-actions .btn {
  padding: 2px var(--space-2);
  font-size: 12px;
}

.carte-formule__entete {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
}

.carte-formule__nom { font-size: 16px; margin-bottom: var(--space-1); }

.carte-formule__prix {
  font-size: 20px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.carte-formule__unite { font-size: 12px; font-weight: 400; color: var(--text-secondary); }

/* Une formule retirée de la vente reste lisible : les contrats en cours la
   référencent, et l'effacer visuellement ferait croire à une suppression
   (DESIGN.md §13). */
.carte-formule--archivee { opacity: 0.62; }

.carte-formule__duo {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
  font-size: 13px;
}

/* La marge est le chiffre de la structure, pas celui de la famille : même
   traitement détaché que le duo. Une marge négative se colore, parce que
   vendre à perte est une information et non une décoration (DESIGN.md §1). */
.carte-formule__marge {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
  font-size: 13px;
}

.carte-formule__marge--perte strong { color: var(--danger); }

/* Sa propre ligne : au fil du texte, la mise en garde se lisait comme la
   suite de « … % du prix de vente » et n'était plus une phrase. */
.carte-formule__marge-alerte { display: block; margin-top: var(--space-2); }

/* La ligne de calcul dépliée — repas + livraison, crédit d'impôt, coût
   réel. C'est ce que la gestionnaire dit au téléphone. */
.calcul { margin-top: var(--space-4); }

.calcul__ligne {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--border);
}

.calcul__ligne:last-child { border-bottom: 0; }

.calcul__ligne dt { font-size: 13px; }
.calcul__ligne dt .meta { display: block; }

.calcul__ligne dd {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  font-size: 13px;
}

/* Le crédit d'impôt est de l'argent qui revient : c'est une information,
   donc une couleur (DESIGN.md §1) — jamais décorative. */
.calcul__ligne--credit dd { color: var(--success); }

.calcul__ligne--total {
  border-top: 1px solid var(--border-strong);
  border-bottom: 0;
  margin-top: var(--space-1);
  padding-top: var(--space-3);
}

.calcul__ligne--total dt, .calcul__ligne--total dd { font-weight: 600; font-size: 14px; }

/* ---------- Composeur de contrat (UX-OFFRE.md §3) ---------- */

/* Une case isolée avec son libellé à droite — le duo, une bascule et non
   une liste. `.cases-en-ligne` suppose plusieurs cases et les répartit. */
.case-seule {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-weight: 400;
}

.case-seule input[type="checkbox"] { width: 16px; height: 16px; flex-shrink: 0; }

/* La répartition des passages, dite en français sous les cases : c'est la
   vérification qui attrape une case cochée de travers. */
.apercu__repartition {
  list-style: none;
  padding: 0;
  margin: var(--space-2) 0 var(--space-4);
  font-size: 12px;
  color: var(--text-secondary);
}

.apercu__repartition li { padding: var(--space-1) 0; }

/* Une suggestion n'est pas une alerte : bordure d'accent, jamais de rouge
   — rien ne va mal, on propose (DESIGN.md §1). */
.apercu__suggestion {
  margin-top: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-left: 2px solid var(--accent);
  font-size: 13px;
}

/* ---------- Calendrier des exceptions (UX-OFFRE.md §5) ---------- */

.calendrier__entete {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
  margin-bottom: var(--space-4);
}

.calendrier { width: 100%; border-collapse: collapse; table-layout: fixed; }

.calendrier th {
  padding: var(--space-2);
  font-size: 12px;
  font-weight: 500;
  color: var(--text-secondary);
  text-align: left;
  border-bottom: 1px solid var(--border);
}

.calendrier td {
  height: 5.5rem;
  padding: var(--space-2);
  border: 1px solid var(--border);
  vertical-align: top;
}

.calendrier__vide { background: var(--bg-raised); border-color: transparent; }

.calendrier__numero {
  display: block;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: var(--text-secondary);
}

.calendrier__jour--aujourdhui .calendrier__numero { font-weight: 600; color: var(--text-primary); }

/* Un jour servi se signale par une barre à gauche, pas par un aplat :
   l'état est une information, pas une décoration (DESIGN.md §1). */
.calendrier__jour--mange { border-left: 2px solid var(--text-primary); }
.calendrier__jour--ajoute { border-left-color: var(--accent); }
.calendrier__jour--absent { border-left: 2px solid var(--warning); }

.calendrier__etat {
  display: block;
  margin-top: var(--space-1);
  font-size: 11px;
  line-height: 1.3;
}

/* L'action reste discrète tant qu'on ne survole pas la case — mais jamais
   invisible : au clavier elle doit se voir au focus. */
.calendrier__action {
  margin-top: var(--space-1);
  padding: 0;
  border: 0;
  background: none;
  color: var(--text-secondary);
  font-size: 11px;
  cursor: pointer;
  text-decoration: underline;
}

.calendrier__action:hover, .calendrier__action:focus-visible { color: var(--text-primary); }

/* Le menu retenu ce jour-là — l'assiette, pas seulement l'état. */
.calendrier__menu {
  display: block;
  margin-top: 2px;
  font-size: 11px;
  color: var(--text-secondary);
}

/* Trente liens « + repas » alignés faisaient de la grille un mur de
   liens : l'action ne se montre qu'au survol de sa case, ou au clavier
   par `:focus-within`. Le calendrier redevient un calendrier, et le geste
   reste à un clic de distance. */
@media (hover: hover) {
  .calendrier__jour .calendrier__action { opacity: 0; }
  .calendrier__jour:hover .calendrier__action,
  .calendrier__jour:focus-within .calendrier__action { opacity: 1; }
}

/* Une date révolue ne se planifie plus : elle s'estompe, mais reste
   parfaitement lisible — c'est là qu'on lit ce qui a eu lieu
   (DESIGN.md §13, un passage servi s'estompe sans disparaître). */
.calendrier__jour--passe { background: var(--bg-raised); }
.calendrier__jour--passe .calendrier__numero { color: var(--text-tertiary); }

@media (max-width: 720px) {
  .calendrier td { height: auto; padding: var(--space-1); }
  .calendrier__etat { font-size: 10px; }
  /* Sans survol possible, l'action reste visible. */
  .calendrier__jour .calendrier__action { opacity: 1; }
}

/* Bloc « Réglages avancés » : replié par défaut, la flèche seule dit qu'il
   s'ouvre — pas de bouton, ce n'est pas une action. */
details.parcours__etape > summary {
  cursor: pointer;
  list-style-position: outside;
}

details.parcours__etape > summary::marker { color: var(--text-tertiary); }

/* La tournée assignée, sur le composeur de contrat : un fait qu'on lit,
   pas un champ qu'on remplit — c'est la tournée qui en décide. */
.tournee-assignee {
  padding: var(--space-2) var(--space-3);
  border-left: 2px solid var(--border-strong);
  font-size: 13px;
}

.tournee-assignee + .tournee-assignee { margin-top: var(--space-2); }
.tournee-assignee .meta { display: block; }

/* Sans tournée, le contrat marche quand même (un passage par repas) : ce
   n'est pas une erreur, seulement un rattachement qui reste à faire. */
.tournee-assignee--absente { border-left-color: var(--warning); }

/* ---------- Pastille d'état — calendrier des exceptions (§5, août 2026) ---------- */

/* Ce qui s'est réellement passé sur le terrain (Commande.statut), doublé
   du libellé texte (jour.etat.libelle) — jamais la couleur seule
   (DESIGN.md §1). Neutre pour « prévu » : ce n'est pas encore un fait. */
.pastille-etat {
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  margin-right: 4px;
  background: var(--text-tertiary);
  vertical-align: middle;
}

.pastille-etat--livree { background: var(--success); }
.pastille-etat--absent { background: var(--warning); }
.pastille-etat--refuse { background: var(--danger); }
.pastille-etat--prevu { background: var(--text-tertiary); }
/* Ni un fait du terrain ni une annulation : une commande qui attend encore
   un choix (téléphone, coupon non scanné) — la couleur d'action, pas celle
   d'un problème (écran Commandes, UX-OFFRE.md §9). */
.pastille-etat--a-choisir { background: var(--info); }
/* Un règlement parti mais pas encore confirmé au relevé : le bleu de
   l'information, jamais l'orange de ce qui approche — un dépôt n'est ni
   une anomalie ni une échéance, c'est un état d'attente normal. */
.pastille-etat--depose { background: var(--info); }
/* Un jour passé dont personne n'a dit ce qu'il est devenu : ni livré, ni
   absent, ni refusé. Ce n'est pas un incident constaté — c'est un trou dans
   ce qu'on sait, et il ne se comblera pas tout seul. D'où l'avertissement,
   entre l'information (bleu) et l'erreur (rouge). */
.pastille-etat--a-trancher { background: var(--warning); }
/* Ne s'est pas produit et ne se produira plus — même famille visuelle que
   « prévu » (rien à voir sur le terrain), pas celle d'un incident. */
.pastille-etat--annulee { background: var(--text-tertiary); }

/* Une formule de menu, sur le calendrier (`apps.menus.calendrier`). Même
   gabarit que `.pastille-etat` — même geste visuel, sens différent : ici la
   teinte ne dit pas un état, elle distingue une formule d'une autre parmi
   celles du même jour. Palette catégorielle partagée avec les tournées
   (tokens.css) : jamais seule, le nom de la formule reste toujours écrit à
   côté. */
.pastille-formule {
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  margin-right: 4px;
  background: var(--text-tertiary);
  vertical-align: middle;
  flex-shrink: 0;
}

.pastille-formule--c1 { background: var(--categorielle-1); }
.pastille-formule--c2 { background: var(--categorielle-2); }
.pastille-formule--c3 { background: var(--categorielle-3); }
.pastille-formule--c4 { background: var(--categorielle-4); }
.pastille-formule--c5 { background: var(--categorielle-5); }
.pastille-formule--c6 { background: var(--categorielle-6); }
.pastille-formule--c7 { background: var(--categorielle-7); }
.pastille-formule--c8 { background: var(--categorielle-8); }

/* Une formule, sur la page du jour (`menus/jour.html`) : un dépliant, pas
   une carte à elle seule — même patron que `.planif-tournee`, résumé en
   une ligne, plats dans le corps une fois ouvert. Le résumé reste du texte
   pur (`generic details > summary` de plus haut fixe déjà le curseur et
   masque le triangle natif) : tout élément interactif y resterait dans la
   zone de bascule du dépliant, `stopPropagation()` n'y changeant rien pour
   un enfant de `<summary>`. */
.menu-repli__resume {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  padding: var(--space-3);
}

.menu-repli__resume:hover { background: var(--bg-raised); }

.menu-repli__titre { font-weight: 500; }

.menu-repli__corps {
  padding: 0 var(--space-3) var(--space-3);
  border-top: 1px solid var(--border);
}

/* Compacte : la colonne « Plat » porte le sens, le type et la suppression
   n'ont besoin que de leur juste largeur. */
.tableau-composants input, .tableau-composants select {
  width: 100%;
  font: inherit;
  font-size: 14px;
  color: var(--text-primary);
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-control);
  padding: var(--space-1) var(--space-2);
}

.tableau-composants input:focus-visible, .tableau-composants select:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

/* Quatre colonnes (type, plat, contient, retrait) ne tiennent pas sous
   ~720 px sans écraser les libellés jusqu'à « Ve… » (constaté à 390 px le
   28 août 2026). Le tableau se **replie alors en blocs** : un plat par
   carte, ses trois champs l'un sous l'autre. Le défilement horizontal du
   `.tableau-defilant` reste le filet au-delà (déclaré une seule fois,
   plus haut dans ce fichier).

   Les étiquettes existent déjà pour les lecteurs d'écran — il suffit de
   les rendre visibles. Pas de `::before` ni d'attribut `data-` à tenir en
   double : une seule source pour le nom d'un champ. */
.tableau-composants { min-width: 34rem; }

@media (max-width: 720px) {
  .tableau-composants { min-width: 0; }
  .tableau-composants thead { display: none; }
  .tableau-composants,
  .tableau-composants tbody,
  .tableau-composants tr,
  .tableau-composants td {
    display: block;
    width: 100%;
  }
  .tableau-composants tr {
    padding: var(--space-3) 0;
    border-bottom: 1px solid var(--border);
    position: relative;
  }
  .tableau-composants td { border: 0; padding: var(--space-1) 0; }
  .tableau-composants .visuellement-cache {
    position: static;
    width: auto;
    height: auto;
    overflow: visible;
    clip: auto;
    clip-path: none;
    white-space: normal;
    display: block;
    margin-bottom: 2px;
    font-size: 12.5px;
    color: var(--text-secondary);
  }
  /* Le retrait revient en haut à droite de la carte : en bas de pile, il
     se lisait comme un geste sur le plat **suivant**. */
  .tableau-composants td.montant {
    position: absolute;
    top: var(--space-2);
    right: 0;
    width: auto;
  }
}

/* Sept colonnes (jour, formule, régime, composants, contient, nombre, et
   la case à cocher) ne se compriment pas : sans largeur minimale, le
   navigateur préfère réduire « Composants » à une lettre par ligne plutôt
   que de laisser défiler (constaté à 390 px le 28 août 2026). */
.tableau-menus { min-width: 60rem; }

/* Un lien qui n'a pas l'air d'un lien : il enveloppe une pastille ou un
   texte qui portent déjà leur propre forme (le régime d'un menu). Le
   soulignement au survol dit qu'il agit, sans repeindre la pastille. */
.lien-nu { color: inherit; text-decoration: none; }
.lien-nu:hover { text-decoration: underline; }
.lien-nu:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: var(--radius-control);
}

/* La cellule « Contient » de la liste des menus : les tags, puis le lien
   qui ouvre le panneau de correction. En colonne plutôt qu'en ligne — le
   lien posé à la suite des puces se lisait comme un tag de plus. */
.cellule-tags {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1);
}

.cellule-tags .lien-action { flex-basis: 100%; }

/* ---------- Saisie en puces (`champ_tags.js`) ----------
   Ce que contient un plat : « poisson », « gluten ». La boîte imite un
   champ ordinaire — même bordure, même rayon — et les puces vivent dedans,
   devant la zone de frappe. Sans JavaScript, le champ reste un texte à
   virgules et cette boîte n'existe pas : rien ici ne conditionne
   l'enregistrement. */

.champ-tags__boite {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1);
  width: 100%;
  min-height: 34px;
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-control);
  padding: 3px var(--space-2);
  cursor: text;
}

.champ-tags__boite:focus-within {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

/* La saisie n'a plus ni bordure ni fond : c'est la boîte qui les porte.
   Elle garde une largeur minimale pour rester cliquable quand les puces
   occupent presque toute la ligne. */
.champ-tags__boite .champ-tags__saisie,
.tableau-composants .champ-tags__boite .champ-tags__saisie {
  flex: 1 1 6rem;
  min-width: 6rem;
  width: auto;
  border: 0;
  background: transparent;
  padding: 2px 0;
  outline: none;
}

/* Neutre et non colorée : ce que contient un plat est un fait, pas un
   état — la couleur reste réservée à ce qui signifie (DESIGN.md §1).
   C'est l'allergie *déclarée d'une personne* qui se colore, pas
   l'ingrédient d'une recette. */
.champ-tags__puce {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 1px var(--space-1) 1px var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--bg-inset);
  color: var(--text-primary);
  font-size: 13px;
  white-space: nowrap;
}

.champ-tags__retirer {
  border: 0;
  background: none;
  color: var(--text-secondary);
  font-size: 15px;
  line-height: 1;
  padding: 0 2px;
  cursor: pointer;
}

.champ-tags__retirer:hover { color: var(--text-primary); }

.champ-tags__retirer:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 1px;
  border-radius: var(--radius-pill);
}

.calendrier__legende {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin: 0 0 var(--space-4);
}

.calendrier__legende span { white-space: nowrap; }

/* Vue liste des repas (onglet « Repas ») — une date révolue s'estompe sans
   cesser d'être lisible, comme sa case au calendrier. */
.liste-repas--passe { color: var(--text-secondary); }

/* ---------- Collecte des choix — « le chemin » (PRD §6.2, 16 août 2026) ---------- */

/* Cinq étapes dans une seule carte, séparées par un filet, plutôt que cinq
   tuiles indépendantes (traitement retenu par le porteur sur maquette). La
   différence n'est pas décorative : des tuiles disent « voici cinq
   mesures », une carte continue dit « voici un parcours, dans cet ordre ».
   L'écran de collecte est le second — chaque étape dépend de la précédente,
   et c'est précisément ce qu'on vient y lire. */
.chemin-collecte {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 0;
  padding: var(--space-5) var(--space-2);
  /* 32px entre grands groupes (DESIGN.md §4), et non 16 : l'entonnoir, la
     règle d'envoi et le tableau sont trois choses distinctes, pas trois
     blocs d'une même liste. Mesuré à 16px partout avant correction — c'est
     ce que le porteur voyait comme « un manque d'espace entre les cartes ». */
  margin-bottom: var(--space-8);
}

.chemin-collecte__etape {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: 0 var(--space-5);
}

/* Un filet, pas une bordure de carte : il sépare deux moments du même
   parcours, il ne découpe pas la surface en cinq boîtes. */
.chemin-collecte__etape + .chemin-collecte__etape {
  border-left: 1px solid var(--border);
}

.chemin-collecte__rang {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-secondary);
}

.chemin-collecte__num {
  display: grid;
  place-items: center;
  flex: none;
  width: 20px;
  height: 20px;
  border-radius: var(--radius-pill);
  background: var(--bg-inset);
  color: var(--text-secondary);
  font-size: 11px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
}

/* Une étape entièrement faite se marque sur son numéro, jamais sur son
   chiffre : le chiffre est la mesure, il doit garder la même valeur d'un
   coup d'œil à l'autre. */
.chemin-collecte__etape--faite .chemin-collecte__num {
  background: var(--success-surface);
  color: var(--success-texte);
}

.chemin-collecte__chiffre {
  font-size: 28px;
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
}

/* Une date n'est pas un nombre : elle ne s'aligne pas en colonne et le
   chiffre tabulaire l'espace pour rien. */
.chemin-collecte__chiffre--date { font-variant-numeric: normal; }

.chemin-collecte__chiffre small {
  font-size: 15px;
  font-weight: 400;
  letter-spacing: 0;
  color: var(--text-tertiary);
}

.chemin-collecte__detail {
  font-size: 12px;
  line-height: 1.45;
  color: var(--text-tertiary);
}

.chemin-collecte__action {
  margin-top: auto;
  padding-top: var(--space-2);
  align-self: flex-start;
  font-size: 13px;
  font-weight: 500;
  color: var(--text-primary);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--border-strong);
}

.chemin-collecte__action:hover { text-decoration-color: var(--text-primary); }

/* Le gel énoncé en français sous l'entonnoir. Surface d'information, pas
   d'alerte : une date de gel n'est pas un incident, c'est la règle du jeu —
   le rouge est réservé aux anomalies (DESIGN.md §2). */
.bandeau-gel {
  background: var(--info-surface);
  color: var(--info-texte);
  border-radius: var(--radius-card);
  padding: var(--space-4) var(--space-5);
  margin-bottom: var(--space-8);
}

.bandeau-gel p { margin: 0; font-size: 14px; line-height: 1.5; }

/* Les dates limites, une ligne par semaine sous la règle. Le bandeau est
   déjà une surface sémantique (info) : les lignes restent dans sa teinte,
   seuls les badges d'urgence portent la leur. */
.dates-limites {
  list-style: none;
  margin: var(--space-3) 0 0;
  padding: 0;
  display: grid;
  gap: var(--space-1);
}
.dates-limites__semaine {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2) var(--space-3);
  font-size: 14px;
}
.dates-limites__quand { font-weight: 500; min-width: 12.5rem; }
.dates-limites__derogation { font-size: 13px; opacity: 0.8; }
/* Le badge du bandeau se pose sur la surface info, pas sur une carte : le
   fond en creux (`--bg-inset`) y disparaîtrait, celui des sémantiques non. */
.dates-limites .badge { font-size: 12px; padding: 1px var(--space-2); }

/* Trois repères de temps (mois précédent, mois en cours, mois suivant) : ils
   tiennent sur une ligne au bureau et se replient en dessous de 700px, au
   lieu de pousser la page entière de côté. Mesuré : sans le repli, l'en-tête
   sortait à 558px pour une fenêtre de 390. */
/* Un réglage, une commande (DESIGN.md §14 Q7) : les flèches appartiennent à
   la valeur qu'elles font varier — un seul objet, posé comme les autres
   surfaces de la v2 — et non trois boutons alignés de rangs différents.
   Effet secondaire mesuré : la rangée passe de 558px à 260px, donc plus de
   repli en trois lignes sur un téléphone. */
.navigation-mois {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  justify-content: flex-end;
}

.navigation-mois__controle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 3px;
  background: var(--bg-surface);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-card);
}

.navigation-mois__valeur {
  padding: 0 var(--space-2);
  font-size: 14px;
  font-weight: 500;
  white-space: nowrap;
}

.navigation-mois__fleche {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-pill);
  color: var(--text-secondary);
  transition: background var(--transition), color var(--transition);
}

.navigation-mois__fleche:hover {
  background: var(--bg-raised);
  color: var(--text-primary);
  text-decoration: none;
}

/* Le sprite n'a qu'un chevron, orienté vers le bas : les deux autres
   directions se déduisent par rotation plutôt que par deux dessins de plus
   à garder d'accord. */
.navigation-mois__chevron--gauche { transform: rotate(90deg); }
.navigation-mois__chevron--droite { transform: rotate(-90deg); }

/* Le tableau de la collecte porte une colonne par semaine : il déborde sur
   un écran étroit. Il défile alors **dans sa carte**, jamais en poussant la
   page entière de côté. `min-width: 0` n'est pas superflu — sans lui, une
   carte posée dans une grille refuse de se rétrécir sous la largeur de son
   contenu, et le `overflow-x` ne clippe plus rien. */
.tableau-collecte__semaine { text-align: center; white-space: nowrap; }

/* L'état tient sur deux lignes franches : le libellé, puis sa précision.
   Laissé au fil du texte, « Réponse incomplète — 1 jour sans réponse » se
   cassait en trois là où la colonne se resserrait, et les rangées
   devenaient inégales — le libellé, qui est ce qu'on vient lire, se
   retrouvait à cheval sur deux lignes.

   ⚠️ Les deux lignes viennent des enfants, jamais d'un `display: flex` sur
   la cellule : une cellule qui cesse d'être `table-cell` sort du calcul de
   bordures du tableau, et les filets de séparation s'arrêtent net de part
   et d'autre — la colonne se met alors à ressembler à un couloir encadré
   (constaté à l'écran, 16 août 2026). */
.tableau-collecte__libelle {
  display: block;
  white-space: nowrap;
}

.tableau-collecte__etat .meta { display: block; }

/* Une date ne se coupe pas en deux : « Imprimé le 16 / Aoû » se lit deux
   fois avant de se comprendre. Le tableau défile dans sa carte, la place
   ne manque donc jamais vraiment. */
.tableau-collecte td:nth-child(4) { white-space: nowrap; }

/* Le canal a de quoi tenir en deux lignes. Laissé à 101px, « Papier déposé
   par le livreur » s'étalait sur trois et devenait le plus haut de la
   rangée : les lignes du tableau perdaient leur régularité pour la colonne
   qu'on lit le moins. */
.tableau-collecte th:nth-child(3),
.tableau-collecte td:nth-child(3) { min-width: 8.5rem; }

/* Le lien famille et sa copie sur la même ligne, la copie en geste de
   troisième rang — un lien souligné, pas une pilule. Même traitement que
   les actions de l'entonnoir : ce qui se lit dans une phrase se clique
   dans la phrase. */
.tableau-collecte__lien { display: block; }

.lien-action {
  border: 0;
  padding: 0;
  background: none;
  font: inherit;
  color: var(--text-primary);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--border-strong);
}

.lien-action:hover { text-decoration-color: var(--text-primary); }
.lien-action:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

/* La pastille d'une semaine se lit sans texte à côté (la colonne est trop
   étroite) : son sens est donc porté par l'infobulle **et** par un libellé
   masqué visuellement, lu par les lecteurs d'écran. Jamais la couleur
   seule (DESIGN.md §8). */
.tableau-collecte__semaine .pastille-etat {
  width: 9px;
  height: 9px;
  margin: 0;
}

@media (max-width: 900px) {
  .chemin-collecte { grid-template-columns: 1fr; gap: var(--space-4); }
  .chemin-collecte__etape + .chemin-collecte__etape {
    border-left: 0;
    border-top: 1px solid var(--border);
    padding-top: var(--space-4);
  }
}

/* ---------- États de la collecte (`apps.commandes.collecte.Etat`) ---------- */

/* Tous répondent à la même question — « qu'est-ce que j'attends encore » —
   et se répartissent sur trois familles seulement : ce qui a abouti, ce qui
   attend, ce qui réclame un geste. Le rouge reste l'échec au sens strict :
   quelque chose a été tenté et a raté. */
.pastille-etat--repondu { background: var(--success); }
.pastille-etat--attente { background: var(--text-tertiary); }
.pastille-etat--a-envoyer { background: var(--warning); }
.pastille-etat--incomplet { background: var(--warning); }
.pastille-etat--echec-envoi { background: var(--danger); }
/* Le menu du traiteur n'est pas arrivé : attendu, mais rien à faire sur
   cette personne — le geste est un import, à l'échelle de la structure.
   D'où un anneau et non un point plein : la teinte dit qu'il manque
   quelque chose, le creux dit que ce n'est pas à cette ligne d'agir. */
.pastille-etat--sans-menu {
  background: transparent;
  box-shadow: inset 0 0 0 2px var(--warning);
}
/* Aucun repas prévu : ni un retard ni un travail. Un anneau vide plutôt
   qu'un point, pour que la ligne des semaines montre le creux au lieu de le
   remplir d'une couleur qui voudrait dire quelque chose. */
.pastille-etat--sans-objet {
  background: transparent;
  box-shadow: inset 0 0 0 1.5px var(--border-strong);
}

/* L'en-tête de la collecte porte deux choses de rangs différents : le geste
   primaire de l'écran et le réglage de période. Elles s'alignent à droite
   du titre, et se replient l'une sous l'autre quand la place manque plutôt
   que de pousser la page de côté. */
.entete-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-3);
  flex-wrap: wrap;
}

/* Le geste que l'état appelle, et le « ⋯ » qui porte les autres : ils
   restent sur une ligne, collés — un menu qui s'éloignerait de son bouton
   se lirait comme un geste de plus, pas comme son prolongement. */
.ligne-gestes {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  justify-content: flex-end;
}

/* Un tableau est un contexte dense : le bouton y prend 36px et non 40
   (DESIGN.md §6), à la même hauteur que le « ⋯ » posé à côté. Mesuré avant
   correction : 40 contre 32, et le décalage se voyait sur chaque ligne. */
.ligne-gestes .btn { min-height: 36px; height: 36px; padding: 0 var(--space-4); }

/* 🔴 Le repli vit désormais sur `.barre-actions` elle-même : les deux
   gestes d'une sélection ne tenaient pas côte à côte sur un téléphone
   (mesuré, la barre sortait de l'écran de 350px), et le même défaut est
   réapparu deux fois ailleurs avant qu'on le corrige à la racine. La règle
   reste ici pour mémoire de la mesure. */

/* ---------- Commande traiteur (PLAN §6) ---------- */

.bloc-traiteur { margin-top: var(--space-4); }

.bloc-traiteur__entete { margin-bottom: var(--space-3); }

/* L'état de l'envoi : la première chose qu'on vient vérifier, donc la
   première ligne du bloc. La couleur porte l'information — parti, arrêté
   mais pas transmis, échoué, rien encore (DESIGN.md §1) — jamais un
   ornement : sans elle, quatre états se liraient identiques. */
.etat-envoi {
  margin: 0 0 var(--space-4);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-card);
  background: var(--bg-raised);
  font-size: 13px;
}

.etat-envoi--parti { background: var(--success-surface); color: var(--success-texte); }
.etat-envoi--erreur { background: var(--danger-surface); color: var(--danger-texte); }
.etat-envoi--attente { background: var(--warning-surface); color: var(--warning-texte); }

/* Ce qui a bougé depuis l'envoi — ce qu'on dit au traiteur au téléphone.
   Encadré à part du détail, sans quoi les deux se confondraient : l'un est
   la commande, l'autre sa correction. */
.ecart-envoi {
  margin-bottom: var(--space-4);
  padding: var(--space-3);
  border: 1px solid var(--warning);
  border-radius: var(--radius-card);
}

.ecart-envoi__titre { margin: 0 0 var(--space-2); font-weight: 600; font-size: 13px; }
.ecart-envoi__jour { margin: 0 0 var(--space-1); font-size: 13px; }

.ecart-envoi__ligne {
  display: inline-block;
  margin-left: var(--space-2);
  font-variant-numeric: tabular-nums;
}

.ecart-envoi__rubrique {
  display: inline-block;
  margin-left: var(--space-3);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-secondary);
}

/* ---------- Matrice produit × jour (Commande traiteur) ---------- */

/* Sept colonnes ne tiennent pas toujours dans la carte : la matrice défile
   sur elle-même plutôt que d'entraîner toute la page de côté, même geste
   que le reste de l'application. */
.matrice-traiteur {
  margin-top: var(--space-4);
  overflow-x: auto;
}

/* La rubrique tient sa propre ligne plutôt que de préfixer le premier
   produit : sur un tableau à plusieurs colonnes, « Menu B » posé sous
   « Menu A » sans repère ne dirait plus s'il s'agit d'une formule ou d'un
   composant — même raison que la même ligne dans le PDF (`.doc-rubrique`).

   🔴 **Et elle porte la couleur de la structure, en aplat** (demande du
   porteur, 31 août 2026 : « les entêtes, ils sont pas du tout lisibles »).
   Elle était grise sur gris, et son étiquette en gris secondaire pesait
   moins que ses propres chiffres — l'en-tête d'un groupe passait sous la
   ligne qu'il coiffe.

   L'aplat plutôt que du texte coloré, et ce n'est pas un goût : une
   couleur de marque n'est validée qu'à 3:1 contre le fond de page
   (`marque.utilisable`, seuil « composant »). Écrite en texte, elle
   passerait sous les 4,5:1 qu'exige DESIGN.md §9 chez les teintes les plus
   claires — c'est-à-dire précisément le défaut qu'on répare. En aplat, le
   texte est **déduit** (`marque.texte_sur`, noir ou blanc) et son
   contraste ne descend jamais sous 4,6:1, quelle que soit la couleur
   choisie : le produit des deux rapports vaut toujours 21, donc le meilleur
   des deux vaut au moins √21.

   Sans couleur posée dans les paramètres, `--marque` vaut l'accent du
   thème : la bande devient noire en clair, claire en sombre. La lisibilité
   ne dépend donc pas d'un réglage que personne n'a fait. */
.ligne-rubrique td {
  background: var(--marque);
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--marque-contrast);
}

/* La bande porte le sous-total de sa rubrique, en gras (demande du
   porteur, 20 août 2026) : la ligne existe déjà, autant qu'elle compte.
   Les chiffres reprennent la taille des lignes de produit — l'étiquette
   reste en petites capitales, le nombre se lit comme un nombre. */
.ligne-rubrique td.montant {
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0;
  color: var(--marque-contrast);
}

/* Le produit se lit en retrait de sa rubrique, comme sur le bon imprimé. */
.cellule-produit { padding-left: calc(var(--space-3) + var(--space-2)); }

.tableau tfoot td {
  border-top: 1px solid var(--border-strong);
  border-bottom: 0;
  font-weight: 600;
}

.liste-quantites { list-style: none; margin: 0; padding: 0; }

.liste-quantites li {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  padding: 2px 0;
  font-size: 13px;
}

.liste-quantites strong { font-variant-numeric: tabular-nums; }

/* ---------- Correction en ligne dans un tableau (Commandes) ---------- */

/* Le formulaire n'est là que pour porter le jeton CSRF : il ne doit rien
   occuper ni casser l'alignement de la cellule. */
.forme-en-ligne { display: inline; margin: 0; }

/* Un sélecteur dans un tableau se tient discret tant qu'on ne le vise pas :
   cent bordures de champ feraient du tableau un formulaire, et l'œil ne
   trouverait plus les lignes. Il se révèle au survol et au clavier — la
   bordure au `:focus-visible` reste, elle, indispensable. */
.select-en-ligne {
  max-width: 100%;
  padding: 2px var(--space-2) 2px var(--space-1);
  border: 1px solid transparent;
  border-radius: var(--radius-control);
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}

.select-en-ligne:hover { border-color: var(--border-strong); background: var(--bg-raised); }

/* Jamais retiré au profit du seul fond : le clavier doit voir où il est. */
/* La liste d'une cellule encore vide : discrète comme le tiret qu'elle
   remplace, tant qu'on ne s'en approche pas. Une cellule sans règlement
   ne doit pas attirer l'œil autant qu'un état réel — l'information est
   qu'il n'y a rien, pas qu'il y a un contrôle. */
.select-en-ligne--vide { color: var(--text-faint); }
.select-en-ligne--vide:hover, .select-en-ligne--vide:focus-visible {
  color: var(--text-primary);
}

/* Pastille et liste déroulante solidaires (29 août 2026) : sans ce
   conteneur, le point de couleur passait à la ligne au-dessus du
   select dès que la cellule se resserrait — vu au navigateur sur la
   colonne Règlement des factures. */
.etat-modifiable {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  white-space: nowrap;
}
.etat-modifiable .pastille-etat { margin-right: 0; flex-shrink: 0; }

.select-en-ligne:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 1px;
  border-color: var(--border-strong);
}

/* Le détail des plats sous le sélecteur : c'est lui qu'on lit pour
   vérifier, la formule seule (« B ») ne dit pas ce qu'il y a dans
   l'assiette. Borné en largeur — huit composants sur une ligne poussaient
   la colonne Origine hors de l'écran. */
.cellule-menu__detail {
  display: block;
  max-width: 32ch;
  margin-top: 2px;
}

/* La pastille et son sélecteur se lisent ensemble : séparés sur deux
   lignes, la couleur ne désignait plus rien.

   ⚠️ Le `display: flex` va sur un conteneur **à l'intérieur** de la
   cellule, jamais sur le `<td>` : un `td` en flex quitte la mise en page
   du tableau, et sa bordure basse cesse de s'aligner sur celles de la
   ligne — les traits partaient en escalier d'une colonne à l'autre
   (signalé par le porteur, 18 août 2026). */
.cellule-statut__contenu {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  white-space: nowrap;
}

/* Les cellules d'une même ligne s'alignent par le haut : la colonne Menu
   porte deux lignes (le sélecteur puis le détail des plats), et un
   alignement centré faisait flotter le numéro et la date à mi-hauteur,
   loin du menu qu'ils désignent. */
.tableau td { vertical-align: top; }

/* Les options d'un jour : une rangée de bascules, alignées à gauche et qui
   passent à la ligne. `.barre-actions` justifiait à droite et les étalait
   sur toute la largeur de la carte — trois boutons aux quatre coins ne se
   lisent pas comme un groupe de choix. */
.rangee-options {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin: 0;
}

/* Le prix collait au libellé (« Pain 0,32 € » se lisait presque
   « Pain0,32€ ») : l'écart entre puce et libellé (space-2) doit se
   retrouver entre libellé et prix, sans quoi le prix a l'air d'un
   sous-titre du mot plutôt que d'une troisième information. */
.rangee-options .meta { margin-left: var(--space-2); }

/*
 * La pilule à puce ronde : même composant que `.cases-en-ligne` (« Ce que
 * contient la formule », catalogue de l'offre), rejoué ici sur un
 * `<button>` puisque chaque option s'enregistre au clic plutôt que de
 * suivre un formulaire soumis en bloc. Les deux posent la même question —
 * « lequel est retenu ? » — et doivent donc se répondre de la même forme
 * (demande du porteur, 18 août 2026) : deux traitements pour un seul type
 * de choix auraient fait deviner, sur chaque écran, si c'est une case ou
 * un bouton qu'on a sous les yeux.
 */
.pilule-choix {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 40px;
  padding: var(--space-2) var(--space-4) var(--space-2) var(--space-3);
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-strong);
  background: var(--bg-surface);
  box-shadow: var(--shadow-card);
  color: var(--text-primary);
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  margin: 0;
  cursor: pointer;
  transition: background var(--transition), border-color var(--transition);
}

.pilule-choix:hover { background: var(--bg-raised); }

.pilule-choix:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

/* Retenue, la pilule s'enfonce d'un ton et son libellé s'affirme — même
   couple de canaux que `.cases-en-ligne` (fond + graisse), une puce cochée
   se lisant mal en biais si elle portait seule le sens. */
.pilule-choix--coche {
  background: var(--bg-inset);
  border-color: transparent;
  font-weight: 600;
}

[data-theme="dark"] .pilule-choix--coche { background: var(--bg-raised); }

/* Le rond qui se remplit : anneau vide au repos, aplat d'accent et coche
   blanche une fois retenu. La coche est dessinée en CSS (deux bords
   tournés), pas en image, pour prendre la couleur du thème. */
.pilule-choix__puce {
  position: relative;
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  border: 1.5px solid var(--border-strong);
  border-radius: 50%;
  background: var(--bg-surface);
  transition: background var(--transition), border-color var(--transition);
}

.pilule-choix--coche .pilule-choix__puce {
  background: var(--accent);
  border-color: var(--accent);
}

.pilule-choix__puce::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 47%;
  width: 5px;
  height: 9px;
  border: solid var(--accent-contrast);
  border-width: 0 2px 2px 0;
  transform: translate(-50%, -50%) rotate(45deg);
  opacity: 0;
  transition: opacity var(--transition);
}

.pilule-choix--coche .pilule-choix__puce::after { opacity: 1; }

/* ---------- Panneau « Modifier la commande » ---------- */

/* Des sections nommées plutôt qu'une suite de cartes muettes : le panneau
   enchaînait un texte, un champ nu et une table sans qu'aucun titre ne
   dise ce qu'on regardait. */
.panneau-bloc { margin-bottom: var(--space-4); }

.panneau-bloc__titre {
  margin: 0 0 var(--space-3);
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-secondary);
}

/* Ce qu'il y a dans l'assiette : le type à gauche, le plat à droite. Deux
   colonnes, parce qu'une énumération « Rôti · Carottes · Potage · Muffin »
   ne dit pas lequel est l'entrée — et que c'est justement la question
   qu'on se pose en relisant une commande au téléphone. */
.composition { margin: 0; }

.composition__ligne {
  display: grid;
  grid-template-columns: minmax(7rem, 1fr) 2fr;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--border);
}

.composition__ligne:last-child { border-bottom: 0; }

.composition dt {
  font-size: 13px;
  color: var(--text-secondary);
}

.composition dd { margin: 0; }

/* Un sélecteur de composition prend la largeur de sa colonne : à droite
   d'un intitulé, un champ à la largeur du texte le plus long faisait
   sauter l'alignement d'une ligne à l'autre. */
.composition dd select {
  width: 100%;
  min-height: 40px;
  font: inherit;
  font-size: 15px;
  color: var(--text-primary);
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-control);
  padding: var(--space-2) var(--space-3);
}

.composition dd select:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

@media (max-width: 480px) {
  /* À 390 px, deux colonnes laissent quatre caractères au plat. */
  .composition__ligne { grid-template-columns: 1fr; gap: var(--space-1); }
}

/* Une option dans le tableau : un jeton, pas une phrase. Celles du contrat
   restent en retrait — elles valent tous les jours et ne demandent aucun
   geste ; celles du jour, ajoutées à la main, portent le fond plein. La
   couleur distingue deux durées, elle ne décore pas (DESIGN.md §1). */
.cellule-options { max-width: 18ch; }

.jeton-option {
  display: inline-block;
  margin: 0 var(--space-1) 2px 0;
  padding: 1px var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--bg-inset);
  font-size: 12px;
  white-space: nowrap;
}

.jeton-option--contrat {
  background: transparent;
  border: 1px solid var(--border-strong);
  color: var(--text-secondary);
}

/*
 * Coupons scannés v2 (maquette du 21 août 2026) — les filtres du tableau
 * des dépôts et la barre du mode revue.
 */
.filtres-depots {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-bottom: var(--space-4);
}
.chip {
  display: inline-flex;
  align-items: center;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-strong);
  background: var(--bg-surface);
  padding: 5px 13px;
  font-size: 13px;
  color: var(--text-secondary);
  text-decoration: none;
}
.chip:hover { color: var(--text-primary); }
.chip--actif,
.chip--actif:hover {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-contrast);
}
.filtres-depots__periodes {
  margin-left: auto;
  display: inline-flex;
  gap: var(--space-3);
}
.filtres-depots__periodes a {
  color: var(--text-tertiary);
  text-decoration: none;
}
.filtres-depots__periodes a:hover { color: var(--text-primary); }
.filtres-depots__periode--active,
.filtres-depots__periodes a.filtres-depots__periode--active {
  color: var(--text-primary);
  font-weight: 600;
}

.tableau .tableau__nombre {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.tableau__action { text-align: right; }
.btn--compact { padding: 6px 14px; }

.revue-barre {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}
.revue-barre__fleche {
  flex: none;
  width: 38px;
  height: 38px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-strong);
  background: var(--bg-surface);
  color: var(--text-primary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 17px;
  text-decoration: none;
}
.revue-barre__fleche:hover { border-color: var(--accent); }
/* Une flèche sans destination reste visible mais ne promet rien. */
.revue-barre__fleche--inerte {
  color: var(--text-faint);
  border-color: var(--border);
}
.revue-barre__position { font-weight: 600; white-space: nowrap; }
.revue-barre__progression {
  flex: 1;
  min-width: 60px;
  height: 4px;
  border-radius: 2px;
  background: var(--bg-inset);
  overflow: hidden;
}
.revue-barre__progression i {
  display: block;
  height: 100%;
  background: var(--accent);
}
.revue-barre__sures {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}
@media (max-width: 720px) {
  .revue-barre { flex-wrap: wrap; }
  .revue-barre__progression { order: 10; flex-basis: 100%; }
}

/*
 * Sélecteur de semaine (maquette du 21 août 2026) : le libellé s'ouvre en
 * calendrier de semaines. Sans JavaScript, le libellé reste un texte.
 */
.selecteur-semaine__libelle--actif {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  background: var(--bg-surface);
  padding: 7px 15px;
  cursor: pointer;
}
.selecteur-semaine__libelle--actif::after {
  content: "";
  width: 7px;
  height: 7px;
  border-right: 1.5px solid var(--text-secondary);
  border-bottom: 1.5px solid var(--text-secondary);
  transform: rotate(45deg) translateY(-2px);
}

/* Le même calendrier, mais **dans un formulaire** (`partials/_champ_date`) :
   il prend la place d'un `<input type="date">`, il en prend donc la forme —
   pleine largeur du champ, coins de contrôle, date alignée à gauche et
   chevron à droite. Sans quoi une pilule flottait au milieu d'une colonne
   de champs rectangulaires. */
.selecteur-date-champ {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  width: 100%;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-control);
  background: var(--bg-surface);
  color: var(--text-primary);
  padding: 9px var(--space-3);
  font-size: 0.9375rem;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
}
.selecteur-date-champ:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 1px;
}

/* Le même contrôle, mais posé **entre deux flèches** plutôt que dans une
   colonne de champs (la navigation de mois de la facturation) : il prend
   alors la forme de son voisinage — une pilule à sa largeur. */
.selecteur-date-champ--pilule {
  width: auto;
  border-radius: var(--radius-pill);
  white-space: nowrap;
}
.cal-semaines {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  z-index: 30;
  /* 308 px là où la place existe, jamais plus large que ce qui l'accueille
     ni que l'écran : ouvert dans le panneau de filtres à 390 px, le
     calendrier débordait la page de 12 px et la faisait défiler de côté
     (mesuré au navigateur, 27 août 2026 — même défaut que les tableaux de
     la fiche client, BENEFICIAIRES.md §8.4). */
  width: min(308px, calc(100vw - 2 * var(--space-4)));
  max-width: 100%;
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-card);
  box-shadow: 0 14px 34px rgb(0 0 0 / 0.18);
  padding: var(--space-4);
}

/* Le choix d'un mois : douze pastilles plutôt qu'une grille de jours. Même
   panneau, même navigation d'année — c'est la même question posée moins
   finement. */
.cal-mois {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-1);
}
.cal-mois button {
  border: 0;
  border-radius: var(--radius-control);
  background: transparent;
  color: var(--text-secondary);
  font: inherit;
  font-size: 13px;
  padding: var(--space-2) 0;
  cursor: pointer;
}
.cal-mois button:hover { background: var(--bg-raised); color: var(--text-primary); }
.cal-mois button:focus-visible { background: var(--bg-inset); }
.cal-mois button.mois--choisi,
.cal-mois button.mois--choisi:hover {
  background: var(--accent);
  color: var(--accent-contrast);
}
.cal-semaines__tete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-2);
}
.cal-semaines__nav { display: flex; gap: 4px; }
.cal-semaines__nav button {
  width: 28px;
  height: 28px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-strong);
  background: var(--bg-surface);
  color: var(--text-primary);
  cursor: pointer;
  font-size: 13px;
}
.cal-semaines table { border-collapse: separate; border-spacing: 0 3px; width: 100%; font-size: 13px; }
.cal-semaines th {
  padding: 3px 0;
  text-align: center;
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-tertiary);
  border: 0;
}
.cal-semaines td {
  padding: 5px 0;
  text-align: center;
  color: var(--text-secondary);
  border: 0;
  font-variant-numeric: tabular-nums;
}
.cal-semaines td.hors { color: var(--text-faint); }
.cal-semaines tr.semaine { cursor: pointer; }
.cal-semaines tr.semaine:hover td { background: var(--bg-raised); }
.cal-semaines tr.semaine:focus-visible td { background: var(--bg-inset); }
/* La ligne entière se colore : on choisit une période, pas un jour. */
.cal-semaines tr.semaine td:first-child { border-radius: var(--radius-pill) 0 0 var(--radius-pill); }
.cal-semaines tr.semaine td:last-child { border-radius: 0 var(--radius-pill) var(--radius-pill) 0; }
.cal-semaines tr.semaine--choisie td { background: var(--accent); color: var(--accent-contrast); }
.cal-semaines tr.semaine--choisie td.hors { color: var(--accent-contrast); opacity: 0.55; }
.cal-semaines__pied { margin-top: var(--space-2); }
.cal-semaines__courante {
  border: 0;
  background: none;
  padding: 4px 0;
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  color: var(--text-primary);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}

/* Commande traiteur v2 : la ligne d'état du bon et les cellules d'écart. */
.etat-bon {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-bottom: var(--space-4);
  font-size: 13px;
  color: var(--text-secondary);
}
.puce-ecart {
  display: inline-flex;
  border-radius: var(--radius-pill);
  padding: 3px 10px;
  font-size: 12.5px;
  background: var(--warning-surface);
  color: var(--warning-texte);
}
.cellule-bougee {
  display: inline-block;
  background: var(--warning-surface);
  color: var(--warning-texte);
  border-radius: 6px;
  padding: 2px 6px;
}
.cellule-bougee .cellule-bougee__avant {
  display: block;
  font-size: 10.5px;
  line-height: 1.1;
}
.badge--neutre { background: var(--bg-inset); color: var(--text-secondary); }

/* Calendrier en mode « jour » : la cellule se choisit, pas la ligne. */
.cal-semaines td.jour { cursor: pointer; border-radius: var(--radius-pill); }
.cal-semaines td.jour:hover { background: var(--bg-raised); color: var(--text-primary); }
.cal-semaines td.jour:focus-visible { background: var(--bg-inset); }
.cal-semaines td.jour--choisi,
.cal-semaines td.jour--choisi:hover { background: var(--accent); color: var(--accent-contrast); }

/*
 * Flotte (paramètres → Livreurs / Camions, 22 août 2026).
 */
.pastilles { display: inline-flex; gap: 6px; flex-wrap: wrap; }
.pastille {
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  padding: 2px 10px;
  font-size: 12.5px;
  color: var(--text-secondary);
  white-space: nowrap;
}
/* Reçue en remplacement : temporaire, et ça se voit. */
.pastille--temporaire { border-style: dashed; color: var(--text-tertiary); }

.ligne-eteinte td { color: var(--text-faint); }
.ligne-eteinte .badge { opacity: 0.75; }

.lien-bouton {
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  font-size: inherit;
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}

/* L'interrupteur : révoquer/réactiver en un geste. C'est un bouton de
   formulaire — la confirmation passe par data-confirmer, comme partout. */
.interrupteur {
  width: 38px;
  height: 22px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-strong);
  background: var(--bg-inset);
  position: relative;
  cursor: pointer;
  padding: 0;
}
.interrupteur::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 3px;
  width: 16px;
  height: 16px;
  border-radius: var(--radius-pill);
  background: var(--text-faint);
  transition: left 0.12s ease;
}
.interrupteur--on { background: var(--accent); border-color: var(--accent); }
.interrupteur--on::after { left: 17px; background: var(--accent-contrast); }
@media (prefers-reduced-motion: reduce) {
  .interrupteur::after { transition: none; }
}

/* ---------- Conformité facture électronique (DESIGN.md §6, 22 août 2026) ----------

   Deux composants seulement, parce que l'écran ne pose que deux questions.

   `.echeance` — la prochaine transmission, en tête du tableau de bord de
   conformité. DESIGN.md §6 l'exige sous cette forme : « prochaine échéance
   en haut (date + compte à rebours) ». Trois zones dans une seule carte —
   le compte à rebours, ce qu'il concerne, le geste — parce qu'une date
   d'échéance, l'objet qu'elle vise et le bouton qui l'éteint ne se lisent
   pas séparément : c'est une seule phrase. Le coût d'un oubli (500 €) ne
   justifie pas un décor, il justifie une hiérarchie.

   `.etape` — une étape numérotée d'un parcours, ici le dépôt (regarder,
   contrôler, déposer). Même raisonnement que le chemin de la collecte : des
   cartes indépendantes diraient « voici trois choses », des étapes
   numérotées disent « voici un parcours, dans cet ordre ». Elles restent
   trois cartes distinctes et non une seule surface, à la différence du
   chemin de la collecte, parce que chacune porte ici un tableau ou un
   formulaire, pas une mesure. */

.echeance {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--space-6);
  margin-bottom: var(--space-6);
}

/* Le compte à rebours est en creux, pas en couleur : au repos, une échéance
   qui approche n'est pas une alerte (§1 — la couleur est une information).
   Elle ne se teinte qu'en retard, ci-dessous. */
.echeance__compte {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  min-width: 132px;
  margin: 0;
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-card);
  background: var(--bg-inset);
  text-align: center;
}

.echeance__compte .chiffre-cle { line-height: 1.1; }
.echeance__objet h2 { margin: 0 0 var(--space-1); }
.echeance__objet .meta { margin: 0 0 var(--space-1); }
.echeance__objet .meta:last-child { margin-bottom: 0; }

.echeance--retard { border-left: 2px solid var(--danger); }
.echeance--retard .echeance__compte { background: var(--danger-surface); }

/* Rien à déposer : la carte perd sa colonne de gauche et son bouton — il
   n'y a ni compte à rebours à tenir ni geste à faire. Une tuile qui
   afficherait « 0 j » en rang d'honneur mentirait deux fois (DESIGN.md
   §14 Q4). */
.echeance--calme { grid-template-columns: 1fr; }

@media (max-width: 720px) {
  .echeance { grid-template-columns: 1fr; }
  .echeance__compte { align-items: flex-start; text-align: left; }
}

/* Une carte qui n'est qu'une pile de lignes (anomalies, manques) : le
   rembourrage vient des lignes, la carte ne fait que porter l'ombre et
   couper les angles. */
.carte--liste {
  padding: 0;
  overflow: hidden;
}

.carte--liste .anomalie:last-child { border-bottom: 0; }

.etape { margin-bottom: var(--space-4); }

.etape__rang {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0 0 var(--space-2);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-secondary);
}

.etape__num {
  display: grid;
  place-items: center;
  flex: none;
  width: 20px;
  height: 20px;
  border-radius: var(--radius-pill);
  background: var(--bg-inset);
  color: var(--text-secondary);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0;
}

/* Une étape franchie : le numéro seul change d'habit. Le titre, lui, se
   relit à l'identique — c'est l'information, elle ne doit pas bouger d'un
   coup d'œil à l'autre. */
.etape__num--faite { background: var(--success-surface); color: var(--success-texte); }

.etape__titre { margin: 0 0 var(--space-1); }
.etape__detail { margin: 0; max-width: 72ch; }
.etape .tableau { margin-top: var(--space-4); }

/* Le tableau d'un agrégat est long (une ligne par jour et par taux) et ne
   porte que des chiffres : il se resserre, sinon trente jours occupent deux
   écrans pour dire un total. */
.tableau--compact td { padding-top: var(--space-2); padding-bottom: var(--space-2); }
.tableau--compact tfoot td {
  border-top: 1px solid var(--border-strong);
  font-weight: 600;
}

/* Télécharger et enregistrer le dépôt sont deux gestes de rangs différents,
   séparés par l'aller-retour sur le portail de la plateforme. L'espace
   entre eux le dit (DESIGN.md §14 Q8). */
.depot-geste {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-6);
  margin-top: var(--space-4);
}

.depot-formulaire { max-width: 420px; width: 100%; }
.depot-formulaire .champ:last-of-type { margin-bottom: var(--space-4); }

.depot-reponse {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-4);
  margin-top: var(--space-4);
}

/* Le rejet est replié : c'est l'issue rare, et la déplier est un geste
   délibéré. L'offrir à côté de « accusé reçu » ferait hésiter à chaque
   fois entre deux boutons de même poids.

   Le chevron n'est pas décoratif : la feuille masque le marqueur natif de
   `<details>` partout (plus haut), et sans lui ce repli ressemblait à une
   phrase morte — mesuré au navigateur le 22 août. */
.depot-rejet summary {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  cursor: pointer;
  padding: var(--space-2) 0;
  color: var(--text-secondary);
  font-size: 14px;
}

.depot-rejet summary::before {
  content: "";
  width: 6px;
  height: 6px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(-45deg);
  transition: transform var(--transition);
}

.depot-rejet[open] summary::before { transform: rotate(45deg); }
.depot-rejet summary:hover { color: var(--text-primary); }
.depot-rejet[open] summary { margin-bottom: var(--space-3); }
.depot-rejet form { max-width: 420px; }

@media (prefers-reduced-motion: reduce) {
  .depot-rejet summary::before { transition: none; }
}

/* Les quatre états d'un dépôt (DESIGN.md §6 — « quatre états et rien
   d'autre »). « À déposer » est neutre : c'est du travail à faire, pas un
   incident. « Transmise » est bleu — l'attente d'une réponse est une
   information, pas un problème. Le retard, lui, n'est pas un état stocké :
   c'est une date qui passe, et il se dit en rouge dans la pastille de la
   ligne. */
.pastille-etat--a-deposer { background: var(--text-tertiary); }
.pastille-etat--transmise { background: var(--info); }
.pastille-etat--acceptee { background: var(--success); }
.pastille-etat--rejetee { background: var(--danger); }

/* Une ligne d'anomalie tient sur une seule ligne au bureau : le libellé
   pousse le montant et le bouton à droite. À 390 px la même disposition
   coince le bouton au milieu du texte — il passe donc dessous, où il se
   lit comme la suite de la phrase qui l'explique. */
@media (max-width: 720px) {
  .anomalie {
    flex-wrap: wrap;
    align-items: flex-start;
    gap: var(--space-3);
  }
  .anomalie__libelle { flex-basis: 100%; }
}

/* La sortie de secours d'un dépôt accepté (DESIGN.md §13) : geste de
   troisième rang, décollé de la phrase qu'il contredit. */
.depot-reprise { margin-top: var(--space-3); margin-left: calc(-1 * var(--space-4)); }

/* Télécharger le fichier et ouvrir le portail sont les deux moitiés d'un
   même aller : côte à côte, pas empilées. */
.depot-portail {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
}

/* Sous la date d'une commande : qui apporte ce repas, et quel jour si le
   camion passe en avance. Une nuance plus bas que la date elle-même — le
   repas est le fait principal, le dépôt est ce qui l'explique. Sur sa
   propre ligne, parce qu'à côté il se lirait comme une seconde date. */
.commande-depot {
  display: block;
  color: var(--text-tertiary);
  white-space: nowrap;
}

/* Le choix du passage d'un repas vit **dans une cellule de tableau**, sur la
   fiche d'une commande. `.champ-radios` y apporte son `min-width: 16rem`,
   voulu là où il sert d'origine (un rapprochement, où chaque option porte
   une légende), mais qui pousse ici la carte entière au-delà de l'écran :
   413 px de contenu dans 390 px de large, mesuré au navigateur le 23 août
   2026. On le relâche pour cet emploi seulement — l'autre garde sa largeur
   plancher, qui lui est utile. */
.choix-passage .champ-radios { min-width: 0; }
.choix-passage form { max-width: 100%; }
.choix-passage input[type="text"] { max-width: 100%; }
.choix-passage .champ { margin-top: var(--space-4); }

/* ==========================================================================
   « À traiter » — le poste de pilotage (PRD §6.8, DESIGN.md §6)

   Deux colonnes : la file d'attention à gauche, le contexte du jour à
   droite. Le rail repasse sous la file en dessous de 1200px — c'est une
   adaptation à la largeur, pas un second écran à maintenir.
   ========================================================================== */

.a-traiter {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 316px;
  gap: var(--space-5);
  align-items: start;
}

.a-traiter__entete { align-items: baseline; }
.a-traiter__resume { margin: var(--space-1) 0 0; }
.a-traiter__date {
  margin: 0;
  color: var(--text-tertiary);
  font-size: 12px;
  white-space: nowrap;
  text-transform: lowercase;
}

.a-traiter__file > .carte + .carte { margin-top: var(--space-5); }
.a-traiter__rail { display: grid; gap: var(--space-5); }

.a-traiter__carte-entete {
  padding: var(--space-4) var(--space-5) 0;
}
.a-traiter__carte-entete h2 {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.01em;
}
.a-traiter__carte-entete .meta { margin: 2px 0 0; }

/* La ligne d'attention — la ligne d'anomalie de facturation généralisée à
   toutes les files du produit. Le liseré gauche dit la gravité, jamais
   seul : le module, le fait et l'enjeu la disent aussi. */
.file-attention {
  list-style: none;
  margin: 0;
  padding: var(--space-2) 0;
}

.file-attention__ligne {
  display: grid;
  grid-template-columns: 104px minmax(0, 1fr) auto auto;
  align-items: center;
  gap: var(--space-4);
  padding: 13px var(--space-5) 13px 18px;
  border-left: 2px solid transparent;
}

.file-attention__ligne + .file-attention__ligne { border-top: 1px solid var(--border); }
.file-attention__ligne:hover { background: var(--bg-raised); }
/* Le rouge est réservé à ce qui coûte déjà (DESIGN.md §1) : une échéance
   qui approche n'est pas une alerte. */
.file-attention__ligne--grave { border-left-color: var(--danger); }
.file-attention__ligne--attention { border-left-color: var(--warning); }
/* Le troisième niveau : la ligne **informe, elle n'alerte pas** (RGPD.md
   §6 lot 5, demande du porteur du 29 août 2026 : « il faut que tout soit
   discret »). Le liseré reste celui de la ligne nue — transparent — et la
   déclaration est écrite ici plutôt que sous-entendue par l'absence de
   règle : sans elle, un lecteur du CSS croirait la classe oubliée. */
.file-attention__ligne--calme { border-left-color: transparent; }

.file-attention__module {
  font-size: 12px;
  color: var(--text-tertiary);
}

.file-attention__fait { font-size: 15px; color: var(--text-primary); }
.file-attention__fait small {
  display: block;
  margin-top: 1px;
  font-size: 12px;
  color: var(--text-tertiary);
}

.file-attention__enjeu {
  font-size: 15px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}
.file-attention__enjeu small {
  display: block;
  font-size: 12px;
  font-weight: 400;
  color: var(--text-tertiary);
}
/* Une échéance n'est pas un montant : même colonne, poids moindre — sinon
   « 12 sept. » pèse autant que « 214 € », ce qui fausse la lecture. */
.file-attention__enjeu--texte {
  font-size: 13px;
  font-weight: 500;
  color: var(--text-secondary);
}

.file-attention__vide {
  margin: 0;
  padding: var(--space-5);
  color: var(--text-secondary);
  font-size: 14px;
}

/* Le rail : des faits constatés, jamais une extrapolation. */
.rail-faits, .rail-tournees { list-style: none; margin: var(--space-2) 0 0; padding: 0 0 var(--space-3); }

.rail-faits li {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  padding: 10px var(--space-5);
  font-size: 14px;
}
.rail-faits li + li, .rail-tournees li + li { border-top: 1px solid var(--border); }
.rail-faits strong { font-variant-numeric: tabular-nums; font-weight: 600; }

.rail-tournees li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  padding: 10px var(--space-5);
  font-size: 14px;
}
.rail-tournees li small {
  grid-column: 1;
  font-size: 12px;
  color: var(--text-tertiary);
}
.rail-tournees li .badge { grid-row: 1 / span 2; font-variant-numeric: tabular-nums; }

.a-traiter__rien {
  margin: 0;
  padding: var(--space-3) var(--space-5) var(--space-5);
  font-size: 13.5px;
  color: var(--text-secondary);
}

.a-traiter__echeance { padding-bottom: var(--space-5); }
.a-traiter__compte-a-rebours {
  margin: var(--space-3) var(--space-5) 0;
  font-size: 28px;
  font-weight: 600;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
  line-height: 1.15;
}
.a-traiter__echeance .meta { margin: 4px var(--space-5) var(--space-4); }
.a-traiter__echeance .btn { margin: 0 var(--space-5); }

@media (max-width: 1200px) {
  .a-traiter { grid-template-columns: minmax(0, 1fr); }
  /* Le jour d'abord quand tout s'empile : sur un portable, c'est lui qu'on
     perdrait de vue en défilant. */
  .a-traiter__rail { order: -1; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
}

@media (max-width: 720px) {
  .a-traiter__rail { grid-template-columns: minmax(0, 1fr); }
  /* La ligne s'empile en trois rangées : le module, le fait sur toute la
     largeur, puis l'enjeu et le geste côte à côte. Les faire cohabiter sur
     une même rangée réduisait le fait à un mot par ligne (mesuré à 390px). */
  .file-attention__ligne {
    grid-template-columns: minmax(0, 1fr) auto;
    row-gap: var(--space-2);
    padding-right: var(--space-4);
  }
  .file-attention__module,
  .file-attention__fait { grid-column: 1 / -1; }
  .file-attention__enjeu { grid-column: 1; text-align: left; }
  .file-attention__ligne .btn { grid-column: 2; justify-self: end; }
}

/* ==========================================================================
   Rentabilité — le pilotage financier (PRD §6.7, DESIGN.md §6)
   ========================================================================== */

.rentabilite__deux {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: var(--space-5);
  align-items: start;
}

/* Le compte se lit en une colonne étroite, la courbe a besoin de largeur :
   à parts égales, douze mois se serraient sur 400 px et les mois se
   chevauchaient. */
@media (min-width: 1000px) {
  .rentabilite__deux--compte { grid-template-columns: 5fr 7fr; }
}

/* Le compte d'exploitation : une carte qui se lit de haut en bas comme une
   soustraction. Le total change de fond parce que c'est le seul chiffre
   qu'on vient chercher. */
.compte { list-style: none; margin: var(--space-3) 0 0; padding: 0; }

.compte li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--space-4);
  align-items: baseline;
  padding: 11px var(--space-5);
}
.compte li + li { border-top: 1px solid var(--border); }

.compte__poste { font-size: 15px; }
.compte__poste small {
  display: block;
  margin-top: 1px;
  font-size: 12px;
  color: var(--text-tertiary);
}

.compte__montant {
  font-size: 15px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.compte__total {
  background: var(--bg-raised);
  border-top: 1px solid var(--border-strong);
}
.compte__total .compte__montant { font-size: 17px; }

/* La note d'exclusion n'est pas décorative : un compte qui ne dit pas ce
   qu'il n'inclut pas se lit comme un résultat net. */
.compte__note {
  margin: 0;
  padding: 12px var(--space-5) var(--space-4);
  border-top: 1px solid var(--border);
  font-size: 12px;
  color: var(--text-tertiary);
}

.marges__entete {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
  padding-right: var(--space-5);
}

.barres-marge { list-style: none; margin: var(--space-3) 0 0; padding: 0; }
.barres-marge li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--space-4);
  align-items: center;
  padding: 12px var(--space-5);
}
.barres-marge li + li { border-top: 1px solid var(--border); }

.barres-marge__nom { font-size: 14px; font-weight: 500; }
.barres-marge__nom small {
  display: block;
  margin-top: 1px;
  font-size: 12px;
  font-weight: 400;
  color: var(--text-tertiary);
}

.barres-marge__valeur {
  font-size: 16px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}
.barres-marge__valeur small {
  display: block;
  font-size: 11px;
  font-weight: 400;
  color: var(--text-tertiary);
}

.pied-geste { margin: 0; padding: var(--space-2) var(--space-5) var(--space-4); }

/* ==========================================================================
   Graphiques (DESIGN.md §6) — marques fines, grille en retrait.

   Le SVG arrive déjà positionné depuis `apps.core.graphiques` : ces règles
   ne font que l'habiller. Rien ici n'est nécessaire pour comprendre le
   graphique — le tableau de valeurs sous chaque courbe reste la source
   lisible sans couleur, sans JavaScript et à l'impression.
   ========================================================================== */

.graphique { padding: var(--space-3) var(--space-5) 0; }
.graphique svg { display: block; width: 100%; height: auto; overflow: visible; }

.graphique__grille { stroke: var(--border-strong); stroke-width: 1; }
.graphique__axe {
  font-size: 11.5px;
  fill: var(--text-tertiary);
  font-family: inherit;
  font-variant-numeric: tabular-nums;
}

.graphique__trace {
  fill: none;
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
}
/* La comparaison à l'année précédente est un **repère**, pas une seconde
   identité : pointillé, encre pâle, aucune teinte catégorielle. */
.graphique__trace--repere {
  stroke: var(--text-faint);
  stroke-width: 1.5;
  stroke-dasharray: 5 4;
}

/* Le dernier point, et lui seul. L'anneau de surface le détache du tracé
   quand la courbe repasse dessous. */
.graphique__point { stroke: var(--bg-surface); stroke-width: 2; }
.graphique__etiquette {
  font-size: 12px;
  font-weight: 600;
  fill: var(--text-primary);
  font-family: inherit;
  font-variant-numeric: tabular-nums;
}

.graphique__viseur { stroke: var(--border-strong); stroke-width: 1; }
.graphique__zone { fill: transparent; }

.graphique__legende {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin: 0;
  padding: var(--space-2) var(--space-5) var(--space-3);
  font-size: 12px;
  color: var(--text-secondary);
}
.graphique__legende span { display: inline-flex; align-items: center; gap: 7px; }
.graphique__legende i { width: 16px; height: 0; border-top: 2px solid currentColor; }
.graphique__legende i.graphique__legende--repere {
  border-top: 2px dashed var(--text-faint);
}

/* Sur un téléphone, le SVG se réduit à environ 330 px : douze mois ne
   tiennent plus côte à côte. Une étiquette sur deux s'efface — la courbe
   reste une tendance lisible, et « Voir les valeurs » porte les chiffres
   exacts (DESIGN.md §6). */
@media (max-width: 700px) {
  .graphique__abscisses text:nth-of-type(even) { display: none; }
}

.graphique__vide {
  margin: 0;
  padding: var(--space-5);
  font-size: 13.5px;
  color: var(--text-secondary);
}

.graphique__valeurs { border-top: 1px solid var(--border); }
.graphique__valeurs summary {
  padding: 9px var(--space-5);
  font-size: 12px;
  color: var(--text-tertiary);
  cursor: pointer;
  list-style: none;
}
.graphique__valeurs summary::-webkit-details-marker { display: none; }
.graphique__valeurs summary::before { content: "▸ "; }
.graphique__valeurs[open] summary::before { content: "▾ "; }
.graphique__defile { overflow-x: auto; }
.graphique__table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}
.graphique__table th, .graphique__table td {
  padding: 6px var(--space-2);
  text-align: right;
  white-space: nowrap;
  border-top: 1px solid var(--border);
  color: var(--text-secondary);
  font-weight: 400;
}
.graphique__table thead th { color: var(--text-tertiary); font-weight: 500; }
.graphique__table th:first-child, .graphique__table td:first-child {
  text-align: left;
  padding-left: var(--space-5);
}

/* L'infobulle du survol : une amélioration, jamais la condition pour lire. */
.graphique-bulle {
  position: fixed;
  z-index: 40;
  pointer-events: none;
  display: none;
  padding: 7px 11px;
  border-radius: var(--radius-control);
  background: var(--accent);
  color: var(--accent-contrast);
  box-shadow: var(--shadow-float);
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* ---------- Barres horizontales ---------- */

.barres { list-style: none; margin: var(--space-3) 0 0; padding: 0 0 var(--space-2); }

.barres__ligne {
  display: grid;
  grid-template-columns: minmax(96px, 148px) minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-4);
  padding: 11px var(--space-5);
}
.barres__ligne + .barres__ligne { border-top: 1px solid var(--border); }

.barres__nom { font-size: 14px; font-weight: 500; }
.barres__nom small {
  display: block;
  margin-top: 1px;
  font-size: 12px;
  font-weight: 400;
  color: var(--text-tertiary);
}

.barres__piste { position: relative; height: 12px; }
.barres__remplie {
  position: absolute;
  inset: 0 auto 0 0;
  /* Extrémité arrondie côté valeur, plate côté ligne de base : la barre
     part d'un zéro franc et se termine sur une marque douce. */
  border-radius: 2px 6px 6px 2px;
}

.barres__valeur {
  font-size: 15px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}
.barres__valeur small {
  display: block;
  font-size: 11px;
  font-weight: 400;
  color: var(--text-tertiary);
}

/* Jauge de remplissage : la part consommée, le reste en creux. Les 2px de
   surface entre les deux évitent que les segments se touchent. */
.jauge {
  position: relative;
  height: 12px;
  border-radius: 6px;
  background: var(--bg-inset);
  overflow: hidden;
}
.jauge__part {
  position: absolute;
  inset: 0 auto 0 0;
  border-right: 2px solid var(--bg-surface);
}

@media (max-width: 720px) {
  .barres__ligne {
    grid-template-columns: minmax(0, 1fr) auto;
    row-gap: var(--space-2);
  }
  /* La piste passe sous le libellé : comprimée entre deux colonnes de
     texte, elle ne mesurait plus rien. */
  .barres__piste { grid-column: 1 / -1; grid-row: 2; }
}

/* La pastille de statut devient le geste qui l'ouvre (24 août 2026) : le
   statut n'était atteignable que par le formulaire complet de la fiche.
   Aucun soulignement ni couleur de lien — c'est la pastille qui porte
   l'information, le lien ne fait que la rendre actionnable. Le curseur et
   l'anneau de focus suffisent à dire qu'elle se clique. */
.lien-statut { text-decoration: none; color: inherit; border-radius: var(--radius-pill); }
.lien-statut:hover .badge { filter: brightness(0.96); }
.lien-statut:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Ce qu'un geste va facturer, dit avant qu'on le valide (panneau « Repas
   invités », UX-OFFRE.md §5). Surface d'information et non d'alerte : un
   prix n'est pas un incident, et le rouge reste réservé aux anomalies
   (DESIGN.md §2). Même surface que `.bandeau-gel`, qui énonce lui aussi une
   règle du jeu plutôt qu'un incident. */
.encart-prix {
  background: var(--info-surface);
  color: var(--info-texte);
  border-radius: var(--radius-card);
  padding: var(--space-4) var(--space-5);
  margin-bottom: var(--space-5);
}

.encart-prix p { margin: 0; font-size: 14px; line-height: 1.5; }
.encart-prix p + p { margin-top: var(--space-2); }

/* Le prix manque : ce n'est plus une information, c'est quelque chose à
   aller poser. L'orange le dit sans crier — rien n'est cassé, mais des
   repas vont être servis sans être facturés. */
.encart-prix--manquant {
  background: var(--warning-surface);
  color: var(--warning-texte);
}

/* Les convives sur une case du calendrier. Un badge et non une pastille
   colorée : ce n'est pas un état du jour (prévu / livré / absent), c'est un
   nombre — et la palette des états ne doit jamais servir à dire autre chose
   qu'un état (DESIGN.md §1).

   `block`, comme `.calendrier__menu` juste au-dessus : en `inline`, le
   bouton d'action venait se coller derrière et la case affichait
   « +2 invités Retirer », qui se lit comme une seule phrase et dit le
   contraire de ce qu'elle fait. */
.calendrier__supplements {
  display: block;
  margin-top: 2px;
  font-size: 11px;
  line-height: 1.4;
  color: var(--text-secondary);
}

/* Deux gestes sur une même case (« Retirer » et les invités) : un écart
   entre eux, sinon les deux libellés soulignés n'en font qu'un à l'œil. */
.calendrier__action + .calendrier__action { margin-left: var(--space-2); }

/* Les convives d'une commande, sur sa fiche. Séparés du menu par un filet
   plutôt que par une carte de plus : ce sont les mêmes barquettes, le même
   jour, la même cuisine — un encadré à part les ferait lire comme une autre
   commande. */
.commande-supplements {
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
}

.commande-supplements p { margin: 0 0 var(--space-2); font-size: 14px; }
.commande-supplements .btn { margin-top: var(--space-2); }

/* ---------- Journal interne (/journal) ---------- */

/* Un compte avec son visage — la photo de profil à côté du nom (demande du
   porteur, 27 août 2026). L'avatar reprend celui de la topbar, en 24px :
   dans un tableau qui dit « qui fait quoi », le visage se repère avant que
   le nom ne se lise. */
.personne {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.personne--absente { color: var(--text-tertiary); }

.avatar--petit { width: 24px; height: 24px; }

/* Les points des puces de statut du journal : bleu information pour ce qui
   attend d'être pris, neutre pour ce qui avance, vert pour ce qui est
   clos. Jamais de rouge — une demande n'est pas une anomalie (DESIGN.md
   §1). */
.pastille-etat--nouvelle { background: var(--info); }
.pastille-etat--en-cours { background: var(--text-tertiary); }
.pastille-etat--traitee { background: var(--success); }

/* Le fil de suivi d'une demande : qui a répondu quoi, quand. Des filets et
   non des cartes — c'est une seule conversation, pas une pile de
   messages. */
.commentaire-journal { padding: var(--space-3) 0; }

.commentaire-journal + .commentaire-journal { border-top: 1px solid var(--border); }

.commentaire-journal__entete {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.commentaire-journal__texte { margin: var(--space-2) 0 0; }

/* Le tableau du journal (liste et fiche client) devient une pile de cartes
   sous 640px — même geste que les contrats et les prises en charge : six
   colonnes dont deux personnes avec avatar ne tiennent pas à 390px. */
@media (max-width: 640px) {
  .tableau--journal thead { display: none; }

  .tableau--journal,
  .tableau--journal tbody,
  .tableau--journal tr,
  .tableau--journal td {
    display: block;
    width: 100%;
  }

  .tableau--journal tr {
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
    margin-bottom: var(--space-3);
    padding: var(--space-3);
  }

  .tableau--journal tr:last-child { margin-bottom: 0; }

  .tableau--journal td {
    border: 0;
    padding: var(--space-2) 0;
  }

  .tableau--journal td:first-child { padding-top: 0; }
  .tableau--journal td:last-child { padding-bottom: 0; }

  .tableau--journal td::before {
    content: attr(data-label);
    display: block;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-tertiary);
    margin-bottom: 2px;
  }
}

/* ---------- La cloche du journal (topbar) ---------- */

.cloche__bouton { position: relative; }

/* La pastille de compte reprend l'accent, jamais le rouge : être attendu
   n'est pas une anomalie (DESIGN.md §1). L'anneau couleur de la barre la
   détache de l'icône sans grossir le chiffre. */
.cloche__compte {
  position: absolute;
  top: 0;
  right: 0;
  pointer-events: none;
  box-shadow: 0 0 0 2px var(--bg-surface);
}

.cloche__panneau { width: min(360px, calc(100vw - 2 * var(--space-4))); }

.cloche__ligne {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-2);
  border-radius: var(--radius-control);
  font-weight: 400;
  transition: background var(--transition);
}

.cloche__ligne:hover { background: var(--bg-raised); text-decoration: none; }

.cloche__ligne .avatar--petit { margin-top: 1px; }

.cloche__texte {
  flex: 1;
  font-size: 13px;
  line-height: 1.45;
  color: var(--text-secondary);
}

.cloche__texte .meta { display: block; margin-top: 2px; }

/* Une ligne non lue se lit en premier : texte plein, et un point bleu
   information — jamais la couleur seule, la phrase dit déjà ce qui s'est
   passé (§8). */
.cloche__texte--non-lue { color: var(--text-primary); font-weight: 500; }

.cloche__texte--non-lue .meta { font-weight: 400; }

.cloche__point {
  flex-shrink: 0;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--info);
  margin-top: 7px;
}

.cloche__vide {
  margin: 0;
  padding: var(--space-3);
  max-width: 280px;
  font-size: 13px;
  color: var(--text-secondary);
}

.cloche__pied {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-1);
  padding-top: var(--space-1);
  border-top: 1px solid var(--border);
}

/* Sous 640px, la cloche est à 130px du bord droit : un panneau de 360px
   ancré sur elle sortirait de l'écran par la gauche. Il s'ancre alors sur
   la topbar entière, d'un bord à l'autre. */
@media (max-width: 640px) {
  .topbar { position: relative; }

  .cloche { position: static; }

  .cloche__panneau {
    left: var(--space-2);
    right: var(--space-2);
    width: auto;
  }
}

/* ==========================================================================
   Les durées de conservation (RGPD.md §6 lot 4, 29 août 2026)
   ==========================================================================

   Une ligne par durée : ce qu'on garde à gauche, combien de temps à
   droite. Les durées imposées par la loi n'ont pas de champ — leur ligne
   se distingue par un fond, pas par une couleur : ce n'est ni une alerte
   ni un succès, c'est un fait (DESIGN.md §1). */
.liste-durees__ligne {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--border);
}

.liste-durees__ligne:last-child { border-bottom: 0; }

.liste-durees__ligne--imposee {
  background: var(--bg-raised);
  border-radius: var(--radius-card);
  padding-left: var(--space-3);
  padding-right: var(--space-3);
}

.liste-durees__nom { margin: 0 0 2px; font-weight: 500; }

.liste-durees__valeur {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  flex-shrink: 0;
  text-align: right;
}

/* Un champ nombre n'a pas besoin de la largeur d'un champ texte : trois
   chiffres suffisent, et un champ pleine largeur ferait croire qu'on y
   attend une phrase. */
/* 🔴 Ces champs ne vivent pas dans un `.champ` : ils sont posés au bout de
   leur ligne, dans la liste. Ils échappaient donc au style commun des
   saisies et gardaient le rendu natif du navigateur — une boîte blanche à
   texte sombre, au milieu d'un écran sombre (trouvé au navigateur le
   31 août 2026, en vérifiant les deux thèmes). Ils reprennent ici le même
   habillage que partout ailleurs. */
.liste-durees__valeur input {
  width: 6rem;
  min-height: 40px;
  font: inherit;
  font-size: 15px;
  color: var(--text-primary);
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-control);
  padding: var(--space-2) var(--space-3);
}

.liste-durees__valeur input:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

.liste-durees__valeur .meta { white-space: nowrap; }

/* Sous 640px, la valeur passe sous son libellé : côte à côte, l'explication
   se réduisait à deux mots par ligne. */
@media (max-width: 640px) {
  .liste-durees__ligne { flex-direction: column; gap: var(--space-2); }
  .liste-durees__valeur { text-align: left; }
}

/* ==========================================================================
   Madelaine — l'étoile, la bulle
   (maquette `maquettes/ia-dans-le-produit.html`, décisions du 30 août 2026)
   ========================================================================== */

/* Sans JavaScript, l'étoile n'a rien à ouvrir : elle n'apparaît pas. Même
   règle que le pli de la barre latérale. */
.topbar-madeleine { display: none; }
.js .topbar-madeleine { display: inline-flex; }

.madeleine {
  position: fixed;
  /* Il commence **sous** ce qui est collé en haut de l'écran — la topbar,
     et le bandeau de réacceptation des conditions quand il est là. Un
     panneau posé à `top: 0` passait derrière ce bandeau (z-index 200) et
     son en-tête devenait illisible : défaut vu au navigateur, pas au test
     (CLAUDE.md §3). La hauteur est mesurée par le script, parce qu'elle
     dépend de bandeaux qui vont et viennent. */
  top: var(--madeleine-haut, 0px);
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 60;
  display: flex;
  justify-content: flex-end;
  pointer-events: none;
}

/* Le voile ne noircit pas l'écran : la bulle n'est pas une modale, on
   continue de travailler derrière. Il ne sert qu'à fermer d'un clic à
   côté — d'où un fond transparent, et `pointer-events` rendus au panneau
   seul tant qu'elle est fermée. */
.madeleine__voile {
  position: absolute;
  inset: 0;
  border: 0;
  padding: 0;
  background: transparent;
  cursor: default;
}
.madeleine--ouverte { pointer-events: auto; }

.madeleine__panneau {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 380px;
  max-width: 100%;
  margin: var(--space-3);
  background: var(--bg-surface);
  border-radius: var(--radius-modal);
  box-shadow: var(--shadow-float);
  overflow: hidden;
  /* Elle arrive, elle n'apparaît pas (DESIGN.md §7) : translation courte et
     fondu, 160 ms, et rien au-delà de 200. */
  opacity: 0;
  transform: translateX(12px) scale(0.99);
  transition: opacity 160ms ease-out, transform 160ms ease-out;
}
.madeleine--ouverte .madeleine__panneau {
  opacity: 1;
  transform: none;
}

.madeleine__entete {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border);
}
.madeleine__identite {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
}
.madeleine__nom { font-size: 14px; font-weight: 600; }
.madeleine__beta {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 2px 7px;
  border-radius: var(--radius-pill);
  background: var(--bg-inset);
  color: var(--text-secondary);
}
.madeleine__fermer { margin-left: auto; }

/* L'avatar : l'image fournie par le porteur (fleurs jaunes sur ciel),
   recadrée au carré et servie en 160 px pour les écrans denses.

   Il ne porte **aucune information** (DESIGN.md §1) : c'est un nom, pas un
   état. Et jamais un visage — un assistant se fait croire parce qu'il cite
   sa source, pas parce qu'il a une figure. Une image abstraite donne un
   endroit où revenir sans rien promettre de plus. */
.madeleine__avatar {
  width: 30px;
  height: 30px;
  flex: none;
  border-radius: var(--radius-pill);
  background-image: url("../img/madeleine.jpg");
  background-size: cover;
  background-position: center;
  box-shadow: inset 0 0 0 1px rgb(27 27 24 / 0.08);
}
.madeleine__avatar--grand { width: 44px; height: 44px; margin: 0 auto var(--space-2); }

.madeleine__fil {
  flex: 1;
  overflow-y: auto;
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  min-height: 220px;
}

/* L'état vide : l'une des quatre surfaces où le décor est permis (§2), et
   la seule bulle qui ait le droit d'être belle. */
.madeleine__accueil {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-card);
  padding: var(--space-5) var(--space-4);
  text-align: center;
  background: var(--bg-raised);
}
/* ⚠️ Le décor ne s'exprime qu'en sombre (DESIGN.md §2, arbitrage du porteur
   du 15 août 2026) : en clair, les pastels tiraient les surfaces d'accueil
   vers le fade, et la chaleur vient du canvas et de l'arrondi. La maquette
   montrait le lavis dans les deux thèmes ; c'est le code qui a raison tant
   que la règle n'est pas levée — et la question est posée au porteur
   (maquette §10, la photo de fond du hero). */
[data-theme="dark"] .madeleine__accueil::before {
  content: "";
  position: absolute;
  inset: -60% -20% auto -20%;
  height: 240%;
  background:
    radial-gradient(40% 60% at 20% 40%, var(--decor-peche) 0%, transparent 70%),
    radial-gradient(45% 60% at 80% 30%, var(--decor-ciel) 0%, transparent 72%);
  filter: blur(22px);
  pointer-events: none;
}
.madeleine__accueil > * { position: relative; }
.madeleine__bonjour { margin: 0 0 var(--space-1); font-weight: 600; }
.madeleine__intro {
  margin: 0 auto;
  max-width: 34ch;
  font-size: 13.5px;
  color: var(--text-secondary);
}

.madeleine__bulle {
  border-radius: var(--radius-card);
  padding: var(--space-3);
  font-size: 14px;
  line-height: 1.5;
}
.madeleine__bulle p { margin: 0; white-space: pre-wrap; }
.madeleine__bulle--utilisateur {
  background: var(--bg-raised);
  align-self: flex-end;
  max-width: 88%;
}
.madeleine__bulle--madeleine { padding: 0 var(--space-1); }
/* Un refus n'est pas une anomalie : ni rouge, ni bandeau d'alerte. Le fond
   sémantique neutre, et le texte dit quoi faire (DESIGN.md §1). */
.madeleine__bulle--refus {
  background: var(--info-surface);
  color: var(--info-texte);
  padding: var(--space-3);
}
.madeleine__emmener { margin-top: var(--space-3); }

/* Trois points qui respirent — jamais une barre de progression : on ne
   mesure rien (DESIGN.md §1, aucun chiffre qu'on ne sait pas tenir). */
.madeleine__bulle--attente p { display: flex; gap: 5px; padding: var(--space-2) 0; }
.madeleine__point {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--text-faint);
  animation: madeleine-respire 1.2s ease-in-out infinite;
}
.madeleine__point:nth-child(2) { animation-delay: 0.15s; }
.madeleine__point:nth-child(3) { animation-delay: 0.3s; }
@keyframes madeleine-respire {
  0%, 100% { opacity: 0.3; }
  50% { opacity: 1; }
}

.madeleine__pied {
  border-top: 1px solid var(--border);
  padding: var(--space-3) var(--space-4) var(--space-4);
  background: var(--bg-raised);
}
.madeleine__saisie {
  display: flex;
  align-items: flex-end;
  gap: var(--space-2);
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-card);
  padding: 6px 6px 6px var(--space-3);
}
.madeleine__saisie:focus-within { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.madeleine__saisie textarea {
  flex: 1;
  border: 0;
  background: transparent;
  resize: none;
  font: inherit;
  font-size: 14px;
  color: var(--text-primary);
  padding: 7px 0;
  max-height: 160px;
}
.madeleine__saisie textarea:focus { outline: none; }
.madeleine__saisie textarea::placeholder { color: var(--text-faint); }
.madeleine__envoyer { flex: none; }
.madeleine__pied--occupe .madeleine__envoyer { opacity: 0.4; pointer-events: none; }

.madeleine__mentions {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin: var(--space-3) 0 0;
  font-size: 11.5px;
  color: var(--text-tertiary);
}
.madeleine__mentions strong { color: var(--text-secondary); font-weight: 600; }
.madeleine__credits {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* Sous dix, le compteur se teinte — et c'est tout. Il ne clignote pas, il
   ne s'excuse pas : un solde qui baisse n'est pas une anomalie. */
.madeleine__credits--bas { color: var(--warning-texte); }

/* Sur téléphone, le panneau prend l'écran : 380 px de bulle sur 390 px de
   large ne laisseraient rien au reste, et il n'y a rien à regarder
   derrière. */
@media (max-width: 560px) {
  .madeleine__panneau { width: 100%; margin: 0; border-radius: 0; }
  .madeleine__fil { min-height: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .madeleine__panneau { transition: none; }
  .madeleine__point { animation: none; opacity: 0.6; }
}

/* ---------- Le hero de l'accueil (maquette §2.3) ---------- */

/* Il déborde des marges de `.contenu` pour venir toucher la topbar : c'est
   ce débord qui fait l'en-tête. Les coins du haut restent carrés, ceux du
   bas s'arrondissent — le bandeau descend et s'arrête.

   ⚠️ Il s'étend sur la colonne de contenu, pas sur toute la largeur de la
   zone de travail : `.contenu` est centrée et plafonnée. Aller plus loin
   demanderait de sortir le hero de ce conteneur — à faire le jour où la
   photo de fond arrive, si le porteur la veut bord à bord. */
.hero-accueil {
  margin: calc(var(--space-8) * -1) calc(var(--space-6) * -1) var(--space-6);
  padding: var(--space-6);
  /* La photo fournie par le porteur, sous un voile qui garde au texte son
     contraste calculé (DESIGN.md §8). Elle vit **sous le bonjour et
     jamais sous la file** : c'est la condition qui a été posée en même
     temps que la décision. */
  background:
    var(--hero-voile),
    url("../img/hero-accueil.jpg") center 62% / cover no-repeat,
    var(--bg-surface);
  border-radius: 0 0 24px 24px;
  box-shadow: var(--shadow-card);
}
.hero-accueil__contenu {
  display: flex;
  align-items: baseline;
  gap: var(--space-4);
  flex-wrap: wrap;
}
.hero-accueil__mot { flex: 1; min-width: 0; }
.hero-accueil__bonjour {
  font-size: 24px;
  font-weight: 600;
  letter-spacing: -0.02em;
  margin: 0 0 var(--space-1);
}
.hero-accueil__phrase {
  margin: 0;
  color: var(--text-secondary);
  max-width: 56ch;
}
/* Le montant en jeu se lit en chiffres alignés : c'est de l'argent. */
.hero-accueil__phrase strong {
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}
.hero-accueil__date {
  margin: 0;
  color: var(--text-tertiary);
  font-size: 12px;
  white-space: nowrap;
  text-transform: lowercase;
}

.hero-accueil__demande { display: none; }
.js .hero-accueil__demande {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: var(--space-5) 0 0;
  max-width: 460px;
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  padding: 5px 6px 5px var(--space-4);
}
.hero-accueil__demande:focus-within { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.hero-accueil__loupe { color: var(--text-tertiary); flex: none; }
.hero-accueil__demande input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: transparent;
  font: inherit;
  color: var(--text-primary);
  padding: 8px 0;
}
.hero-accueil__demande input:focus { outline: none; }
.hero-accueil__demande input::placeholder { color: var(--text-faint); }
.hero-accueil__demande .btn { flex: none; height: 34px; }

.a-traiter__titre-file { margin: 0 0 var(--space-3); }
.a-traiter__titre-file h2 { margin: 0; font-size: 16px; }
.a-traiter__titre-file .meta { margin: 2px 0 0; }

@media (max-width: 720px) {
  .hero-accueil {
    margin: calc(var(--space-6) * -1) calc(var(--space-4) * -1) var(--space-5);
    padding: var(--space-5) var(--space-4);
  }
  .hero-accueil__bonjour { font-size: 21px; }
}

/* Sur téléphone, le champ et son bouton ne tiennent pas sur une ligne : la
   pilule devient une carte et le geste passe dessous, pleine largeur. Un
   bouton comprimé à côté d'un texte tronqué se lit deux fois. */
@media (max-width: 560px) {
  .hero-accueil { background-image: none; }
  .js .hero-accueil__demande {
    flex-wrap: wrap;
    border-radius: var(--radius-card);
    padding: var(--space-2) var(--space-3) var(--space-3);
  }
  .hero-accueil__demande input { flex: 1 1 auto; }
  .hero-accueil__demande .btn { flex: 1 1 100%; justify-content: center; }
}

/* ---------- La ligne « Demander à Madelaine » sous la recherche ---------- */

.topbar-recherche { position: relative; }
.topbar-recherche__suggestion {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  background: var(--bg-surface);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-float);
  padding: var(--space-1);
  z-index: 40;
}
.topbar-recherche__demander {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  border: 0;
  background: transparent;
  font: inherit;
  font-size: 14px;
  color: var(--text-primary);
  text-align: left;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-control);
  cursor: pointer;
}
.topbar-recherche__demander:hover { background: var(--bg-raised); }
.topbar-recherche__demander .icone { color: var(--text-tertiary); flex: none; }
.topbar-recherche__demander span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }


/* Le papier ne connaît pas la photo : noir sur blanc, et rien qui grise
   une feuille (DESIGN.md §10). */
@media print {
  .hero-accueil { background: none; box-shadow: none; }
}

/* ---------- Le chiffre rendu par le produit dans la bulle ---------- */

.madeleine__resultat { margin-top: var(--space-3); }
.madeleine-chiffre {
  padding: var(--space-3) var(--space-4) var(--space-4);
  box-shadow: none;
  border: 1px solid var(--border-strong);
}
.madeleine-chiffre .chiffre-cle { font-size: 26px; margin: 2px 0 0; }
/* La ligne d'origine n'est pas une note de bas de page : c'est elle qui
   rend le chiffre défendable. Elle reste lisible (AA), séparée par un
   filet plutôt que par du gris pâle. */
.madeleine-chiffre__origine {
  margin: var(--space-3) 0 0;
  padding-top: var(--space-2);
  border-top: 1px solid var(--border);
  font-size: 12.5px;
  color: var(--text-secondary);
}
.madeleine-chiffre__lien { margin-top: var(--space-3); }

/* ---------- Partager une question restée sans réponse ---------- */

/* Discret, sous le refus : c'est une faveur qu'on demande, pas un appel à
   l'action. Un bouton primaire ici ferait pression. */
.madeleine__partager {
  margin-top: var(--space-3);
  padding: 5px var(--space-3);
  background: none;
  border: 1px solid var(--border);
  border-radius: var(--radius-pilule);
  color: var(--text-secondary);
  font-size: 12.5px;
  text-align: left;
  cursor: pointer;
}
.madeleine__partager:hover { border-color: var(--border-strong); color: var(--text); }
.madeleine__partager--faite,
.madeleine__partager:disabled { cursor: default; opacity: 0.7; }

/* ---------- Les jours précédents, repliés en haut du fil ---------- */

/* Repliés, et non affichés d'emblée : ce qu'on vient d'écrire doit rester
   la première chose qu'on voit en ouvrant. */
.madeleine__passe { margin-bottom: var(--space-4); }
.madeleine__passe-bouton {
  width: 100%;
  padding: 6px var(--space-3);
  background: none;
  border: 1px solid var(--border);
  border-radius: var(--radius-pilule);
  color: var(--text-secondary);
  font-size: 12.5px;
  cursor: pointer;
}
.madeleine__passe-bouton:hover { border-color: var(--border-strong); color: var(--text); }
/* ⚠️ Le même contexte que le fil, et pas seulement par souci de symétrie :
   `.madeleine__bulle--utilisateur` s'aligne à droite par `align-self`, qui
   ne vaut que pour un **enfant direct** d'un conteneur flex. Sans ces trois
   lignes, les questions relues d'hier partaient à gauche, comme si
   Madelaine les avait posées. Défaut vu au navigateur, pas au test. */
.madeleine__passe-contenu {
  margin-top: var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.madeleine__passe-jour {
  margin: var(--space-4) 0 var(--space-2);
  font-size: 12px;
  font-weight: 600;
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
/* Ce qui reste d'un résultat effacé : la trace qu'il a eu lieu, pas le
   chiffre — recalculé aujourd'hui, il mentirait sous la question d'hier. */
.madeleine__resultat-passe {
  margin: var(--space-2) 0 0;
  font-size: 12px;
  font-style: italic;
  color: var(--text-tertiary);
}

/* ---------- La lecture de dossier dans la bulle ---------- */

.madeleine-dossier {
  padding: var(--space-3) var(--space-4) var(--space-4);
  box-shadow: none;
  border: 1px solid var(--border-strong);
}
.madeleine-dossier__reponse { margin: 2px 0 0; font-weight: 600; }
.madeleine-dossier__lignes { margin: var(--space-2) 0 0; }
.madeleine-dossier__ligne {
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
  padding: 6px 0;
  border-top: 1px solid var(--border);
  font-size: 13px;
}
.madeleine-dossier__ligne dt { color: var(--text-secondary); }
.madeleine-dossier__ligne dd { margin: 0; text-align: right; }
/* Même rôle que la ligne d'origine du chiffre : c'est elle qui rend la
   réponse recoupable. */
.madeleine-dossier__origine {
  margin: var(--space-3) 0 0;
  padding-top: var(--space-2);
  border-top: 1px solid var(--border);
  font-size: 12.5px;
  color: var(--text-secondary);
}
.madeleine-dossier__lien { margin-top: var(--space-3); }

/* ---------- La feuille d'approbation dans la bulle (maquette §4.3) ---------- */

.feuille {
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-card);
  overflow: hidden;
  margin-top: var(--space-3);
}
.feuille__titre {
  margin: 0;
  padding: var(--space-3) var(--space-4);
  font-weight: 600;
  font-size: 14px;
  border-bottom: 1px solid var(--border);
}
.feuille__phrase {
  margin: 0;
  padding: var(--space-3) var(--space-4);
  background: var(--bg-raised);
  font-size: 13px;
  color: var(--text-secondary);
  border-bottom: 1px solid var(--border);
}
.feuille__bloc { padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--border); }
.feuille__entete {
  margin: 0 0 var(--space-2);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--text-tertiary);
}
.feuille__compte { letter-spacing: 0; text-transform: none; font-size: 11.5px; }
.feuille__ligne {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  padding: 5px 0;
  font-size: 13.5px;
  border-bottom: 1px solid var(--border);
}
.feuille__ligne:last-child { border-bottom: 0; }
/* ⚠️ `flex: 1` sur le seul libellé écrasait la ligne dès que la valeur était
   longue : « Le tarif du jour ne change pas » s'affichait un mot par ligne
   à côté de « il vient du contrat, pas de la formule retenue ». Les deux
   parts se rétrécissent maintenant ensemble, et c'est la plus longue qui
   cède le plus. Trouvé au navigateur, comme toujours pour ce genre de
   défaut — aucun test ne voit une colonne écrasée. */
.feuille__quoi { flex: 1 1 auto; min-width: 0; color: var(--text-secondary); }
/* Le « avant » barré et le « après » net : c'est la comparaison qui informe,
   et elle doit se lire sans avoir à chercher lequel des deux est le nouveau. */
.feuille__avant {
  color: var(--text-tertiary);
  text-decoration: line-through;
  font-variant-numeric: tabular-nums;
}
.feuille__apres {
  flex: 0 1 auto;
  min-width: 0;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.feuille__objet {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  flex-wrap: wrap;
  padding: 6px 0;
  font-size: 13.5px;
  border-bottom: 1px solid var(--border);
}
.feuille__objet:last-child { border-bottom: 0; }
/* Ce qui est conservé s'estompe sans disparaître : c'est le cas qui ne se
   devine pas — le repas déjà livré qui reste facturé — et le masquer
   ferait perdre l'argent en silence. */
.feuille__objet--conserve .feuille__arrive { color: var(--text-primary); font-weight: 500; }
.feuille__jour { width: 92px; flex: none; color: var(--text-secondary); }
.feuille__objet-quoi { flex: 1; min-width: 90px; }
.feuille__arrive { color: var(--text-secondary); flex-basis: 100%; padding-left: 92px; }

.etat-pastille {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 11.5px;
  padding: 2px 9px;
  border-radius: var(--radius-pill);
  white-space: nowrap;
}
.etat-pastille::before {
  content: "";
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: currentColor;
}
.etat-pastille--livree { background: var(--success-surface); color: var(--success-texte); }
.etat-pastille--produite { background: var(--warning-surface); color: var(--warning-texte); }
.etat-pastille--prevue { background: var(--bg-inset); color: var(--text-secondary); }

.feuille__inchange { margin: 0; padding-left: 18px; font-size: 13.5px; color: var(--text-secondary); }
.feuille__inchange li { margin-bottom: 4px; }

.feuille__avertissement {
  margin: 0;
  padding: var(--space-3) var(--space-4);
  background: var(--warning-surface);
  color: var(--warning-texte);
  font-size: 13px;
  border-bottom: 1px solid var(--border);
}

.feuille__pied {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  padding: var(--space-3) var(--space-4);
  background: var(--bg-raised);
}
.feuille__defaire { font-size: 12px; color: var(--text-tertiary); }
.feuille--faite { opacity: 0.55; }
.feuille--faite .feuille__pied { display: none; }

/* ===========================================================================
   La page des tarifs — formules d'abonnement
   ===========================================================================

   🔴 **Surface d'accueil, donc décor autorisé** (DESIGN.md §1) : les
   bandeaux poudrés en tête de carte sont la seule couleur non porteuse
   d'information du produit, et ils sont bornés à cette page, à la
   connexion et aux états vides. Jamais dans un tableau ni un formulaire.

   ⚠️ **Les bandeaux sont des dégradés CSS, pas des images.** Une image
   ferait une requête de plus, un fichier à servir et, si elle venait d'un
   CDN, un sous-traitant non déclaré — ce qui coûterait au produit son
   absence de bandeau cookies (`conformite-rgpd/SOUS-TRAITANTS.md`). Trois
   dégradés radiaux superposés donnent le même flou diffus, pèsent zéro
   octet et suivent le thème.
   ------------------------------------------------------------------------ */

.tarifs {
  max-width: 1060px;
  margin: 0 auto;
  padding-bottom: var(--space-12);
}

.tarifs__entete {
  text-align: center;
  max-width: 56ch;
  margin: 0 auto var(--space-8);
}

.tarifs__glyphe {
  width: 22px;
  height: 22px;
  color: var(--text-primary);
  margin-bottom: var(--space-3);
}

.tarifs__entete h1 { margin-bottom: var(--space-2); }

.tarifs__accroche {
  color: var(--text-secondary);
  margin: 0;
}

/* L'engagement : un segmented posé au centre, sous le titre. */
.tarifs__engagement {
  display: flex;
  justify-content: center;
  gap: var(--space-1);
  margin-bottom: var(--space-8);
  padding: var(--space-1);
  background: var(--bg-inset);
  border-radius: var(--radius-pill);
  width: fit-content;
  margin-inline: auto;
}

.tarifs__engagement .btn { min-height: 34px; padding-inline: var(--space-5); }
.tarifs__engagement .btn--actif { box-shadow: var(--shadow-card); background: var(--bg-surface); }

/* `auto-fit` et non un nombre de colonnes fixe : le porteur peut créer une
   quatrième formule dans le super admin sans qu'une règle CSS l'attende. */
.tarifs__grille {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--space-5);
  align-items: stretch;
}

.formule {
  display: flex;
  flex-direction: column;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-modal);
  box-shadow: var(--shadow-card);
  overflow: hidden;
}

/* La formule en cours se distingue par un trait, pas par une couleur : la
   couleur dirait « attention », alors qu'il n'y a rien à faire ici. */
.formule--actuelle { border-color: var(--border-strong); }

.formule__art {
  height: 96px;
  flex: none;
  background: var(--bg-inset);
}

/* Quatre lavis, du froid au chaud. Les couleurs sont posées en dur et non
   en tokens : ce sont des teintes de décor propres à cette page, et les
   promouvoir en tokens les rendrait disponibles là où elles sont
   interdites. */
.formule__art--1 {
  background:
    radial-gradient(75% 120% at 18% 12%, #EAD9F5 0%, transparent 62%),
    radial-gradient(85% 130% at 82% 78%, #CFE0F6 0%, transparent 66%),
    radial-gradient(60% 90% at 55% 40%, #FFFFFF 0%, transparent 70%),
    #DCE6F5;
}
.formule__art--2 {
  background:
    radial-gradient(80% 130% at 25% 78%, #FBD9CB 0%, transparent 64%),
    radial-gradient(75% 115% at 75% 18%, #FCE9B8 0%, transparent 62%),
    radial-gradient(60% 90% at 50% 55%, #FFFFFF 0%, transparent 68%),
    #F7E3D4;
}
.formule__art--3 {
  background:
    radial-gradient(80% 125% at 20% 25%, #D7EBDC 0%, transparent 64%),
    radial-gradient(80% 120% at 80% 80%, #CDE4EE 0%, transparent 64%),
    radial-gradient(58% 88% at 52% 48%, #FFFFFF 0%, transparent 70%),
    #E1EEE7;
}
.formule__art--4 {
  background:
    radial-gradient(78% 120% at 80% 20%, #F6D5E2 0%, transparent 62%),
    radial-gradient(82% 125% at 18% 82%, #DAD8F6 0%, transparent 64%),
    radial-gradient(58% 90% at 48% 50%, #FFFFFF 0%, transparent 70%),
    #EEE2F1;
}

/* En sombre, les mêmes lavis mais sourds : les pastels clairs y feraient
   quatre rectangles lumineux qui écraseraient le texte des cartes. */
[data-theme="dark"] .formule__art--1 {
  background:
    radial-gradient(75% 120% at 18% 12%, #3A3350 0%, transparent 62%),
    radial-gradient(85% 130% at 82% 78%, #23364E 0%, transparent 66%),
    #202433;
}
[data-theme="dark"] .formule__art--2 {
  background:
    radial-gradient(80% 130% at 25% 78%, #4A3226 0%, transparent 64%),
    radial-gradient(75% 115% at 75% 18%, #4A3D1E 0%, transparent 62%),
    #33281F;
}
[data-theme="dark"] .formule__art--3 {
  background:
    radial-gradient(80% 125% at 20% 25%, #263A2C 0%, transparent 64%),
    radial-gradient(80% 120% at 80% 80%, #1F3540 0%, transparent 64%),
    #232B21;
}
[data-theme="dark"] .formule__art--4 {
  background:
    radial-gradient(78% 120% at 80% 20%, #46293A 0%, transparent 62%),
    radial-gradient(82% 125% at 18% 82%, #2E2B4C 0%, transparent 64%),
    #322A3C;
}

.formule__corps {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: var(--space-5);
  gap: var(--space-3);
}

.formule__ligne-titre {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

.formule__nom {
  font-size: 19px;
  font-weight: 600;
  margin: 0;
}

/* La pastille de prix : le chiffre est l'information de la carte, il porte
   donc le seul fond coloré — et seulement sur la formule mise en avant. */
.formule__prix {
  flex: none;
  font-size: 13px;
  font-weight: 600;
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-pill);
  background: var(--bg-inset);
  color: var(--text-primary);
  white-space: nowrap;
}

.formule__prix--phare { background: var(--info-surface); color: var(--info-texte); }
.formule__prix--muet { background: transparent; color: var(--text-tertiary); font-weight: 500; }

.formule__periode { font-weight: 500; opacity: 0.75; }

.formule__resume {
  color: var(--text-secondary);
  margin: 0;
  font-size: 14px;
}

.formule__libelle-liste {
  margin: var(--space-2) 0 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-primary);
}

.formule__liste {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  font-size: 14px;
  color: var(--text-secondary);
}

.formule__liste li {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
}

.formule__liste .icone {
  width: 16px;
  height: 16px;
  flex: none;
  margin-top: 2px;
  color: var(--text-tertiary);
}

/* `margin-top: auto` colle le pied en bas quelle que soit la longueur de la
   liste : sans cela, trois cartes aux listes inégales auraient leurs
   boutons à trois hauteurs différentes. */
.formule__pied { margin-top: auto; padding-top: var(--space-4); }

.formule__bouton { width: 100%; }
.formule__pied form { margin: 0; }

.formule__actuelle-mention {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  margin: 0;
  min-height: 40px;
  font-size: 14px;
  font-weight: 500;
  color: var(--success-texte);
}

.formule__actuelle-mention .icone { width: 16px; height: 16px; }

.tarifs__pied {
  margin-top: var(--space-8);
  text-align: center;
  max-width: 68ch;
  margin-inline: auto;
}

.tarifs__pied .meta { margin-bottom: var(--space-2); }

@media (max-width: 640px) {
  .tarifs__grille { grid-template-columns: 1fr; }
  .formule__art { height: 76px; }
}

/* Une formule trop petite pour la structure : la carte reste lisible et
   entière — la cacher ferait croire que le porteur n'a que deux formules —
   mais son bandeau se retire, et le motif prend la place du bouton. */
.formule--hors-atteinte .formule__art { filter: saturate(0.25); opacity: 0.6; }

.formule__blocage {
  margin: 0;
  font-size: 13px;
  line-height: 1.45;
  color: var(--warning-texte);
  background: var(--warning-surface);
  padding: var(--space-3);
  border-radius: var(--radius-control);
}

/* Le report d'une rétrogradation : un avertissement, pas un refus — il
   accompagne un bouton qui marche, contrairement à `.formule__blocage` qui
   en tient lieu. D'où le ton neutre plutôt que l'ambre. */
.formule__report {
  margin: 0 0 var(--space-3);
  font-size: 13px;
  line-height: 1.45;
  color: var(--text-secondary);
  background: var(--bg-inset);
  padding: var(--space-3);
  border-radius: var(--radius-control);
}

/* ==========================================================================
   Premiers pas — la mise en service guidée (validée le 31 août 2026)

   Deux surfaces, une même synthèse (apps/core/premiers_pas.py) : le
   panneau flottant de la barre latérale porte la liste, la carte du jour
   d'« À traiter » porte l'étape courante. Les deux disparaissent quand
   les sept étapes sont faites — le gabarit ne les rend plus.
   ========================================================================== */

/* Le panneau, épinglé sous la navigation. Il flotte (ombre --shadow-float)
   mais garde un filet : blanc sur la barre blanche, l'ombre seule ne
   suffirait pas à dire où il commence — la leçon de la pilule active
   (DESIGN.md §5.1).

   `position: sticky` parce que la page défile en entier (la barre n'est
   pas sticky) : sans lui, le panneau vivrait au pied d'une colonne plus
   haute que l'écran — sous le pli, il ne guiderait personne. Sous
   800 px, la barre devient un tiroir qui défile pour lui-même : le
   panneau y reprend sa place dans le flux. */
.premiers-pas {
  margin-top: auto;
  position: sticky;
  bottom: var(--space-3);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-float);
  padding: var(--space-2) var(--space-3) var(--space-3);
}

@media (max-width: 800px) {
  .premiers-pas { position: static; }
}

.premiers-pas__tete {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-2) 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

/* L'anneau de progression : un `conic-gradient` piloté par un angle
   **entier** posé en variable par le gabarit — jamais un nombre formaté
   par la localisation, qui écrirait « 102,9deg » et casserait la règle
   sans erreur (DESIGN.md §6, graphiques). Le vert dit « accompli »,
   comme partout (§1). */
.premiers-pas__anneau {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  border-radius: var(--radius-pill);
  background: conic-gradient(var(--success) var(--premiers-pas-angle, 0deg), var(--bg-inset) 0deg);
  -webkit-mask: radial-gradient(circle, transparent 8px, #000 8.5px);
  mask: radial-gradient(circle, transparent 8px, #000 8.5px);
}

.premiers-pas__mot {
  flex: 1;
  min-width: 0;
  display: grid;
  gap: 1px;
}

.premiers-pas__mot strong {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.premiers-pas__mot small {
  font-size: 12px;
  color: var(--text-tertiary);
  font-variant-numeric: tabular-nums;
}

.premiers-pas__chevron {
  width: 16px;
  height: 16px;
  color: var(--text-tertiary);
  transition: transform 140ms ease-out;
}

.premiers-pas--replie .premiers-pas__chevron { transform: rotate(-90deg); }
.premiers-pas--replie .premiers-pas__detail { display: none; }

.premiers-pas__liste {
  list-style: none;
  margin: var(--space-1) 0 0;
  padding: 0;
}

.premiers-pas__etape {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 5px var(--space-2);
  margin: 0 calc(-1 * var(--space-2));
  border-radius: var(--radius-pill);
  font-size: 13px;
  color: var(--text-secondary);
  text-decoration: none;
  transition: background 140ms ease-out;
}

.premiers-pas__etape:hover { background: var(--bg-raised); }

/* L'étape courante se creuse vers la page, comme la pilule active de la
   navigation : une surface blanche disparaîtrait sur le panneau blanc. */
.premiers-pas__etape--courante {
  background: var(--bg-app);
  color: var(--text-primary);
  font-weight: 600;
}

/* Le rond d'une étape franchie change d'habit, le titre jamais — la même
   règle que le numéro du composant `.etape`. La coche est dessinée en
   CSS : elle suit le thème, quand une image en dur resterait d'une seule
   couleur (DESIGN.md §6). */
.premiers-pas__rond {
  position: relative;
  flex-shrink: 0;
  width: 14px;
  height: 14px;
  border-radius: var(--radius-pill);
  border: 1.5px solid var(--border-strong);
}

.premiers-pas__etape--faite { color: var(--text-tertiary); }

.premiers-pas__etape--faite .premiers-pas__rond {
  border-color: transparent;
  background: var(--success-surface);
}

.premiers-pas__etape--faite .premiers-pas__rond::after {
  content: "";
  position: absolute;
  left: 4px;
  top: 2px;
  width: 4px;
  height: 7px;
  border-right: 1.5px solid var(--success-texte);
  border-bottom: 1.5px solid var(--success-texte);
  transform: rotate(45deg);
}

/* La ligne d'étape et sa case à cocher (31 août 2026). La case ne se
   montre qu'au survol ou au clavier : le panneau reste une liste qu'on
   lit, pas une grille de boutons — mais le geste doit rester atteignable
   au doigt, donc elle reste visible sur les écrans sans survol. */
.premiers-pas__ligne {
  display: flex;
  align-items: center;
  gap: var(--space-1);
}

.premiers-pas__ligne .premiers-pas__etape { flex: 1; min-width: 0; }

.premiers-pas__cocher { display: flex; }

.premiers-pas__cocher button {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--text-tertiary);
  cursor: pointer;
  opacity: 0;
  transition: opacity 140ms ease-out, color 140ms ease-out, background 140ms ease-out;
}

.premiers-pas__cocher .icone { width: 15px; height: 15px; }

.premiers-pas__ligne:hover .premiers-pas__cocher button,
.premiers-pas__cocher button:focus-visible { opacity: 1; }

.premiers-pas__cocher button:hover {
  background: var(--bg-raised);
  color: var(--text-primary);
}

/* Une étape cochée à la main garde sa case allumée : c'est le seul
   endroit qui distingue « mesuré » de « déclaré », et l'effacer ferait
   du panneau un affichage qui ne dit pas d'où vient son vert. */
.premiers-pas__etape--faite + .premiers-pas__cocher button {
  opacity: 1;
  color: var(--success-texte);
}

@media (hover: none) {
  .premiers-pas__cocher button { opacity: 1; }
}

/* « Passer la mise en service » : un lien de texte, pas un bouton — le
   geste est offert, jamais proposé. */
.premiers-pas__passer { margin: var(--space-2) 0 0; }

.premiers-pas__passer button {
  padding: 0;
  border: 0;
  background: none;
  font-size: 12px;
  color: var(--text-tertiary);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}

.premiers-pas__passer button:hover { color: var(--text-secondary); }

/* La ligne calme de la démonstration : une information, pas une alerte
   (DESIGN.md §1) — des personnes fictives ne doivent jamais approcher
   une vraie feuille de tournée. */
.premiers-pas__demo {
  margin: var(--space-2) 0 0;
  padding-top: var(--space-2);
  border-top: 1px solid var(--border);
  font-size: 12px;
  line-height: 1.45;
  color: var(--text-tertiary);
}

.premiers-pas__demo a { color: inherit; }

/* La carte du jour, sur « À traiter » : l'image en bandeau, le titre de
   l'étape courante, le pourquoi, un seul geste — le seul bouton noir de
   l'écran (DESIGN.md §14 Q1). La marge du bas la sépare du titre de la
   file : deux grands groupes, 32 px (DESIGN.md §4). */
.premiers-pas-carte {
  padding: 0;
  overflow: hidden;
  margin-bottom: var(--space-8);
}

/* Le dégradé flou de la maquette validée (choix du porteur, 31 août
   2026) : l'image par défaut du parcours, servie en bandeau sur la carte
   du jour et en panneau sur l'écran d'arrivée — une seule déclaration,
   deux tailles. C'est une image au sens strict : décorative, couleurs
   figées dans les deux thèmes, comme le serait une photographie ; aucune
   information ne vit dedans. */
.degrade-premiers-pas {
  position: relative;
  overflow: hidden;
  background: #C4AE97;
}

.degrade-premiers-pas::before {
  content: "";
  position: absolute;
  inset: -22%;
  filter: blur(26px) saturate(1.05);
  background:
    radial-gradient(45% 60% at 18% 60%, #23345C 0 42%, transparent 70%),
    radial-gradient(26% 30% at 68% 26%, #F3E6D8 0 40%, transparent 72%),
    radial-gradient(34% 30% at 78% 54%, #D9C7B4 0 45%, transparent 75%),
    radial-gradient(22% 18% at 52% 60%, #C96F3F 0 35%, transparent 70%),
    radial-gradient(28% 24% at 8% 16%, #9DB6D8 0 40%, transparent 75%),
    radial-gradient(26% 20% at 12% 88%, #7E9C6B 0 40%, transparent 75%),
    linear-gradient(160deg, #D8C6B2, #C4AE97);
}

.premiers-pas-carte__image { height: 132px; }

/* L'écran d'arrivée (premiers_pas_arrivee.html) : le gabarit divisé de
   la connexion, le dégradé à la place de la photo. */
.arrivee__degrade {
  height: 100%;
  min-height: 320px;
  border-radius: var(--radius-modal);
}

.arrivee .cartes-choix { margin-top: var(--space-5); }

.arrivee__actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-top: var(--space-6);
}

.premiers-pas-carte__corps { padding: var(--space-4) var(--space-5) var(--space-5); }

.premiers-pas-carte__fil {
  margin: 0 0 var(--space-1);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--text-tertiary);
  font-variant-numeric: tabular-nums;
}

.premiers-pas-carte__detail {
  margin: var(--space-1) 0 var(--space-4);
  max-width: 60ch;
  font-size: 14px;
  color: var(--text-secondary);
}

.premiers-pas-carte__actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-2);
  flex-wrap: wrap;
}

/* « Poser les contrats » — la saisie en série (Premiers pas, lot 3).
   Chaque ligne est son propre formulaire : la grille aligne le qui, le
   quoi et le geste ; les lignes se séparent d'un filet, jamais de cartes
   dans la carte. */
.serie { padding-top: 0; padding-bottom: 0; }

.serie__ligne {
  display: grid;
  grid-template-columns: 24px 200px minmax(0, 1fr) auto;
  gap: var(--space-4);
  align-items: start;
  padding: var(--space-4) 0;
  border-top: 1px solid var(--border);
}

/* La case du lot s'aligne sur le nom ; le carré natif reste la règle
   dans une ligne de tableau (DESIGN.md §6). */
.serie__selection { padding-top: 2px; }
.serie__selection input { width: 18px; height: 18px; accent-color: var(--accent); }

/* « Tout sélectionner » : une ligne discrète en tête de la file. */
.serie__tout {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) 0 var(--space-1);
  cursor: pointer;
}
.serie__tout input { width: 16px; height: 16px; accent-color: var(--accent); }

/* Le contrat modèle : mêmes champs que la ligne, avec leurs libellés
   visibles — c'est ici qu'on compose, les lignes déclinent. La barre
   d'actions n'apparaît qu'avec une sélection (selection_lot.js). */
.modele { margin-bottom: var(--space-5); }
.modele__entete { margin-bottom: var(--space-3); }
.modele__entete h2 { margin: 0 0 2px; font-size: 16px; }
.modele__entete .meta { margin: 0; max-width: 68ch; }
.modele__actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-4);
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
}

.serie__ligne:first-child { border-top: none; }

.serie__qui { display: grid; gap: 2px; min-width: 0; }

.serie__nom {
  font-weight: 600;
  color: var(--text-primary);
  text-decoration: none;
}

.serie__nom:hover { text-decoration: underline; }

.serie__reglages {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-3);
}

/* Les champs d'une ligne n'empilent pas la marge des formulaires pleine
   page : la ligne est déjà aérée par son propre padding. */
.serie__champ { margin-bottom: 0; }
.serie__champ--formule { flex: 1 1 200px; }
.serie__champ--formule select { width: 100%; }
.serie__champ--tarif input { width: 110px; }
.serie__jours { margin: var(--space-2) 0 0; }

.serie__geste {
  display: grid;
  gap: var(--space-1);
  justify-items: end;
  text-align: right;
}

.serie__aide { font-size: 12px; }

/* La ligne posée : le fait accompli en une phrase calme — le rond coché
   reprend l'habit des étapes franchies du panneau. */
.serie__ligne--faite { grid-template-columns: 24px 1fr; color: var(--text-secondary); }
.serie__ligne--faite .serie__coche { grid-column: 1; }

.serie__fait { margin: 0; font-size: 14px; }
.serie__fait a { color: inherit; }

.serie__coche {
  position: relative;
  width: 18px;
  height: 18px;
  margin-top: 2px;
  border-radius: var(--radius-pill);
  background: var(--success-surface);
}

.serie__coche::after {
  content: "";
  position: absolute;
  left: 6px;
  top: 3px;
  width: 5px;
  height: 9px;
  border-right: 1.5px solid var(--success-texte);
  border-bottom: 1.5px solid var(--success-texte);
  transform: rotate(45deg);
}

@media (max-width: 800px) {
  .serie__ligne { grid-template-columns: 1fr; }
  .serie__geste { justify-items: start; text-align: left; }
}

/* La bande de fin d'import : le geste suivant, là où l'import se
   termine. Un avertissement qui accompagne un bouton — pas une anomalie,
   rien n'a échoué. */
.import-suite {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-top: var(--space-4);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-control);
  background: var(--warning-surface);
}

.import-suite p {
  margin: 0;
  font-size: 14px;
  line-height: 1.45;
  color: var(--warning-texte);
}

/* Le guide d'utilisation (/aide/) — la typographie de lecture des
   chapitres rendus par apps/core/aide.py. */
.aide { max-width: 880px; }
.aide h1 { font-size: 24px; font-weight: 600; letter-spacing: -0.02em; margin: 0 0 var(--space-3); }
.aide h2 { font-size: 17px; font-weight: 600; letter-spacing: -0.01em; margin: var(--space-6) 0 var(--space-2); }
.aide h3 { font-size: 15px; font-weight: 600; margin: var(--space-4) 0 var(--space-2); }
.aide p, .aide li { max-width: 72ch; }
.aide p { margin: 0 0 var(--space-3); }
.aide ul, .aide ol { margin: 0 0 var(--space-3); padding-left: 22px; }
.aide li { margin-bottom: 4px; }

.aide img {
  display: block;
  max-width: 100%;
  margin: var(--space-3) 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
}

.aide table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
  margin: var(--space-3) 0 var(--space-4);
}
.aide th, .aide td {
  text-align: left;
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}
.aide th {
  font-size: 11.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-secondary);
}

.aide blockquote {
  margin: var(--space-3) 0;
  padding: var(--space-2) var(--space-3);
  border-left: 3px solid var(--border-strong);
  color: var(--text-secondary);
}
.aide blockquote p { margin: 0; }

.aide pre {
  margin: var(--space-3) 0;
  padding: var(--space-3);
  background: var(--bg-inset);
  border-radius: var(--radius-control);
  overflow-x: auto;
  font-size: 13px;
  line-height: 1.5;
}
.aide code {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.92em;
  background: var(--bg-inset);
  padding: 1px 5px;
  border-radius: 6px;
}
.aide pre code { background: none; padding: 0; }

/* La modale de fin des Premiers pas — la célébration que DESIGN.md §6
   prévoyait. L'en-tête porte le décor poudré, en sombre seulement (§2) ;
   en clair, il reste un simple bandeau chaud. */
.celebration {
  border: 0;
  padding: 0;
  max-width: 460px;
  width: calc(100% - 2 * var(--space-4));
  border-radius: var(--radius-modal);
  background: var(--bg-surface);
  color: var(--text-primary);
  box-shadow: var(--shadow-float);
  overflow: hidden;
}

.celebration::backdrop { background: rgb(23 20 14 / 0.45); }

.celebration__entete { height: 92px; background: var(--bg-raised); }

[data-theme="dark"] .celebration__entete {
  background:
    radial-gradient(90% 140% at 12% 0%, var(--decor-peche) 0%, transparent 55%),
    radial-gradient(90% 140% at 90% 100%, var(--decor-ciel) 0%, transparent 55%),
    var(--bg-raised);
}

.celebration__corps { padding: var(--space-5) var(--space-6) var(--space-6); }
.celebration__corps h2 { margin: 0 0 var(--space-2); font-size: 20px; font-weight: 600; letter-spacing: -0.015em; }
.celebration__corps p { margin: 0 0 var(--space-3); }

.celebration__actions {
  display: flex;
  justify-content: flex-end;
  margin: var(--space-4) 0 0;
}
