/* =========================================================================
   Oikos — feuille de style
   Charte : docs/DESIGN.md, validée le 13.09.2026 — la terre cuite du dossier
   investisseurs de l'écosystème, la colonne ionique (docs/MOTIF-COLONNE.md).
   L'écran de travail est neutre ; la marque vit dans le cadre : une teinte de
   résine, un mur, un lot de carreaux se jugent sur un fond neutre.
   Séparation par bordures et contraste de fond. Deux graisses d'Inter : 400
   et 500 ; Marcellus pour le nom et les grands titres de la vitrine.
   ========================================================================= */

/* --- Polices embarquées localement (aucun appel CDN, CSP font-src 'self') - */
@font-face {
  font-family: "Inter";
  src: url("/static/fonts/inter-variable.woff2") format("woff2");
  font-weight: 100 900;
  font-display: swap;
}
@font-face {
  font-family: "Marcellus";
  src: url("/static/fonts/marcellus.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}

/* --- Variables de référence (docs/DESIGN.md) — aucune couleur en dur ----- */
:root {
  /* Identité — la terre cuite d'Oikos (docs/DESIGN.md § 2). --maison ne porte
     jamais un texte courant (3.5:1) : signe, filets, motif, grands titres. */
  --maison:          #BA7A45;
  --primaire:        #9A5F30;   /* blanc dessus 5.2:1 */
  --primaire-fonce:  #7F4D26;   /* blanc dessus 7.0:1 */
  --primaire-tint:   #F8F0E8;

  /* Moments — le profond et la pierre. Jamais le quotidien. Les anciens noms
     du socle (--vert-profond, --or, --sur-vert) restent des alias : les
     gabarits repris les lisent, et les renommer ici ne changerait qu'un mot. */
  --profond:         #3A2418;
  --pierre:          #E9DFD2;
  --vert-profond:    var(--profond);
  --or:              var(--maison);
  --filet-or:        rgba(186, 122, 69, .55);
  --filet-or-faible: rgba(186, 122, 69, .22);
  --sur-vert:        var(--pierre);

  /* Quotidien — le blanc galerie, ni crème ni gris. */
  --encre:           #1A1D1B;   /* 15.1:1 sur surface */
  --encre-douce:     #5B5852;   /*  7.1:1 */
  --encre-attenuee:  #6E6B64;   /*  5.3:1 — remonté de #8B948F (2.9:1, échouait AA) */

  --bordure:         #E6E3DC;
  --fond:            #FBFAF8;
  --surface:         #FFFFFF;

  /* Doré sur fond clair : rare, réservé aux valeurs et aux certifications.
     --accent est la valeur de la charte, pour les filets et les marques ;
     --accent-texte est sa version lisible, obligatoire dès qu'il porte du
     texte (#A8792B ne tient que 3.4:1 sur son propre fond teinté). */
  --accent:          #BA7A45;   /* la terre cuite, pour les filets et le signe */
  --accent-texte:    #7F4D26;   /* 7.0:1 sur surface : obligatoire dès qu'il porte du texte */
  --accent-tint:     #F8F0E8;

  /* Sémantique — la charte n'impose rien ici, elle encadre l'identité.
     Valeurs choisies pour tenir 4.5:1 sur leur fond teinté. */
  --succes:          #1D7A4F;
  --succes-tint:     #EAF5EF;
  --avert:           #9C4E0C;
  --avert-tint:      #FDF2E6;
  --danger:          #A3312B;
  --danger-tint:     #FBECEC;
  --info:            #2C5F82;
  --info-tint:       #EAF1F6;

  --police:          "Inter", -apple-system, "Segoe UI", sans-serif;
  --police-marque:   "Marcellus", "Playfair Display", Georgia, serif;

  --rayon:           4px;

  /* Case à cocher, bouton radio, barre de progression : sans cette ligne, le
     navigateur les peint dans l'accent du SYSTÈME — bleu Windows sur un écran
     qui n'a pas une seule autre touche de bleu. */
  accent-color:      #9A5F30;
  --ligne-tableau:   36px;
  --barre-laterale:  220px;
  --bandeau:         48px;

  /* Mouvement — strictement fonctionnel : il lève une ambiguïté, il ne
     décore pas. Toute animation est désactivée sous prefers-reduced-motion. */
  --duree-court:     120ms;
  --duree-moyen:     180ms;
  --duree-long:      260ms;
  --sortie:          cubic-bezier(.22, 1, .36, 1);
  --entree:          cubic-bezier(.4, 0, .2, 1);
}

/* Aucune ombre portée n'est définie : c'est volontaire (DESIGN.md).
   Le seul relief autorisé est le trait de base d'un cadre de moment fort. */

* { box-sizing: border-box; }

html, body {
  margin: 0; padding: 0;
  font-family: var(--police);
  font-size: 14px;
  font-weight: 400;
  color: var(--encre);
  background: var(--fond);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 { font-weight: 500; color: var(--encre); margin: 0 0 .5em; }
a { color: var(--primaire); text-decoration: none;
    transition: color var(--duree-court) var(--entree); }
a:hover { color: var(--primaire-fonce); text-decoration: underline; text-underline-offset: 2px; }
p { margin: 0 0 .8em; }

/* --- Surfaces que nous n'avons pas dessinées, mais qui portent la charte -- */
::selection { background: var(--primaire-tint); color: var(--primaire-fonce); }
input, textarea, select { caret-color: var(--primaire); }
* { scrollbar-width: thin; scrollbar-color: #CFCCC4 transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-thumb { background: #CFCCC4; border: 3px solid transparent; background-clip: content-box; border-radius: 999px; }
*::-webkit-scrollbar-thumb { transition: background-color var(--duree-court) var(--entree); }
*::-webkit-scrollbar-thumb:hover { background: #B4B0A6; background-clip: content-box; }
*::-webkit-scrollbar-track { background: transparent; }

/* --- Focus clavier : un opérateur passe six heures par jour ici ----------- */
:focus-visible {
  outline: 2px solid var(--primaire);
  outline-offset: 2px;
  border-radius: 2px;
}
.moment :focus-visible { outline-color: var(--primaire); }

/* Lien d'évitement : premier élément focalisable de chaque page d'app.
   Position fixe et translation, pour qu'il apparaisse au même endroit quel que
   soit le défilement de la page. */
.evitement {
  position: fixed; left: 8px; top: 8px; z-index: 100;
  background: var(--surface); border: 1px solid var(--primaire);
  color: var(--primaire); font-weight: 500;
  padding: 8px 14px; border-radius: var(--rayon);
  transform: translateY(calc(-100% - 16px));
  transition: transform var(--duree-court) var(--entree);
}
.evitement:focus { transform: none; text-decoration: none; }

/* Chiffres tabulaires : tout montant, nombre, DATE et numéro. Sans
   cela les colonnes ne s'alignent pas et l'outil a l'air amateur. */
.tabulaire, td.montant, td.poids, th.montant, th.poids {
  font-variant-numeric: tabular-nums;
  text-align: right;
}
td.date, .compteur-jours, .faits dd, .valeur, .total-bloc, kbd, .pagination {
  font-variant-numeric: tabular-nums;
}

/* --- Marque -------------------------------------------------------------- */
/* Marcellus est en réserve absolue, et la réserve est étroite : le wordmark,
   l'écran de connexion, la référence du passeport public. Rien d'autre.
   Les surfaces de travail — indicateurs, totaux, tableaux, formulaires —
   restent en Inter : un chiffre qu'on lit cent fois par jour se lit mieux en
   linéale, et l'outil doit rester fluide avant d'être signé.
   Jamais dans une liste, jamais en italique. */
.marque { font-family: var(--police-marque); font-weight: 400; }

/* Wordmark complet : picto à gauche, gap 12px, OIKOS en capitales espacées. */
.wordmark { display: inline-flex; align-items: center; gap: 10px; text-decoration: none; }
.wordmark .picto { width: 26px; height: 26px; flex: none; color: var(--maison); }
.wordmark .lettres {
  font-family: var(--police-marque); font-weight: 400;
  font-size: 1.2rem; letter-spacing: .2em; text-indent: .2em;
  color: var(--maison); line-height: 1; text-transform: uppercase;
}
.wordmark:hover { text-decoration: none; }
.wordmark .lettres { transition: color var(--duree-court) var(--entree); }
.wordmark:hover .lettres { color: var(--primaire-fonce); }

/* Légendes et labels techniques : Inter 500, 12px, capitales espacées. */
.legende {
  font-size: 12px; font-weight: 500; letter-spacing: .14em;
  text-transform: uppercase; color: var(--encre-douce);
}
/* Surtitre sur fond sombre : même dessin, plus espacé, doré. */
.moment .legende { letter-spacing: .2em; color: var(--accent-texte); }

/* ======================= Ossature ======================================== */
.app {
  display: grid;
  grid-template-columns: var(--barre-laterale) 1fr;
  grid-template-rows: var(--bandeau) 1fr;
  grid-template-areas: "laterale bandeau" "laterale contenu";
  min-height: 100vh;
}

/* --- Barre latérale ------------------------------------------------------ */
.laterale {
  grid-area: laterale;
  background: var(--surface);
  border-right: 1px solid var(--bordure);
  display: flex; flex-direction: column;
  /* Le défilement appartient au MENU, pas à la colonne entière.
     Auparavant la colonne entière défilait : sur une fenêtre d'environ 800 px
     de haut, le pied — Paramètres et Utilisateurs — passait sous le bord et
     n'était atteignable qu'en faisant défiler la barre elle-même. Rien ne le
     signalait, et « le module Utilisateurs a disparu » a été rapporté trois
     fois. Le pied reste désormais posé en bas de l'écran quelle que soit la
     hauteur ; seules les entrées du milieu défilent. */
  position: sticky; top: 0; height: 100vh; overflow: hidden;
}
.laterale-logo { display: block; padding: 13px 16px 12px; flex: none; }
.laterale-menu {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 4px 0 10px;
  /* Le défilement du menu ne se propage pas à la page : arrivé en bas de la
     liste, un cran de plus faisait sauter la page entière. Et la gouttière est
     réservée, sinon les libellés se décalent de la largeur de l'ascenseur
     entre un menu qui déborde et un menu qui tient. */
  overscroll-behavior: contain; scrollbar-gutter: stable; scrollbar-width: thin;
}
.laterale-menu::-webkit-scrollbar { width: 8px; }
.laterale-menu::-webkit-scrollbar-thumb { background: var(--bordure); border-radius: 4px; }
.laterale-menu:hover::-webkit-scrollbar-thumb { background: var(--encre-douce); }
.laterale-pied {
  flex: none;
  border-top: 1px solid var(--bordure); padding: 4px 0 10px;
}

/* Les douze entrées sont regroupées : un opérateur cherche « le flux
   commercial », pas la neuvième ligne d'une liste plate. */
.laterale-groupe { padding: 12px 16px 5px; }
.laterale-groupe:first-child { padding-top: 6px; }

.laterale a.entree {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 16px; color: var(--encre-douce);
  border-left: 3px solid transparent; text-decoration: none;
  /* `background-color`, jamais `background` : la barre porte le motif Veine
     d'Or en image de fond, et le raccourci l'emporterait dans la transition.
     La matière ne bouge pas (MOTIF-VEINE-DOR.md règle 4). */
  transition: background-color var(--duree-court) var(--entree),
              color var(--duree-court) var(--entree),
              border-left-color var(--duree-court) var(--entree);
}
.laterale a.entree:hover { background: var(--fond); color: var(--encre); }
.laterale a.entree.active {
  background: var(--primaire-tint);
  border-left-color: var(--primaire);
  color: var(--primaire); font-weight: 500;
}
.laterale a.entree:focus-visible { outline-offset: -2px; }
.laterale .icone { width: 16px; height: 16px; flex: none; stroke-width: 1.5; }

/* --- Bandeau supérieur --------------------------------------------------- */
.bandeau {
  grid-area: bandeau;
  height: var(--bandeau);
  background: var(--surface);
  border-bottom: 1px solid var(--bordure);
  display: flex; align-items: center; gap: 16px;
  padding: 0 20px;
  position: sticky; top: 0; z-index: 40;
  /* Sans `min-width: 0`, la piste `1fr` de la grille prend la largeur
     MINIMALE du bandeau — nom, « Changer d'espace », « Déconnexion » en une
     rangée — et toute la page suit : mesuré à 505 px sur un écran de 390. */
  min-width: 0;
}
.fil-ariane { display: flex; align-items: center; gap: 6px; color: var(--encre-attenuee); white-space: nowrap; }
/* À toute largeur, le fil cède avant le compte : « Carrelage / Immeuble
   Clos-Fleuri — 24 logements / Appartement 1.2 · Salle de bains, sol » poussait
   « Déconnexion » hors du cadre à 1024 px (banc du 16.09.2026). La page courante
   se coupe la première : son titre est déjà l'en-tête de la fiche. */
.fil-ariane { flex: 0 1 auto; min-width: 0; }
.fil-ariane > a, .fil-ariane .courant { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.fil-ariane .courant { flex-shrink: 4; }
.fil-ariane a { color: var(--encre-douce); }
.fil-ariane .courant { color: var(--encre); font-weight: 500; }
.recherche-globale { flex: 1; max-width: 420px; margin: 0 auto; position: relative; }
.recherche-globale input {
  width: 100%; height: 30px; padding: 0 62px 0 30px;
  border: 1px solid var(--bordure); border-radius: var(--rayon);
  background: var(--fond); font: inherit; color: var(--encre);
}
.recherche-globale input::placeholder { color: var(--encre-attenuee); }
.recherche-globale .icone {
  position: absolute; left: 8px; top: 7px; width: 16px; height: 16px;
  color: var(--encre-attenuee); pointer-events: none;
}
.recherche-globale kbd {
  position: absolute; right: 8px; top: 6px; pointer-events: none;
  font: 500 11px var(--police); color: var(--encre-attenuee);
  border: 1px solid var(--bordure); border-radius: 3px; padding: 0 4px;
  background: var(--surface);
}
.bandeau-compte { margin-left: auto; display: flex; align-items: center; gap: 10px; color: var(--encre-douce); }

/* --- Zone de contenu ----------------------------------------------------- */
.contenu { grid-area: contenu; padding: 20px; min-width: 0; }

/* ======================= Vue liste ======================================= */
.barre-actions {
  display: flex; align-items: center; gap: 10px; margin-bottom: 12px;
  flex-wrap: wrap;
}
.barre-actions .compteur { margin-left: auto; color: var(--encre-attenuee); }

.carte {
  background: var(--surface); border: 1px solid var(--bordure);
  border-radius: var(--rayon);
}
/* En-tête de carte : la légende technique, pas un titre de plus. */
.carte-entete {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 16px; border-bottom: 1px solid var(--bordure);
}
.carte-entete h2 { margin: 0; font-size: 12px; font-weight: 500; letter-spacing: .14em; text-transform: uppercase; color: var(--encre-douce); }
.carte-entete .appoint { margin-left: auto; color: var(--encre-attenuee); font-size: 12.5px; }

table.liste { width: 100%; border-collapse: collapse; background: var(--surface); }
table.liste th, table.liste td {
  height: var(--ligne-tableau);
  padding: 8px 12px;
  border-bottom: 1px solid var(--bordure);
  text-align: left; vertical-align: middle;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
table.liste th {
  font-size: 12px; font-weight: 500; letter-spacing: .06em;
  text-transform: uppercase; color: var(--encre-attenuee);
  background: var(--surface);
}
/* `.prose` : la cellule qui porte une PHRASE, et non une référence, une date
   ou un montant. Le `nowrap` ci-dessus vaut pour une liste — il devient faux
   dès qu'une cellule contient du texte suivi, et personne ne le voit avant
   d'ouvrir l'écran sur un jeu réel : la documentation d'API mesurait 1758 px
   dans une carte de 1010, la liste des tâches 1245 px, et la page entière
   défilait de côté avec ses boutons d'action hors champ. Nommer la cellule
   coûte un mot ; l'oublier fait sortir la colonne de droite de l'écran. */
table.liste th.prose, table.liste td.prose {
  /* `overflow-wrap` autant que `white-space` : une adresse électronique ou une
     référence longue est UN mot pour le navigateur, et une cellule qui ne peut
     pas la couper pousse la page malgré le retour à la ligne autorisé. */
  white-space: normal; overflow-wrap: anywhere;
}
/* Le collant ne vaut QUE pour la ligne de titres. Posé sur tous les `th`, il
   emportait aussi ceux d'un `tfoot` — la ligne « Total » d'une fiche de devis,
   de vente ou de facture se décollait de son montant au défilement — et ceux
   d'un `th scope="row"`, qui flottaient au-dessus de leur propre rangée. */
table.liste thead th { position: sticky; top: var(--bandeau); z-index: 10; }

/* Le même filet ouvre la carte de chaque tableau, au-dessus de la ligne de
   titres — jamais derrière une vignette ni une cellule de couleur,
   ce que la charte protège pour de bonnes raisons gemmologiques.

   Il est porté par la CARTE et non par les cellules d'en-tête : celles-ci sont
   en `overflow: hidden` pour l'ellipse, ce qui rognait leur propre
   pseudo-élément — le filet existait dans le CSS et ne paraissait nulle part.
   Et un fond par cellule aurait redémarré le motif à chaque colonne.

   Cadrage vertical différent de celui du bandeau : deux traces identiques
   feraient une frise, et une frise est un décor. */
table.liste tbody tr { transition: background-color var(--duree-court) var(--entree); }
table.liste tbody tr:hover { background-color: var(--fond); cursor: pointer; }
/* Pendant que la PAGE défile, le survol est suspendu. Sans cela, la ligne
   sous le curseur change à chaque image du défilement, sa couleur de fond
   avec elle — et la liste entière repeint à chaque image, sur le fil
   principal. Mesuré le 03.09.2026 (Chromium, 3 s de molette sur 50 lignes
   de Oikos) : ~500 peintures avec le curseur sur le tableau, zéro avec le
   curseur à côté. C'est ce que le client a décrit comme « le module stock
   lague » le jour où son stock a dépassé un écran. La classe est posée par
   oikos.js le temps du défilement ; sans JavaScript, rien ne change : le
   survol reste entier. Règle CSS-5 de l'auditeur. */
body.defile table.liste tbody { pointer-events: none; }
table.liste tbody tr.selectionnee { background: var(--primaire-tint); }
table.liste tbody tr:last-child td { border-bottom: none; }

/* Urgence : un filet à gauche et le chiffre en couleur. Le fond des rangées
   reste neutre — les listes portent des vignettes, et un fond
   teinté fausse le jugement d'une couleur (DESIGN.md). */
table.liste tr.ligne-retard td:first-child { box-shadow: inset 2px 0 0 var(--danger); }
table.liste tr.ligne-urgence td:first-child { box-shadow: inset 2px 0 0 var(--avert); }
table.liste tr.ligne-urgence td.compteur-jours { color: var(--avert); font-weight: 500; }
table.liste tr.ligne-retard td.compteur-jours { color: var(--danger); font-weight: 500; }
table.liste .col-coche { width: 34px; }
table.liste .vignette {
  width: 28px; height: 28px; border-radius: 3px; object-fit: cover;
  display: block; background: var(--surface); border: 1px solid var(--bordure);
}

/* Filtres rapides en pastilles au-dessus du tableau. */
.filtres-rapides { display: flex; gap: 6px; flex-wrap: wrap; }
.filtre-pastille {
  border: 1px solid var(--bordure); border-radius: 999px;
  background: var(--surface); color: var(--encre-douce);
  padding: 3px 12px; font: inherit; cursor: pointer; text-decoration: none;
}
.filtre-pastille {
  transition: background-color var(--duree-court) var(--entree),
              border-color var(--duree-court) var(--entree),
              color var(--duree-court) var(--entree);
}
.filtre-pastille:hover { border-color: var(--encre-attenuee); text-decoration: none; }
.filtre-pastille.actif {
  background: var(--primaire-tint); border-color: var(--primaire);
  color: var(--primaire); font-weight: 500;
}

/* Pagination serveur. */
.pagination { display: flex; align-items: center; gap: 8px; padding: 10px 12px; color: var(--encre-attenuee); }
.pagination a { padding: 2px 8px; border: 1px solid var(--bordure); border-radius: var(--rayon); }

/* ======================= Pastilles de statut ============================= */
.pastille {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 1px 9px; border-radius: 999px; font-size: 12.5px; font-weight: 500;
  border: 1px solid transparent; white-space: nowrap;
}
.pastille::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.pastille-succes { color: var(--succes); background: var(--succes-tint); }
.pastille-avert  { color: var(--avert);  background: var(--avert-tint); }
.pastille-danger { color: var(--danger); background: var(--danger-tint); }
.pastille-info   { color: var(--info);   background: var(--info-tint); }
.pastille-neutre { color: var(--encre-douce); background: var(--fond); border-color: var(--bordure); }
.pastille-accent { color: var(--accent-texte); background: var(--accent-tint); }

/* Rôles de tiers. */
.pastille-role { font-size: 11.5px; padding: 0 8px; }



/* Badge repère (annotation numérotée) — pastille bordée, chiffre tabulaire. */
.repere {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 22px; padding: 1px 7px 2px;
  background: var(--fond); border: 1px solid var(--primaire); border-radius: var(--rayon);
  font-size: 12px; font-weight: 500; color: var(--primaire);
  font-variant-numeric: tabular-nums; line-height: 1.5;
}

/* ======================= Boutons ========================================= */
.btn {
  display: inline-flex; align-items: center; gap: 6px;
  height: 30px; padding: 0 14px;
  border: 1px solid var(--bordure); border-radius: var(--rayon);
  background: var(--surface); color: var(--encre);
  font: inherit; font-weight: 500; cursor: pointer; text-decoration: none;
  white-space: nowrap;
  transition: background-color var(--duree-court) var(--entree),
              border-color var(--duree-court) var(--entree),
              color var(--duree-court) var(--entree);
}
.btn:hover { background: var(--fond); text-decoration: none; color: var(--encre); }
.btn-primaire { background: var(--primaire); border-color: var(--primaire); color: var(--surface); }
.btn-primaire:hover { background: var(--primaire-fonce); border-color: var(--primaire-fonce); color: var(--surface); }
.btn-danger { color: var(--danger); border-color: var(--danger); }
.btn-danger:hover { background: var(--danger-tint); color: var(--danger); }
.btn-secondaire { color: var(--primaire); border-color: var(--primaire); }
/* Le grand bouton d'un formulaire passe à la ligne à toute largeur, pas seulement au téléphone : dans la colonne
   d'une tablette, « Recevoir l'avis et ouvrir la réserve » tenu sur une ligne par .btn sortait du cadre (banc, 26.09.2026). */
.btn-large-telephone { height: auto; min-height: 30px; padding-block: 5px; white-space: normal; text-align: center; }
.btn-secondaire:hover { background: var(--primaire-tint); color: var(--primaire-fonce); }
.btn-discret { border-color: transparent; background: transparent; color: var(--encre-douce); }
.btn-discret:hover { background: var(--fond); color: var(--encre); }
.btn-icone { width: 30px; min-width: 30px; padding: 0; justify-content: center; }
/* Un pixel, le temps de l'appui. C'est la seule chose qui distingue un
   bouton qui a pris le clic d'un bouton qui n'a rien vu — et sur un écran
   d'encaissement, cette différence-là se cherche. */
.btn:active:not(:disabled) { transform: translateY(1px); }
.btn:disabled { opacity: .45; cursor: not-allowed; transform: none; }
.btn .icone { width: 16px; height: 16px; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* ======================= Champs ========================================== */
.champ { margin-bottom: 14px; }
.champ label { display: block; margin-bottom: 4px; color: var(--encre-douce); font-weight: 500; }
/* `.champ-nu` : un champ dont le libellé est ailleurs — l'en-tête de sa
   colonne, dans un tableau de saisie. Mêmes traits et même mise au point que
   dans un bloc `.champ`, sans le bloc. */
.champ input, .champ select, .champ textarea, .champ-nu {
  width: 100%; padding: 6px 10px; height: 32px;
  border: 1px solid var(--bordure); border-radius: var(--rayon);
  background: var(--surface); color: var(--encre); font: inherit;
  transition: border-color var(--duree-court) var(--entree);
}
.champ textarea { height: auto; min-height: 72px; resize: vertical; }
.champ input::placeholder, .champ textarea::placeholder { color: var(--encre-attenuee); }
.champ input:focus, .champ select:focus, .champ textarea:focus,
.champ-nu:focus { border-color: var(--primaire); outline: none; }

/* Une case à cocher n'est pas un champ de saisie, et la règle ci-dessus ne lui
   était pas destinée — elle l'atteignait quand même, par `input`. La case
   recevait donc 100 % de large, 32 px de haut, un filet et un rembourrage :
   sur WebKit elle s'étirait sur toute la colonne et repoussait son libellé à
   l'autre bout de la ligne (constaté sur « Autres rôles », fiche fournisseur,
   le 31.08.2026) ; sur Chrome elle devenait un rectangle de 13 × 32 px, décalé
   de sept pixels sous le mot qu'elle étiquette.
   Sept gabarits portaient déjà `style="width:auto;height:auto"` en ligne pour
   s'en défendre — c'est le huitième qui l'oubliait, et rien dans la feuille ne
   permettait de le voir. La règle vit ici une fois ; la rustine nulle part.
   `flex: none` couvre le libellé en flex, où la case se faisait sinon étirer à
   la hauteur de la ligne. */
.champ input[type="checkbox"], .champ input[type="radio"] {
  width: auto; height: auto; padding: 0; border: 0; background: none;
  flex: none;
}
/* Le sélecteur de fichier garde le filet et la largeur du champ, mais pas la
   hauteur fixe : son bouton natif mesure plus que les 20 px de contenu qui
   restaient sous les 32 px, et se faisait rogner en bas. */
.champ input[type="file"] { height: auto; min-height: 32px; padding: 5px 10px; }

/* Filtre compact d'une barre d'actions : mêmes filet, rayon et police que
   `.champ`, en 30 px de haut et sans bloc — son libellé est son `aria-label`.
   Les listes du dépôt refaisaient ces cinq déclarations en style en ligne,
   copiées d'un écran à l'autre : la première qui dérive ne se voit pas. */
.champ-compact {
  transition: border-color var(--duree-court) var(--entree);
  height: 30px; padding: 0 10px;
  border: 1px solid var(--bordure); border-radius: var(--rayon);
  background: var(--surface); color: var(--encre); font: inherit;
}
.champ-compact:focus { border-color: var(--primaire); outline: none; }
.champ input[readonly] { background: var(--fond); color: var(--encre-douce); }
.champ .aide { font-size: 12px; color: var(--encre-attenuee); margin-top: 3px; }
/* Messages d'erreur : précis, à côté du champ concerné. Jamais de bandeau. */
.champ .erreur { font-size: 12.5px; color: var(--danger); margin-top: 3px; }
.champ.en-erreur input, .champ.en-erreur select, .champ.en-erreur textarea { border-color: var(--danger); }
/* Plafond de piste : sans lui, un champ seul dans une carte large
   s'étirait sur toute sa largeur — une date de neuf cents pixels. Un champ de
   saisie a une largeur utile, au-delà elle ne sert plus qu'à viser. */
.grille-champs {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 360px));
  gap: 0 16px;
}

/* Réglages en lecture seule (écran Paramètres, compte non administrateur).
   Le <fieldset> ne sert qu'à porter l'attribut « disabled » sur tout un bloc :
   il n'a ni cadre ni marge propres. Ce grisage est un CONFORT D'ÉCRAN, pas un
   contrôle d'accès — le refus est posé sur les routes POST, qui exigent
   require_admin (voir app/parametres/routes.py). */
.reglages { border: 0; padding: 0; margin: 0; min-width: 0; }
.reglages[disabled] input, .reglages[disabled] select, .reglages[disabled] textarea {
  background: var(--fond); color: var(--encre-douce); cursor: default; opacity: 1;
}

/* ======================= Vue fiche ======================================= */
.fiche-entete {
  display: flex; align-items: flex-start; gap: 16px; margin-bottom: 16px;
  flex-wrap: wrap;
}
.fiche-titre h1 { font-size: 1.35rem; margin: 0; }
/* La flèche de retour se cale sur la ligne du titre, pas sur le haut du
   bloc : le titre fait 1.5rem, le bouton 30 px. */
.fiche-retour { margin-top: 3px; flex: none; }
.fiche-titre .sous-titre { color: var(--encre-douce); }
.fiche-actions { display: flex; gap: 8px; flex-wrap: wrap; }

/* Barre de statut : le cycle de vie de l'objet, en haut à droite. */
.barre-statut { display: flex; margin-left: auto; }
.barre-statut .etape {
  padding: 4px 14px; border: 1px solid var(--bordure);
  background: var(--surface); color: var(--encre-attenuee);
  border-left-width: 0; font-size: 12.5px; white-space: nowrap;
}
.barre-statut .etape:first-child { border-left-width: 1px; border-radius: var(--rayon) 0 0 var(--rayon); }
.barre-statut .etape:last-child { border-radius: 0 var(--rayon) var(--rayon) 0; }
.barre-statut .etape.courante {
  background: var(--primaire-tint); color: var(--primaire);
  border-color: var(--primaire); border-left-width: 1px; font-weight: 500;
}

.fiche-colonnes { display: grid; grid-template-columns: 2fr 1fr; gap: 16px; align-items: start; }
/* Sans cela, un tableau large (colonnes en nowrap) force sa colonne de grille
   et fait défiler la page entière à l'horizontale. */
.fiche-colonnes > * { min-width: 0; }
/* La variante deux colonnes est une CLASSE, déclarée AVANT la media query de
   repli : écrite en style inline, elle gagnait sur la media query et figeait
   deux colonnes de 180 px sur téléphone. */
.fiche-colonnes--deux { grid-template-columns: 1fr 1fr; }
/* Le calculateur de pièce : le résumé tient une colonne de 320 px. En classe et
   non en style en ligne — un style en ligne bat toute requête média, et la
   fiche restait à deux colonnes sur un téléphone de 390 px, le panneau
   recouvrant le formulaire. */
.fiche-colonnes--resume { grid-template-columns: 1fr 320px; }
@media (max-width: 1100px) {
  .fiche-colonnes, .fiche-colonnes--resume { grid-template-columns: 1fr; }
}

/* Un tableau plus large que son cadre défile dans son propre cadre, jamais en
   emportant la page avec lui.
   `position: relative` est la moitié de la règle. Piège payé sur le banc du
   14.09.2026 : un libellé `.sr-only` (absolu) posé dans une cellule prenait
   `.contenu` pour bloc conteneur ; hors du cadre, il n'était plus rogné par lui
   et élargissait la PAGE jusqu'au bord droit du tableau — 912 px pour 768 sur
   tablette. Le cadre positionné garde ses descendants absolus avec lui. */
.tableau-defilant { overflow-x: auto; position: relative; }

/* Dans une carte composite (fiche voyage, tableau de bord), l'en-tête collant
   n'a pas de sens : il se fige au défilement de la page et flotte par-dessus
   le contenu de la carte — l'en-tête qui recouvrait « Aucun frais enregistré ».
   Le collant reste réservé aux pages-listes pleine hauteur. */
.tableau-defilant table.liste th { position: static; }

/* Cadre posé par oikos.js autour de CHAQUE table.liste qui n'en a pas. Il ne
   défile que lorsque le tableau déborde (classe `deborde`, mesurée par un
   ResizeObserver) : tant que le tableau tient, le cadre reste transparent et
   l'en-tête collant continue de coller — un `overflow-x: auto` permanent
   ferait du cadre le conteneur de défilement, et l'en-tête n'aurait plus rien
   à quoi coller. Sur un écran étroit, le tableau défile dans son cadre et la
   page garde sa largeur ; mesuré avant : /tiers à 1219 px sur une tablette de
   768. */
.tableau-defilant--auto { overflow: visible; }
.tableau-defilant--auto.deborde { overflow-x: auto; overscroll-behavior-x: contain; }
.tableau-defilant--auto:not(.deborde) table.liste thead th { position: sticky; }
.tableau-defilant--auto.deborde table.liste { min-width: max-content; }

/* Un état vide vit dans une cellule de tableau : sans ceci, le nowrap des
   cellules tronque son texte au milieu d'un mot et force un défilement
   horizontal absurde. */
table.liste td:has(> .etat-vide) { white-space: normal; }
.fiche-section { padding: 16px; }
.fiche-section + .fiche-section { border-top: 1px solid var(--bordure); }
.fiche-section h2 { font-size: .95rem; color: var(--encre-douce); }

/* Liste de faits (libellé/valeur) dans une fiche. */
.faits { display: grid; grid-template-columns: max-content 1fr; gap: 4px 18px; }
.faits dt { color: var(--encre-attenuee); }
.faits dd { margin: 0; }

/* Index typographique : rangées séparées par des filets, sans tableau.
   Pattern de la charte — pour les listes riches et les faits mis en scène. */
.index-liste { border-top: 1px solid var(--bordure); }
.index-liste .rangee {
  display: flex; align-items: baseline; gap: 16px;
  padding: 11px 2px; border-bottom: 1px solid var(--bordure);
  color: var(--encre-douce);
}
.index-liste .rangee .cle {
  flex: none; width: 130px; font-weight: 500; color: var(--encre);
}
.index-liste .rangee .valeur { font-size: inherit; font-weight: 400; color: var(--encre); }

/* ======================= Onglets ========================================= */
.onglets {
  display: flex; gap: 2px; border-bottom: 1px solid var(--bordure); margin-bottom: 0;
  overflow-x: auto; scrollbar-width: none; -ms-overflow-style: none;
}
.onglets::-webkit-scrollbar { display: none; }
.onglets .onglet { flex: none; white-space: nowrap; }
.onglet {
  padding: 8px 14px; color: var(--encre-douce); cursor: pointer;
  border: none; background: none; font: inherit;
  border-bottom: 2px solid transparent; text-decoration: none;
}
.onglet {
  transition: color var(--duree-court) var(--entree),
              border-bottom-color var(--duree-court) var(--entree);
}
.onglet:hover { color: var(--encre); text-decoration: none; }
.onglet.actif { color: var(--primaire); border-bottom-color: var(--primaire); font-weight: 500; }
.onglet-panneau { padding: 16px; }

/* ======================= Blocs pliables (calculateur) ==================== */
.bloc-pliable { border: 1px solid var(--bordure); border-radius: var(--rayon); background: var(--surface); margin-bottom: 12px; }
.bloc-pliable > .bloc-titre {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 10px 14px; font: inherit; font-weight: 500; color: var(--encre);
  background: none; border: none; cursor: pointer; text-align: left;
}
.bloc-pliable.vide > .bloc-titre { color: var(--encre-attenuee); font-weight: 400; }
.bloc-titre .total-bloc { margin-left: auto; font-variant-numeric: tabular-nums; color: var(--encre-douce); }
.bloc-corps {
  border-top: 1px solid var(--bordure); padding: 10px 14px;
  animation: bloc-ouvre var(--duree-moyen) var(--sortie);
}
.bloc-titre .chevron { transition: transform var(--duree-court) var(--entree); width: 16px; height: 16px; }
.bloc-titre .chevron.ouvert { transform: rotate(90deg); }
@keyframes bloc-ouvre {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: none; }
}

/* ======================= Panneau latéral ================================= */
.panneau-fond {
  position: fixed; inset: 0; background: rgba(26, 29, 27, .28); z-index: 90;
  animation: voile-entre var(--duree-moyen) var(--entree);
}
.panneau {
  position: fixed; top: 0; right: 0; bottom: 0; width: 420px; max-width: 92vw;
  background: var(--surface); border-left: 1px solid var(--bordure); z-index: 91;
  display: flex; flex-direction: column;
  animation: panneau-entre var(--duree-long) var(--sortie);
}
.panneau-entete {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 16px; border-bottom: 1px solid var(--bordure);
}
.panneau-corps { padding: 16px; overflow-y: auto; flex: 1; }
@keyframes panneau-entre { from { transform: translateX(24px); opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes voile-entre { from { opacity: 0; } to { opacity: 1; } }

/* Panneau de résumé du calculateur : collant au défilement. */
.panneau-resume { position: sticky; top: calc(var(--bandeau) + 20px); }
.panneau-resume .cout { color: var(--encre-douce); }
/* Le coût de revient est le montant qui compte sur cet écran : plus grand et
   plus contrasté que le reste, mais en Inter — on le relit à chaque ligne
   saisie. « Calculé par le serveur — c'est un fait. » */
.panneau-resume .cout .valeur {
  display: block;
  font-weight: 500; letter-spacing: -.01em;
  font-size: 1.35rem; line-height: 1.2; color: var(--encre);
  font-variant-numeric: tabular-nums;
}
.panneau-resume .marge-positive { color: var(--succes); }
.panneau-resume .marge-negative { color: var(--danger); }

/* ======================= Dialogue ======================================== */
.dialogue-fond {
  position: fixed; inset: 0; background: rgba(26, 29, 27, .28); z-index: 95;
  display: flex; align-items: flex-start; justify-content: center; padding-top: 12vh;
  animation: voile-entre var(--duree-moyen) var(--entree);
}
.dialogue {
  width: 440px; max-width: 92vw;
  background: var(--surface); border: 1px solid var(--bordure); border-radius: var(--rayon);
  animation: dialogue-entre var(--duree-moyen) var(--sortie);
}
.dialogue-entete { padding: 14px 16px; border-bottom: 1px solid var(--bordure); font-weight: 500; }
.dialogue-corps { padding: 16px; }
.dialogue-pied { padding: 12px 16px; border-top: 1px solid var(--bordure); display: flex; justify-content: flex-end; gap: 8px; }
@keyframes dialogue-entre { from { transform: scale(.98) translateY(-6px); opacity: 0; } to { transform: none; opacity: 1; } }

/* ======================= Avis et alertes ================================= */
/* « Le système ne décide jamais à la place de l'utilisateur : alerter sans
   bloquer. » Un avis se lit, il ne ressemble pas à une erreur. */
.avis {
  display: flex; align-items: baseline; gap: 10px;
  padding: 11px 14px; border: 1px solid var(--bordure); border-radius: var(--rayon);
  background: var(--surface); color: var(--encre-douce);
}
.avis .titre { font-weight: 500; color: var(--encre); }
.avis .appoint { color: var(--encre-attenuee); }
.avis-avert { border-color: #E9CFA8; background: var(--avert-tint); }
.avis-avert .titre { color: var(--avert); }
/* La proposition du tutoriel. Elle s'aligne sur les avis, pas sur un encart
   publicitaire : même hauteur, même bordure, même calme. Ce n'est pas une
   réclame, c'est un service — le lecteur doit pouvoir la traverser du regard
   sans qu'elle lui coupe la page. */
.avis-tutoriel { align-items: center; flex-wrap: wrap; }
.avis-tutoriel .appoint { flex: 1 1 300px; }
.avis-actions { display: flex; align-items: center; gap: 8px; margin-left: auto; }
.avis-actions form { margin: 0; }

/* ======================= États vides ===================================== */
/* Le seul endroit du quotidien où le picto a droit de cité : une liste vide
   n'est pas une erreur, c'est un moment de calme. */
.etat-vide {
  display: flex; flex-direction: column; align-items: center;
  gap: 14px; padding: 44px 24px; text-align: center;
}
.etat-vide .picto { width: 30px; height: 30px; opacity: .5; }
.etat-vide .propos { color: var(--encre-douce); max-width: 44ch; margin: 0; }
.etat-vide .propos strong { font-weight: 500; color: var(--encre); }
/* Variante en cellule de tableau, pour les listes existantes. */
td > .etat-vide { padding: 30px 16px; }

/* ======================= Moments forts =================================== */
/* Aplat vert profond cadré d'un double filet doré. Connexion et splash
   uniquement : jamais une surface de travail, jamais près d'une photo. */
.moment {
  background: var(--vert-profond);
  color: #FFFFFF;
  position: relative;
}
.moment .doux { color: var(--sur-vert); }
.moment .valeur-or { color: var(--primaire); font-family: var(--police-marque); font-variant-numeric: tabular-nums; }
/* Les deux filets de cadre : 1px à 36px du bord, 1px à 42px. */
.cadre-or::before, .cadre-or::after {
  content: ""; position: absolute; pointer-events: none; border: 1px solid var(--bordure);
}
.cadre-or::before { inset: 20px; }
.cadre-or::after { inset: 26px; border-color: var(--bordure); }

/* ======================= Divers ========================================== */
.flashes { position: fixed; top: calc(var(--bandeau) + 8px); right: 16px; z-index: 99; max-width: 380px; }
.flash {
  padding: 9px 14px; border-radius: var(--rayon); border: 1px solid var(--bordure);
  margin-bottom: 8px; background: var(--surface);
  animation: flash-entre var(--duree-long) var(--sortie);
}
.flash-success { background: var(--succes-tint); border-color: var(--succes); color: var(--succes); }
.flash-error   { background: var(--danger-tint); border-color: var(--danger); color: var(--danger); }
.flash-info    { background: var(--info-tint);   border-color: var(--info);   color: var(--info); }
@keyframes flash-entre { from { transform: translateY(-8px); opacity: 0; } to { transform: none; opacity: 1; } }

/* Indicateur de chargement HTMX : un tableau qui se recharge sans signal
   donne l'impression d'une application figée. */
.htmx-indicator { opacity: 0; transition: opacity var(--duree-court) var(--entree); }
.htmx-request .htmx-indicator, .htmx-request.htmx-indicator { opacity: 1; }
.indicateur {
  display: inline-block; width: 14px; height: 14px;
  border: 2px solid var(--bordure); border-top-color: var(--primaire);
  border-radius: 50%; animation: rotation .7s linear infinite;
}
@keyframes rotation { to { transform: rotate(360deg); } }

/* --- Sélecteur cherchable (V1.1) --------------------------------------------
   On tape un code, on trouve. Remplace les listes déroulantes qu'il fallait
   faire défiler. Sobre : un champ, une liste, un filet — pas d'ombre portée. */
.selecteur { position: relative; }
.selecteur-saisie {
  width: 100%; height: 32px; padding: 0 10px;
  border: 1px solid var(--bordure); border-radius: var(--rayon);
  background: var(--surface); color: var(--encre); font: inherit;
}
.selecteur-saisie:focus { border-color: var(--primaire); outline: none; }
.selecteur-saisie::placeholder { color: var(--encre-attenuee); }
.selecteur-attente { position: absolute; right: 26px; top: 8px; }
.selecteur-liste {
  position: absolute; z-index: 30; left: 0; right: 0; top: calc(100% + 3px);
  max-height: 292px; overflow-y: auto;
  background: var(--surface); border: 1px solid var(--primaire);
  border-radius: var(--rayon);
  display: flex; flex-direction: column;
}
/* Le display:flex ci-dessus l'emporte sur le style navigateur de [hidden] :
   sans cette règle, la liste ne se fermait JAMAIS — ni au clic à côté, ni
   après sélection (régression vue en démo le 17.08.2026). Le garde-fou est
   GLOBAL : hidden doit gagner sur n'importe quel display posé par une classe,
   pour ce composant et tous ceux qui viendront. */
[hidden] { display: none !important; }
.selecteur-option {
  display: flex; align-items: baseline; gap: 10px; width: 100%;
  padding: 7px 10px; text-align: left; font: inherit; color: var(--encre);
  background: none; border: 0; border-bottom: 1px solid var(--bordure);
  cursor: pointer;
}
.selecteur-option:last-child { border-bottom: 0; }
.selecteur-option { transition: background-color var(--duree-court) var(--entree); }
.selecteur-option:hover, .selecteur-option:focus-visible {
  background: var(--primaire-tint); outline: none;
}
.selecteur-principal { font-weight: 500; white-space: nowrap; }
.selecteur-appoint {
  color: var(--encre-douce); font-size: 12.5px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.selecteur-montant { margin-left: auto; color: var(--encre-douce); white-space: nowrap; }
.selecteur-note { padding: 10px; color: var(--encre-attenuee); font-size: 12.5px; }
/* Dans une rangée de tableau (calculateur, lignes de vente), la cellule est
   étroite : la liste garde son ancrage sur le champ mais s'élargit pour rester
   lisible. Un tableau ne rogne pas ce qui dépasse de ses cellules. */
td .selecteur-liste { min-width: 320px; }

/* --- Documents joints (V1.1) ------------------------------------------------
   Bloc partagé par toutes les fiches qui portent des documents. Sobre :
   une liste, une zone de dépôt, rien de plus. Aucune ombre portée, un filet
   pour séparer — l'ERP est carré (DESIGN.md). */
.documents-liste { list-style: none; margin: 0; padding: 0; }
.documents-liste li {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 16px; border-bottom: 1px solid var(--bordure);
  min-height: var(--ligne-tableau);
}
.documents-liste li:last-child { border-bottom: none; }
.documents-nom { font-weight: 500; overflow-wrap: anywhere; }
.documents-meta {
  margin-left: auto; color: var(--encre-attenuee); font-size: 12.5px;
  white-space: nowrap;
}
.documents-supprimer { display: flex; }
.documents-vide { padding: 14px 16px; margin: 0; }
.documents-erreur {
  margin: 0 16px 12px; padding: 8px 10px;
  background: var(--danger-tint); color: var(--danger);
  border: 1px solid var(--danger); border-radius: var(--rayon);
  font-size: 13px;
}
.documents-depot { padding: 14px 16px 16px; border-top: 1px solid var(--bordure); }
.documents-zone {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 18px 14px; text-align: center; cursor: pointer;
  border: 1px dashed var(--bordure); border-radius: var(--rayon);
  background: var(--fond); color: var(--encre-douce);
  transition: border-color var(--duree-court) var(--entree),
              background-color var(--duree-court) var(--entree);
}
.documents-zone:hover { border-color: var(--primaire); }
.documents-zone:focus-within { border-color: var(--primaire); border-style: solid; }
.documents-zone-survol {
  border-color: var(--primaire); border-style: solid; background: var(--primaire-tint);
}
.documents-zone-remplie {
  border-style: solid; border-color: var(--primaire); background: var(--surface);
}
.documents-zone-remplie .documents-invite { color: var(--primaire); font-weight: 500; }
/* Le champ natif est masqué, jamais retiré du flux : il reste atteignable au
   clavier et le label porte le clic.
   Le sélecteur porte le type : sans lui, `.champ input` (une classe ET un
   type) l'emportait sur `.documents-fichier` (une classe) et rendait au champ
   ses 100 % de large. Masqué par `clip-path`, il restait invisible — mais sa
   boîte, posée en absolu, mesurait 920 px et poussait la page de « Nouvelle
   fiche » 56 px au-delà de la fenêtre : une barre de défilement horizontale
   sans rien à voir au bout. */
input.documents-fichier {
  position: absolute; width: 1px; height: 1px; opacity: 0;
  overflow: hidden; clip-path: inset(50%);
}
.documents-invite { overflow-wrap: anywhere; }
.documents-formats { font-size: 12px; }
.documents-champs {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px; margin-top: 12px;
}
.documents-champs .champ { margin-bottom: 0; flex: 1 1 160px; }
@media (max-width: 720px) {
  /* Le nom, la nature, la date et l'auteur ne tiennent pas sur 320 px : le nom
     s'écrasait à 12 px de large et « ✕ » sortait du cadre (banc du 16.09.2026,
     photos de l'étanchéité). La date et l'auteur passent à la ligne. */
  .documents-liste li { flex-wrap: wrap; row-gap: 2px; }
  .documents-nom { flex: 1 1 55%; min-width: 0; }
  .documents-meta { order: 3; flex: 1 1 100%; margin-left: 0; white-space: normal; }
}
.documents-attente { display: block; margin-top: 8px; font-size: 12.5px; }

/* Une valeur recalculée par le serveur doit se voir changer, sinon
   l'opérateur ne sait pas si le montant qu'il lit est à jour. */
.valeur-fraiche { animation: valeur-change var(--duree-long) var(--sortie); }
@keyframes valeur-change {
  from { opacity: .35; filter: blur(3px); }
  to   { opacity: 1; filter: none; }
}

/* Erreur de validation : le champ se signale une fois, puis redevient neutre. */
.champ.en-erreur input, .champ.en-erreur select, .champ.en-erreur textarea {
  animation: secousse 320ms var(--entree);
}
@keyframes secousse {
  0%, 100% { transform: none; }
  20% { transform: translateX(-4px); }
  45% { transform: translateX(3px); }
  70% { transform: translateX(-2px); }
}

/* Indicateurs en cartes (rapports, fiche tiers, tableau de bord). */
/* Même plafond : un compteur à deux chiffres au bord d'une carte de
   560 px se cherche du regard. */
.indicateurs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 280px));
  gap: 12px; margin-bottom: 16px;
}
.indicateur-carte { padding: 14px 16px; }
.indicateur-carte .libelle {
  font-size: 12px; font-weight: 500; letter-spacing: .1em;
  text-transform: uppercase; color: var(--encre-attenuee);
}
.indicateur-carte .valeur {
  margin-top: 6px;
  font-weight: 500;
  font-size: 1.35rem; line-height: 1.2; color: var(--encre);
  font-variant-numeric: tabular-nums;
}

/* Index chiffré : la bande qui ouvre le tableau de bord. Pas cinq boîtes de
   poids égal — un index typographique, séparé par des filets, dont le premier
   chiffre porte le vert de la marque. La hiérarchie vient de la taille, du
   filet et de la couleur, pas d'une police à empattements : c'est un écran
   qu'on ouvre chaque matin pour lire des montants. */
.index-chiffres {
  /* Une BARRE, pas une grille qui se replie : les deux à quatre mesures
     tiennent sur une ligne, séparées d'un filet, et remplissent le cadre.
     `auto-fit` les faisait passer à la ligne à l'intérieur de la bordure dès
     que la fenêtre se resserrait — un cadre à moitié vide sous une rangée
     complète. Le repli est décidé par la requête média, en bas de bloc. */
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(160px, 1fr);
  margin-bottom: 22px; background: var(--surface);
  border: 1px solid var(--bordure); border-radius: var(--rayon);
  overflow: hidden;
}
.index-chiffres .mesure { padding: 18px 20px 20px; border-right: 1px solid var(--bordure); }
.index-chiffres .mesure:last-child { border-right: none; }
.index-chiffres .libelle {
  font-size: 12px; font-weight: 500; letter-spacing: .1em;
  text-transform: uppercase; color: var(--encre-attenuee);
  display: block; min-height: 2.6em;
}
/* 1.35rem et pas davantage : à cinq colonnes, c'est la taille maximale où un
   montant à sept chiffres tient encore sur une ligne. Un montant qui passe à
   la ligne coûte plus en lisibilité qu'il ne rapporte en présence. */
.index-chiffres .valeur {
  display: block; margin-top: 6px;
  font-weight: 500; letter-spacing: -.01em;
  font-size: 1.35rem; line-height: 1.2; color: var(--encre);
  font-variant-numeric: tabular-nums;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.index-chiffres .mesure.majeur .valeur { color: var(--primaire); }
.resume-mobile { display: none; }
.index-chiffres .valeur.en-alerte { color: var(--avert); }
@media (max-width: 900px) {
  /* Sous 900 px, les mesures s'empilent : la barre devient une liste, et
     chaque mesure garde sa largeur de lecture. */
  .index-chiffres { grid-auto-flow: row; }
  .index-chiffres .mesure {
    border-bottom: 1px solid var(--bordure); border-right: none;
  }
  .index-chiffres .mesure:last-child { border-bottom: none; }
  .index-chiffres .libelle { min-height: 0; }
}

/* Cadre photo : fond blanc STRICT, jamais teinté — condition pour juger d'une
   couleur (DESIGN.md). */
.cadre-photo {
  background: #FFFFFF; border: 1px solid var(--bordure); border-radius: var(--rayon);
  display: flex; align-items: center; justify-content: center; overflow: hidden;
}
.cadre-photo img { max-width: 100%; max-height: 100%; display: block; }
.bande-miniatures { display: flex; gap: 6px; margin-top: 8px; flex-wrap: wrap; }
.bande-miniatures img {
  width: 52px; height: 52px; object-fit: cover; border-radius: 3px;
  border: 1px solid var(--bordure); cursor: pointer; background: #FFFFFF;
}
.bande-miniatures img.active { border-color: var(--primaire); }

/* Une miniature porte DEUX gestes : mettre en avant (l'image) et retirer (la
   croix). Deux formulaires côte à côte, jamais imbriqués — un <form> dans un
   <form> n'est pas soumis par les navigateurs, et le geste se perdrait sans
   la moindre erreur visible. */
.miniature { display: flex; flex-direction: column; align-items: center; gap: 2px; }
.miniature form { margin: 0; line-height: 0; }
.miniature-choix { border: none; background: none; padding: 0; cursor: pointer; }
.miniature-film {
  display: flex; align-items: center; justify-content: center;
  width: 52px; height: 52px; border: 1px solid var(--bordure);
  border-radius: 3px; background: #FFFFFF; text-decoration: none; font-size: 20px;
}
.miniature-retirer {
  border: none; background: none; padding: 1px 4px; cursor: pointer;
  color: var(--encre-attenuee); font-size: 12px; line-height: 1;
  border-radius: 3px; transition: color var(--duree-court) var(--sortie);
}
.miniature-retirer:hover { color: var(--danger); background: var(--danger-tint); }

/* ======================= Écrans publics ================================== */
body.public { display: flex; min-height: 100vh; background: var(--fond); }
.public-cadre { margin: auto; width: 100%; max-width: 400px; padding: 24px 16px; }
.public-carte {
  background: var(--surface); border: 1px solid var(--bordure);
  border-radius: var(--rayon); padding: 32px;
}

/* Connexion : le moment fort de l'app. Aplat vert profond, double filet doré,
   emblème au centre. C'est le seul écran de travail qui a le droit d'être
   sombre — aucune photo n'y est jugée. */
body.connexion { display: block; background: var(--fond); }
.connexion-scene {
  min-height: 100vh; min-height: 100svh; display: flex; align-items: center; justify-content: center;
  padding: 32px 16px; position: relative;
}
/* Récupération de mot de passe : mêmes cartes que la connexion, un recours
   discret sous le bouton, un message qui ne distingue jamais les deux issues. */
.lien-oubli { margin: 14px 0 0; text-align: center; font-size: 12.5px; }
.lien-oubli a { color: var(--encre-douce); }
.lien-oubli a:hover { color: var(--primaire); }
.oubli-message { margin: 0 0 18px; color: var(--encre-douce); font-size: 13.5px; line-height: 1.6; }
.oubli-message--erreur { color: var(--danger); }
.connexion-carte {
  position: relative; width: 100%; max-width: 392px;
  background: var(--surface); border: 1px solid var(--bordure);
  border-radius: var(--rayon); padding: 36px 32px 24px;
  box-shadow: 0 18px 40px -24px rgba(58, 36, 24, .28);
}
.connexion-embleme { display: block; width: 46px; margin: 0 auto; color: var(--maison); }
.connexion-pied {
  margin: 22px 0 0; padding-top: 14px; border-top: 1px solid var(--bordure);
  text-align: center; font-size: 12px; color: var(--encre-attenuee);
}
.connexion-embleme svg { display: block; }
.connexion-marque {
  margin-top: 20px; text-align: center;
  font-family: var(--police-marque); font-weight: 400; font-size: 1.9rem;
  letter-spacing: .2em; text-indent: .2em; text-transform: uppercase;
  color: var(--maison); line-height: 1.1;
}
.connexion-org {
  margin-top: 10px; text-align: center;
  font-size: 11.5px; font-weight: 500; letter-spacing: .16em;
  text-transform: uppercase; color: var(--encre-douce);
}
.connexion-filet { width: 44px; height: 1px; background: var(--accent); margin: 22px auto 26px; }

/* Passeport public : la photo d'abord, l'interface s'efface.
   Surface client final — elle doit tenir dans un téléphone, en pleine rue. */
.passeport-public { max-width: 520px; margin: 24px auto 40px; padding: 0 16px; }

/* La photo en premier, sur blanc strict et sans cadre lourd : rien ne doit
   entrer en concurrence avec la couleur qu'on est venu regarder. */
.passeport-photo {
  background: #FFFFFF; border: 1px solid var(--bordure); border-radius: var(--rayon);
  padding: 10px; margin-bottom: 8px;
}
.passeport-photo img {
  display: block; width: 100%; height: auto;
  max-height: 62vh; object-fit: contain; margin: 0 auto;
}
/* Sur un téléphone, 32 px de marge de carte volent un tiers de la photo. */
@media (max-width: 480px) {
  .passeport-public { padding: 0 10px; }
  .public-carte { padding: 20px 16px 24px; }
}
.passeport-marque {
  font-family: var(--police-marque); font-weight: 400;
  font-size: 1.8rem; letter-spacing: .04em; text-transform: uppercase;
  color: var(--primaire); text-align: center; line-height: 1.15;
  font-variant-numeric: tabular-nums;
}
.passeport-espece { text-align: center; color: var(--encre-douce); margin: 6px 0 0; }
.passeport-org {
  text-align: center; font-size: 12px; font-weight: 500; letter-spacing: .14em;
  text-transform: uppercase; color: var(--encre-attenuee); margin-bottom: 18px;
}
.passeport-pied {
  display: flex; align-items: center; justify-content: center; gap: 9px;
  margin-top: 22px; font-size: 12px; font-weight: 500; letter-spacing: .14em;
  text-transform: uppercase; color: var(--encre-attenuee);
}
.passeport-pied .picto { width: 11px; height: 11px; }

/* ======================= Pages d'erreur ================================== */
.page-erreur {
  max-width: 480px; margin: 15vh auto 0; padding: 32px;
  background: var(--surface); border: 1px solid var(--bordure); border-radius: var(--rayon);
  text-align: center;
}
.page-erreur h1 { font-size: 1.3rem; }

/* ======================= Utilitaires ===================================== */
.texte-attenue { color: var(--encre-attenuee); }
.texte-doux { color: var(--encre-douce); }
/* Impression (rapport hebdomadaire) : la page seule, sans navigation ni
   fond décoratif — ce qu'on remet à un chef d'équipe sur papier. */
@media print {
  .laterale, .bandeau, .onglets-bas, .sans-impression, .flashes { display: none !important; }
  .contenu, .app { padding: 0; margin: 0; max-width: none; background: #fff; }
  body { background: #fff; }
  .carte { break-inside: avoid; box-shadow: none; }
  a[href]::after { content: none; }
}
.texte-succes { color: var(--succes); }
.texte-avert { color: var(--avert); }
.texte-danger { color: var(--danger); }
.texte-accent { color: var(--accent-texte); }
.texte-droite { text-align: right; }
.mono-espace { font-variant-numeric: tabular-nums; }
/* Aucun bloc préformaté ne pousse la page : ce qui dépasse défile DANS le
   bloc. Une ligne de détail de calcul aligne ses montants sur des points de
   conduite — un seul « mot » pour le navigateur, que `pre-wrap` ne sait pas
   couper. Règle qui balaye : le bloc écrit demain entre dans le périmètre
   sans que personne y pense. Rétroportée de Kronos, où quatre fiches
   débordaient de 44 px. */
pre { max-width: 100%; overflow-x: auto; overflow-wrap: anywhere; }

.separateur-v { width: 1px; align-self: stretch; background: var(--bordure); }
.mt-0 { margin-top: 0; } .mb-0 { margin-bottom: 0; }
[x-cloak] { display: none !important; }

/* ======================= Adaptation aux écrans =========================== */
/* L'ossature n'avait aucun point de rupture : sous 900 px la barre latérale
   mangeait l'écran. Elle passe en bandeau d'icônes, puis en rangée défilante. */
@media (max-width: 1200px) {
  :root { --barre-laterale: 188px; }
}
@media (max-width: 960px) {
  :root { --barre-laterale: 56px; }
  .laterale-logo { padding: 13px 0; display: flex; justify-content: center; }
  .wordmark .lettres, .laterale-groupe { display: none; }
  .laterale a.entree { justify-content: center; padding: 10px 0; gap: 0; }
  .laterale a.entree { font-size: 0; }
  .laterale .icone { width: 18px; height: 18px; }
  .recherche-globale { max-width: none; }
}
/* La navigation basse n'existe que sous 720 px : au-dessus, la barre
   latérale porte la navigation et la barre basse disparaît. */
.onglets-bas { display: none; }

@media (max-width: 720px) {
  .app {
    grid-template-columns: 1fr;
    grid-template-rows: var(--bandeau) 1fr;
    grid-template-areas: "bandeau" "contenu";
  }
  /* La barre latérale cède la place à la navigation basse : une rangée
     d'icônes défilante en haut mettait la navigation hors de portée du pouce
     et se faisait emporter par le défilement horizontal de la page. */
  .laterale { display: none; }
  .fil-ariane { display: none; }
  /* Le défilement horizontal n'emporte plus la page : chaque tableau trop
     large défile dans SON cadre (règle .liste ci-dessous). La zone de
     contenu réserve la hauteur de la barre basse. Contrepartie assumée : les
     en-têtes collants ne collent plus sous 720 px. */
  .contenu { padding: 14px; overflow-x: hidden; padding-bottom: 98px; }
  .contenu table.liste { display: block; overflow-x: auto; }
  /* Quand oikos.js a posé le cadre défilant, le tableau redevient un tableau :
     en `display: block` il ne remplirait plus sa carte. La règle du dessus
     reste le filet de sécurité sans JavaScript. */
  .tableau-defilant > table.liste { display: table; }
  /* Le compte (nom, espace, déconnexion) vit déjà dans le volet « Plus » : au
     bandeau il ne reste que la recherche, pleine largeur, sans le raccourci
     clavier qu'aucun téléphone ne tape. */
  .bandeau { padding: 0 12px; gap: 10px; }
  .bandeau-compte { display: none; }
  .recherche-globale { max-width: none; }
  .recherche-globale kbd { display: none; }
  .recherche-globale input { padding-right: 10px; height: 34px; }
  .fiche-entete { flex-direction: column; }
  .barre-statut { margin-left: 0; }
  .indicateurs { grid-template-columns: repeat(2, 1fr); }
  .indicateur-carte { border-bottom: 1px solid var(--bordure); }
  /* Les compteurs d'une feuille (rendues, nulles, attestées, révoquées) et
     ceux du tableau de bord du dialogueur : quatre cartes empilées faisaient
     un écran de téléphone entier pour quatre nombres — vu sur la planche du
     10.09.2026. Deux par rangée, un filet entre les deux. */
  .index-chiffres { grid-auto-flow: row; grid-template-columns: repeat(2, 1fr); grid-auto-columns: auto; }
  .index-chiffres > :nth-child(odd) { border-right: 1px solid var(--bordure); }
  /* La ligne de chiffres sous le titre d'une campagne, au tableau de bord :
     visible seulement quand les colonnes du tableau partent hors champ. */
  /* La cellule vit dans un tableau qui défile : sans largeur bornée, la
     ligne s'étire avec le tableau et repart hors champ. */
  .resume-mobile {
    display: block; margin-top: 4px; font-size: 12.5px; color: var(--encre-attenuee);
    white-space: normal; max-width: calc(100vw - 88px); text-align: left;
  }
  .flashes { left: 12px; right: 12px; max-width: none; }

  /* --- La barre d'onglets, sous le pouce --------------------------------- */
  .onglets-bas {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
    display: flex; align-items: stretch;
    background: var(--surface); border-top: 1px solid var(--bordure);
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  .onglets-bas .onglet {
    flex: 1; min-height: 56px;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 3px; padding: 6px 2px 7px;
    color: var(--encre-attenuee); font-size: 10px; text-decoration: none;
    border-top: 2px solid transparent; margin-top: -1px;
    cursor: pointer; list-style: none;
  }
  .onglets-bas .onglet.actif { color: var(--primaire); font-weight: 500; border-top-color: var(--primaire); }
  /* Le métier en tête (décision G7) : « Couverture, ferblanterie et étanchéité »
     ne tient pas sous une icône de 21 px ; le mot se coupe, la barre ne se
     déforme pas. */
  .onglets-bas .onglet-metier { min-width: 0; }
  .onglets-bas .onglet-metier span { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .onglets-bas .onglet .icone { width: 21px; height: 21px; }
  .onglets-plus { flex: 1; display: flex; }
  .onglets-plus > summary { flex: 1; }
  .onglets-plus > summary::-webkit-details-marker { display: none; }
  .onglets-plus[open] > summary { color: var(--primaire); }
  /* Le voile derrière le volet — un clic dessus referme (oikos.js). */
  .onglets-plus[open]::before {
    content: ""; position: fixed; inset: 0; z-index: 39;
    background: rgba(26, 29, 27, .34);
  }
  .volet-plus {
    position: fixed; left: 0; right: 0; z-index: 40;
    bottom: calc(56px + env(safe-area-inset-bottom, 0px));
    background: var(--surface); border-top: 1px solid var(--bordure);
    max-height: calc(100vh - 140px); overflow-y: auto;
  }
  .volet-plus-grille { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .volet-plus-grille .entree {
    display: flex; align-items: center; gap: 10px; min-height: 48px; padding: 0 16px;
    color: var(--encre); font-size: 13.5px; text-decoration: none;
    border-bottom: 1px solid var(--bordure);
  }
  .volet-plus-grille .entree:nth-child(odd) { border-right: 1px solid var(--bordure); }
  .volet-plus-grille .entree.active { color: var(--primaire); font-weight: 500; }
  .volet-plus-grille .entree .icone { width: 18px; height: 18px; color: var(--encre-douce); flex: none; }
  .volet-plus-grille .entree.active .icone { color: var(--primaire); }
  .volet-plus-pied {
    display: flex; align-items: center; justify-content: space-between;
    padding: 10px 16px; font-size: 13px; color: var(--encre-douce);
  }

  /* --- Une liste en cartes (téléphone) ------------------------------------- */
  /* Neuf colonnes en nowrap ne tiendront jamais sur 390 px : chaque ligne
     devient une carte — référence, montant, statut d'un regard, zéro
     défilement horizontal. Le gabarit reste le MÊME tableau (HTMX recharge
     le même fragment) : c'est la grille qui replace les cellules. */
  .contenu table.liste-cartes { display: block; overflow: visible; }
  .liste-cartes thead { display: none; }
  .liste-cartes tbody { display: block; }
  .liste-cartes tr {
    display: grid;
    grid-template-columns: 56px minmax(0, 1fr) auto;
    grid-template-areas:
      "vignette reference montant"
      "vignette espece   poids"
      "vignette origine  statut";
    gap: 2px 12px; align-items: baseline;
    padding: 11px 12px; margin-bottom: 8px;
    background: var(--surface); border: 1px solid var(--bordure); border-radius: var(--rayon);
  }
  .liste-cartes td { display: block; padding: 0; border: none; height: auto; }
  /* Le geste de masse (cases à cocher) reste un geste de poste de travail. */
  .liste-cartes td.col-coche { display: none; }
  .liste-cartes td:nth-child(2) { grid-area: vignette; align-self: center; width: auto !important; }
  .liste-cartes td:nth-child(2) .vignette { width: 56px; height: 56px; }
  .liste-cartes td:nth-child(3) { grid-area: reference; font-weight: 500; }
  .liste-cartes td:nth-child(4) { grid-area: espece; color: var(--encre-douce); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
  .liste-cartes td:nth-child(5) { grid-area: poids; text-align: right; color: var(--encre-douce); }
  /* La couleur se lit sur la photo, pas dans une colonne. */
  .liste-cartes td:nth-child(6) { display: none; }
  .liste-cartes td:nth-child(7) { grid-area: origine; color: var(--encre-attenuee); font-size: 12px; }
  .liste-cartes td:nth-child(8) { grid-area: statut; text-align: right; }
  .liste-cartes td:nth-child(9) { grid-area: montant; text-align: right; font-weight: 500; }
  /* L'état vide (une seule cellule en colspan) s'étale sur toute la carte. */
  .liste-cartes td[colspan] { grid-area: auto; grid-column: 1 / -1; }
}

/* Téléphone couché sous 720 px de large (iPhone SE, petits Android) : 390 px
   de haut au plus. La barre basse passe en rangée icône + mot, 44 px au lieu
   de 56, et le contenu récupère ce qu'elle rend. */
@media (max-width: 720px) and (max-height: 520px) {
  .onglets-bas .onglet { min-height: 44px; flex-direction: row; gap: 6px; padding: 4px 2px; font-size: 11px; }
  .onglets-bas .onglet .icone { width: 18px; height: 18px; }
  .volet-plus { bottom: calc(44px + env(safe-area-inset-bottom, 0px)); max-height: calc(100vh - 100px); }
  .contenu { padding-bottom: 72px; }
  /* Trois colonnes de 40 px : les treize entrées tiennent sans défiler dans
     les 275 px qui restent, là où deux colonnes de 48 px en cachaient cinq. */
  .volet-plus-grille { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .volet-plus-grille .entree { min-height: 40px; }
  .volet-plus-grille .entree:nth-child(odd) { border-right: 1px solid var(--bordure); }
  .volet-plus-grille .entree:nth-child(3n) { border-right: 0; }
}

/* Le mouvement lève une ambiguïté ; il n'est jamais nécessaire à la
   compréhension. Qui n'en veut pas n'en a aucun. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ======================= Veine d'Or (identité, 20.08.2026) =============== */
/* Motif kintsugi or + turquoise — docs/MOTIF-VEINE-DOR.md fait autorité.
   Règles : un geste par écran ; jamais sur une surface de travail ; jamais
   de bord d'image visible ; matière statique ; rien de simulé en code. */

/* Écrans publics « moments » (connexion, première connexion, changement de
   mot de passe, passeport introuvable, erreur 500) : le vert profond cède
   au fond Veine d'Or clair — amendement DESIGN.md du 20.08.2026, choix 1C
   retenu sur planches. Le motif remplace le filigrane ; le double filet doré reste
   l'apanage des supports de vente. */
body.connexion { background: var(--fond); }
body.connexion .connexion-filigrane { display: none; }
body.connexion .cadre-or::before,
body.connexion .cadre-or::after { display: none; }
body.connexion .connexion-carte { box-shadow: 0 10px 30px rgba(26, 29, 27, .10); }

/* Coin fondu — l'angle turquoise-or qui entre par le coin bas-gauche d'une
   zone « moment ». Jamais derrière du texte : le contenu passe au-dessus. */
/* Fond uni (décision du 10.09.2026) : le coin fondu et la veine ne portent
   plus d'image dans l'application. Les classes restent — les gabarits les
   posent — mais ne dessinent qu'un trait, ou rien. */
.vd-coin { display: none; }

/* Trait organique — la veine comme filet. Page À propos uniquement dans
   l'app : la seule page où Oikos parle de lui. */
.vd-veine { height: 1px; width: 150px; background: var(--accent); opacity: .5; }

/* Variante « marque » — amendement du 21.08.2026.
   L'identité paraît désormais sur les écrans de travail, mais confinée à la
   zone de marque de la barre latérale. L'interdit d'origine protégeait le
   jugement des couleurs : cette veine vit à l'opposé des photos et
   des colonnes de couleur, en dehors de la zone de contenu. Plus courte et
   plus discrète que la veine des « moments » — une signature, pas un décor. */
.vd-veine--marque { width: 128px; margin: 2px 0 14px 20px; }

/* Variante « centrée » — pied du passeport public.
   Le fondu part des deux bords : la veine ferme la page au lieu de pointer
   vers la droite comme celle des ouvertures de section. */
.vd-veine--centree { width: 168px; margin: 26px auto 14px; }

/* Pas de variante sombre ici : Oikos est en thème clair, délibérément — un
   fond sombre fausse l'appréciation des couleurs d'une photo. Une règle
   « prefers-color-scheme: dark » se déclencherait chez qui a son système en
   sombre et repeindrait le motif sur une interface restée claire. */

/* ---------------------------------------------------------------------------
   Veine d'Or sur les écrans de travail — décision du 21.08.2026.

   Deux couches, dosées sur planches comparatives :

   1. UN VOILE. Le fond kintsugi passe derrière toute la zone de travail, tenu
      à 38 % par un voile blanc superposé. Le marbre plein « faisait trop » ;
      en transparence il donne une profondeur, pas un décor.

   2. DES PAILLETTES. Sur les surfaces blanches, un semis d'or repris de la
      zone vive du motif, sous un voile blanc à 86 %. On le devine de près, il
      disparaît quand on travaille.

   Deux pièges rencontrés, tous deux évités ici :
   — `overflow: hidden` sur la carte casse l'en-tête collant du tableau ;
   — un pseudo-élément posé sur la carte passe DERRIÈRE le fond opaque des
     cellules et ne se voit jamais. D'où des fonds superposés, pas des calques.

   Le voile ne défile pas avec la liste ; en dessous de 720 px il repasse en
   `scroll`, un calque fixe étant erratique sur mobile.

   IL EST PORTÉ PAR UN CALQUE FIXE (`::before`), PAS PAR
   `background-attachment: fixed` — mesure du 03.09.2026 sur Oikos. Un fond
   attaché à la fenêtre n'a pas de calque à lui : à chaque cran de molette le
   navigateur repeint la zone de travail entière, image de 1920 px comprise,
   sur le fil principal. Sur le banc (Chromium, 3 s de molette sur une liste
   de 50 lignes) la rastérisation coûtait 2,6 fois plus qu'avec un calque, et
   c'est ce que le client a vu le jour où son stock a dépassé un écran :
   « la navigation saccade ». Un pseudo-élément en `position: fixed` reçoit
   son propre calque du compositeur, peint une fois, et le défilement ne le
   touche plus. Même image, même cadrage (le calque couvre la fenêtre entière
   comme le faisait `fixed`, puis se rogne à la zone de travail) : rien ne
   change à l'écran, seul le coût disparaît. Règle CSS-4 de l'auditeur.

   `isolation: isolate` ferme le groupe de fusion : le coin en `multiply`
   (plus bas) se compose contre ce calque et lui seul, pas contre la page
   entière. */
.contenu { isolation: isolate; }
.contenu::before { content: none; }

/* Les surfaces qui portent de la donnée restent opaques : rien ne se juge sur
   du doré. C'est la seule règle conservée de l'interdit d'origine, et elle
   protège l'argument gemmologique de Oikos. */
.contenu .carte,
.contenu .fiche-section,
.contenu .bloc-pliable {
  background-color: var(--surface);
}
.contenu .carte { box-shadow: 0 1px 3px rgba(26, 29, 27, .07); }

/* Les paillettes. Fond superposé plutôt que calque, pour les raisons dites
   plus haut. `td` redevient transparent afin de les laisser paraître. */
.contenu table.liste,
.contenu table.liste th,
.contenu .carte > .fiche-corps {
  background-color: var(--surface);
}
.contenu table.liste td { background-color: transparent; }

@media (max-width: 720px) {
  /* Le calque redevient un fond qui défile avec la page : `.contenu` est en
     `position: relative` (plus bas), `inset: 0` le couvre en entier. */
  .contenu::before { position: absolute; clip-path: none; }
}

/* ---------------------------------------------------------------------------
   Le coin fondu et la barre marbrée — piste retenue le 22.08.2026.

   Elle remplace les deux filets retirés le matin même. Ceux-ci découpaient une
   bande de deux pixels dans une photographie : à cette échelle il ne reste que
   du bruit. La leçon tient en une phrase — un motif photographique a besoin de
   place. On la lui donne ici : un coin de 520 px et une colonne entière.

   Ce qui rend le coin juste, et que les filets n'avaient pas : il occupe le
   VIDE. Sur une liste courte il monte du bas et remplit ce que la page laisse
   nu ; sur une liste longue le tableau le recouvre et il disparaît. Le motif
   s'efface exactement quand la donnée arrive — l'inverse d'un bandeau qui
   s'impose en haut de chaque écran quoi qu'il arrive.

   Le coin est en `fixed` : il appartient à la fenêtre, pas au flux. S'il
   défilait avec le contenu, il traverserait le tableau au lieu de rester dans
   son angle.
--------------------------------------------------------------------------- */
.contenu { position: relative; }
.contenu::after { content: none; }
/* Le contenu passe au-dessus du motif : la règle vit dans la section « La
   Colonne », en fin de feuille, et EXCLUT la colonne dressée — écrite ici sur
   `.contenu > *`, elle la rendait relative (piège de la planche du 13.09.2026). */

/* La colonne de navigation reçoit le motif vertical, jamais employé jusqu'ici.
   C'est la zone la plus sûre de l'écran : aucune photo n'y est jugée, aucun
   chiffre n'y est lu. */
.laterale { position: relative; }
.laterale::before { content: none; }
.laterale > * { position: relative; }

/* Sous 720 px la barre latérale cesse d'être une colonne : elle devient un
   bandeau horizontal d'icônes. Le motif `bordure` est vertical — l'y étirer
   reviendrait à l'employer dans une forme qui n'est pas la sienne, l'erreur
   même des filets retirés. On le retire, et le coin rétrécit pour ne pas
   occuper le tiers d'un écran de téléphone. */
@media (max-width: 720px) {
  .laterale::before { display: none; }
  .contenu::after { width: 260px; height: 200px; opacity: .24; }
}

/* État vide — variante Veine d'Or (choix 3C « coin fondu »). Opt-in par la
   classe modificatrice : seuls les états vides de module la portent, jamais
   les panneaux du tableau de bord ni les sous-listes de fiche. */
.etat-vide--signe { position: relative; overflow: hidden; }
.etat-vide--signe::before { content: none; }
.etat-vide--signe > * { position: relative; z-index: 1; }
.etat-vide--signe .picto { opacity: 1; color: var(--maison); }

/* Pages d'erreur dans l'app (404, 403) — le coin console l'impasse. */
.page-erreur { position: relative; min-height: 55vh; padding-top: 10vh; text-align: center; }

/* Moment Oikos — la toute première campagne / feuille / rapport d'un compte.
   Rendu par partials/flashes.html (catégorie « premiere »), une seule fois :
   le flash est consommé au premier affichage. Sans JavaScript : le lien
   « Continuer » recharge la page, l'instant est passé. */
.moment-premiere {
  position: fixed; inset: 0; z-index: 200;
  background: rgba(26, 29, 27, .45);
  display: flex; align-items: center; justify-content: center; padding: 24px;
}
.moment-premiere-carte {
  position: relative; overflow: hidden;
  background: var(--surface); border: 1px solid var(--bordure);
  border-radius: var(--rayon); max-width: 400px; width: 100%;
  padding: 34px 36px 30px; text-align: center;
  box-shadow: 0 14px 40px rgba(26, 29, 27, .25);
}
.moment-premiere-contenu { position: relative; z-index: 1; }
.moment-premiere-titre {
  font-family: var(--police-marque); font-size: 1.15rem; color: var(--primaire);
  letter-spacing: .02em;
}
.moment-premiere-texte { color: var(--encre-douce); margin: 10px 0 20px; }

/* Entrée directe par rôle sur l'écran de connexion — hors production. */
.demo-comptes { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--bordure); }
.demo-titre {
  margin: 0 0 8px; font-size: 11px; letter-spacing: .07em; text-transform: uppercase;
  color: var(--encre-attenuee);
}
.demo-forme { margin: 0; }
.demo-compte {
  display: flex; flex-direction: column; gap: 1px; width: 100%; text-align: left;
  padding: 7px 10px; margin-bottom: 6px; cursor: pointer;
  background: var(--surface); border: 1px solid var(--bordure); border-radius: 4px;
  font: inherit; color: var(--encre);
}
.demo-compte {
  transition: background-color var(--duree-court) var(--entree),
              border-color var(--duree-court) var(--entree);
}
.demo-compte:hover { background-color: var(--primaire-tint); border-color: var(--primaire); }
.demo-compte:focus-visible { outline: 2px solid var(--primaire); outline-offset: 1px; }
/* L'administratrice porte la marque : c'est le compte qui montre tout. */
.demo-compte--admin { border-color: var(--primaire); }
.demo-nom { font-weight: 500; }
.demo-role { font-size: 12px; color: var(--encre-attenuee); }

/* Console de l'éditeur. Elle reste distincte de l'espace client et ne montre
   que le registre et ses métadonnées d'exploitation (ADR 0001). */
body.pilotage { background: var(--fond); }
.pilotage-page { max-width: 1420px; margin: 0 auto; padding: 0 24px 60px; }
.pilotage-entete {
  min-height: 64px; display: flex; justify-content: space-between; align-items: center;
  gap: 20px; border-bottom: 1px solid var(--bordure);
}
.pilotage-identite { display: flex; align-items: center; gap: 12px; min-width: 0; }
.pilotage-marque { display: flex; align-items: center; gap: 8px; font-weight: 500; color: var(--primaire); }
.pilotage-marque span { font-family: var(--police-marque); font-size: 1.05rem; }
.pilotage-produit { padding-left: 12px; border-left: 1px solid var(--bordure); color: var(--encre-douce); }
.pilotage-compte { display: flex; align-items: center; gap: 10px; color: var(--encre-douce); }
.pilotage-compte > span { display: flex; flex-direction: column; align-items: flex-end; line-height: 1.35; }
.pilotage-compte strong { color: var(--encre); font-weight: 500; }
.pilotage-compte small { color: var(--encre-attenuee); }
.pilotage-mention { margin: 0 0 14px; font-size: 12px; letter-spacing: 0;
  text-transform: uppercase; color: var(--encre-attenuee); text-align: center; }

.pilotage-garde {
  display: grid; grid-template-columns: 40px minmax(0, 1fr) auto; align-items: center;
  gap: 16px; padding: 20px 0; border-bottom: 1px solid var(--bordure);
}
.pilotage-garde-icone {
  width: 40px; height: 40px; display: grid; place-items: center;
  color: var(--primaire); background: var(--primaire-tint); border: 1px solid #C9DDD0;
  border-radius: var(--rayon);
}
.pilotage-garde-icone .icone { width: 21px; height: 21px; }
.pilotage-garde h1 { margin: 0 0 3px; font-size: 1.28rem; }
.pilotage-garde p { margin: 0; max-width: 760px; color: var(--encre-douce); }
.pilotage-registre { display: flex; flex-direction: column; align-items: flex-end; }
.pilotage-registre span { font-size: 12px; color: var(--encre-attenuee); }
.pilotage-registre code { font-size: 13px; color: var(--encre); }

.pilotage-indicateurs {
  display: grid; grid-template-columns: repeat(5, minmax(120px, 1fr));
  margin: 18px 0 0; background: var(--surface); border: 1px solid var(--bordure);
  border-radius: var(--rayon);
}
.pilotage-indicateur { min-height: 72px; padding: 13px 16px; border-left: 1px solid var(--bordure); }
.pilotage-indicateur:first-child { border-left: 0; }
.pilotage-indicateur span { display: block; color: var(--encre-douce); font-size: 12.5px; }
.pilotage-indicateur strong { display: block; margin-top: 3px; font-size: 1.35rem; font-weight: 500;
  font-variant-numeric: tabular-nums; }
.pilotage-indicateur--succes strong { color: var(--succes); }
.pilotage-indicateur--avert strong { color: var(--avert); }
.pilotage-indicateur--danger strong { color: var(--danger); }

.pilotage-etat-services {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  min-height: 42px; padding: 6px 0; color: var(--encre-douce); font-size: 12.5px;
}
.pilotage-etat-separateur { width: 1px; height: 18px; margin: 0 6px; background: var(--bordure); }
.pilotage-section { margin-top: 22px; }
.pilotage-section-entete {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 16px; margin-bottom: 9px;
}
.pilotage-section-entete h2 {
  display: flex; align-items: center; gap: 7px; margin: 0; font-size: 1rem;
}
.pilotage-section-entete h2 .icone { width: 17px; height: 17px; color: var(--encre-douce); }
.pilotage-section-entete p { margin: 2px 0 0; color: var(--encre-attenuee); font-size: 12.5px; }
.pilotage-filtres { display: flex; align-items: center; justify-content: flex-end; gap: 7px; flex-wrap: wrap; }
.pilotage-filtres input, .pilotage-filtres select {
  transition: border-color var(--duree-court) var(--entree);
  height: 30px; padding: 0 9px; border: 1px solid var(--bordure);
  border-radius: var(--rayon); background: var(--surface); color: var(--encre); font: inherit;
}
.pilotage-filtres input { width: 230px; }
.pilotage-filtres select { width: 150px; }
.pilotage-filtres input:focus, .pilotage-filtres select:focus { border-color: var(--primaire); }

.pilotage-tableau table { min-width: 1140px; table-layout: auto; }
.pilotage-tableau table.liste tbody tr:hover { cursor: default; }
.pilotage-tableau table.liste td { height: 58px; }
.pilotage-tableau table.liste th:nth-child(1) { width: 19%; }
.pilotage-tableau table.liste th:nth-child(2) { width: 17%; }
.pilotage-tableau table.liste th:nth-child(3) { width: 11%; }
.pilotage-tableau table.liste th:nth-child(4) { width: 11%; }
.pilotage-tableau table.liste th:nth-child(5) { width: 13%; }
.pilotage-tableau table.liste th:nth-child(6) { width: 14%; }
.pilotage-tableau table.liste th:nth-child(7) { width: 15%; }
.pilotage-ligne-alerte td:first-child { box-shadow: inset 2px 0 0 var(--danger); }
.pilotage-client-nom, .pilotage-base-nom { display: block; overflow: hidden; text-overflow: ellipsis; }
.pilotage-client-nom { font-weight: 500; color: var(--encre); }
.pilotage-base-nom { max-width: 240px; color: var(--encre); }
.pilotage-meta { display: block; margin-top: 3px; color: var(--encre-attenuee); font-size: 11.5px; }
.pilotage-meta--seul { margin-top: 0; }
.pilotage-actions { display: flex; align-items: center; gap: 2px; flex-wrap: nowrap; }
.pilotage-actions form { margin: 0; }
/* Caisse Stripe : le rapprochement. Les constats sont du texte, pas des
   pastilles — un écart se lit, il ne se code pas en couleur seule. La barre de
   gauche donne la gravité au coup d'œil, la phrase la donne à qui ne voit pas
   les couleurs. */
.pilotage-lien-caisse { display: inline-block; min-height: 24px; line-height: 24px; color: var(--primaire); text-decoration: none; }
.pilotage-lien-caisse:hover { text-decoration: underline; }
/* Le nom mène à la fiche : 24 px au doigt, comme toute cible (WCAG 2.5.8). */
a.pilotage-client-nom { min-height: 24px; line-height: 24px; text-decoration: none; }
a.pilotage-client-nom:hover { text-decoration: underline; }
/* Un libellé masqué (`sr-only`, en absolu) posé dans une cellule prend la PAGE
   pour bloc conteneur quand le cadre défilant n'est pas positionné : il
   s'affiche à sa place dans le tableau large, et la page entière s'élargit
   d'autant au téléphone (591 px pour 390, mesuré sur Mydas le 27.09.2026). */
.pilotage-tableau .tableau-defilant { position: relative; }
.pilotage-anomalies { margin: 0; padding: 0; list-style: none; }
.pilotage-anomalie {
  padding: 2px 0 2px 9px; border-left: 2px solid var(--bordure);
  color: var(--encre-douce); font-size: 12px; line-height: 1.35;
}
.pilotage-anomalie + .pilotage-anomalie { margin-top: 5px; }
.pilotage-anomalie--danger { border-left-color: var(--danger); color: var(--danger); }
.pilotage-anomalie--avert { border-left-color: var(--avert); color: var(--encre); }
.pilotage-anomalie--info { border-left-color: var(--bordure); }
/* Six colonnes ici, sept au registre : la largeur de confort n'est pas la même,
   et les pourcentages du registre feraient déborder celle-ci. */
/* Six indicateurs ici, cinq au registre : la grille fixe du registre écraserait
   le sixième plutôt que de lui faire de la place. */
.pilotage-indicateurs--six { grid-template-columns: repeat(6, minmax(110px, 1fr)); }
@media (max-width: 1100px) {
  .pilotage-indicateurs--six { grid-template-columns: repeat(3, minmax(110px, 1fr)); }
  .pilotage-indicateurs--six .pilotage-indicateur:nth-child(3n+1) { border-left: 0; }
  .pilotage-indicateurs--six .pilotage-indicateur:nth-child(n+4) { border-top: 1px solid var(--bordure); }
}
.pilotage-caisse table { min-width: 1180px; }
.pilotage-echeance { display: block; font-weight: 500; font-variant-numeric: tabular-nums; }
/* « personne » n'est pas une adresse : elle ne doit pas se lire comme un nom
   de payeur qu'on aurait simplement mal orthographié. */
.pilotage-personne { color: var(--danger); font-weight: 500; }
.pilotage-caisse table.liste th:nth-child(n) { width: auto; }
.pilotage-caisse table.liste td { height: auto; padding-top: 10px; padding-bottom: 10px; }

/* Import Self-made : deux cartes côte à côte, gabarit puis dépôt — l'ordre de
   lecture est l'ordre du geste. */
.pilotage-registre .btn { margin-top: 7px; }
.pilotage-alerte {
  display: flex; align-items: flex-start; gap: 9px; margin-top: 18px; padding: 11px 14px;
  border-left: 3px solid var(--bordure); background: var(--surface);
}
.pilotage-alerte .icone { width: 18px; height: 18px; flex: 0 0 auto; margin-top: 1px; }
.pilotage-alerte--danger { border-left-color: var(--danger); color: var(--danger); }
.pilotage-alerte--avert { border-left-color: var(--avert); color: var(--encre); }
.pilotage-import-gabarits, .pilotage-import-actions {
  display: flex; align-items: center; justify-content: flex-end; gap: 8px; flex-wrap: wrap;
}
.pilotage-import-forme {
  display: grid; grid-template-columns: 190px minmax(260px, 680px); gap: 12px 18px;
  align-items: center; padding: 20px 0; border-top: 1px solid var(--bordure);
  border-bottom: 1px solid var(--bordure);
}
.pilotage-import-forme label { font-weight: 500; }
.pilotage-import-forme select, .pilotage-import-forme input[type="file"] {
  width: 100%; min-width: 0; padding: 9px 11px; border: 1px solid var(--bordure);
  border-radius: var(--rayon); background: var(--surface); color: var(--encre); font: inherit;
}
.pilotage-import-forme input[type="file"] { font-size: 12.5px; color: var(--encre-douce); }
.pilotage-import-forme select:focus, .pilotage-import-forme input[type="file"]:focus {
  border-color: var(--primaire); outline: none;
}
.pilotage-import-forme .pilotage-import-actions { grid-column: 2; justify-content: flex-start; }
.pilotage-import-cible { margin: 12px 0 16px; border-top: 1px solid var(--bordure); }
.pilotage-import-cible dl { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); margin: 0; }
.pilotage-import-cible dl > div { min-width: 0; padding: 12px 14px; border-bottom: 1px solid var(--bordure); }
.pilotage-import-cible dt { margin-bottom: 3px; color: var(--encre-attenuee); font-size: 12px; }
.pilotage-import-cible dd { margin: 0; overflow-wrap: anywhere; font-weight: 500; }
.pilotage-import-tableau { margin: 14px 0; }
.pilotage-import-tableau table { min-width: 620px; }
.pilotage-import-tableau table.liste th:first-child { width: 90px; }
.pilotage-import-lecture table.liste th:first-child { width: 50%; }
.pilotage-import-actions form { margin: 0; }

/* Jauge de sièges de l'écran Utilisateurs : une phrase, pas un widget. Elle
   passe à l'avertissement quand le plafond est atteint — le moment où l'admin
   doit la lire. */
.jauge-sieges { margin: -8px 0 16px; color: var(--encre-douce); font-size: 13px; }
.jauge-sieges--pleine { color: var(--avert); }

/* Réglage du plafond de sièges : un champ court, un bouton — le geste est
   rare, il n'a pas besoin de plus de place que la pastille qu'il jouxte. */
.pilotage-sieges { display: flex; align-items: center; gap: 6px; margin: 0; }
.pilotage-sieges input { width: 64px; padding: 4px 8px; border: 1px solid var(--bordure); border-radius: var(--rayon); font: inherit; font-variant-numeric: tabular-nums; }
.pilotage-sieges input:focus { border-color: var(--primaire); outline: none; }
.pilotage-sieges .btn { height: 28px; padding: 0 8px; font-size: 12px; }
.pilotage-actions .btn { height: 28px; padding: 0 8px; font-size: 12px; }

@media (max-width: 760px) {
  .pilotage-page { padding: 0 12px 40px; }
  .pilotage-entete { min-height: 58px; }
  .pilotage-produit, .pilotage-compte small { display: none; }
  .pilotage-garde { grid-template-columns: 40px minmax(0, 1fr); }
  .pilotage-registre { grid-column: 2; align-items: flex-start; }
  .pilotage-indicateurs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .pilotage-indicateur { border-left: 0; border-top: 1px solid var(--bordure); }
  .pilotage-indicateur:nth-child(1), .pilotage-indicateur:nth-child(2) { border-top: 0; }
  .pilotage-indicateur:nth-child(even) { border-left: 1px solid var(--bordure); }
  .pilotage-indicateur:last-child { grid-column: 1 / -1; }
  .pilotage-section-entete { align-items: stretch; flex-direction: column; }
  .pilotage-filtres { justify-content: flex-start; }
  .pilotage-filtres input { width: 100%; }
  .pilotage-filtres select { flex: 1; min-width: 150px; }
  .pilotage-import-forme { grid-template-columns: 1fr; }
  .pilotage-import-forme .pilotage-import-actions { grid-column: 1; }
  .pilotage-import-cible dl { grid-template-columns: 1fr 1fr; }
  .pilotage-import-cible dl > div:last-child { grid-column: 1 / -1; }
  .pilotage-import-actions { justify-content: flex-start; }
}

/* Titres de section de la page de recherche : le compte suit le nom, pour
   qu on sache d un coup d oeil ou porte la reponse. */
.recherche-titre { display:flex; align-items:baseline; gap:8px; font-size:1.05rem; margin:18px 0 8px; }
.recherche-titre .texte-attenue { font-size:.85rem; }
.recherche-bloc { margin-bottom: 18px; }
.recherche-suite { margin: 10px 14px 2px; }


/* ======================= Totaux d'une facture ============================ */
/* `totaux-facture`, `total-final`, `detail-calcul` et `discret` étaient
   appelées par les gabarits de facturation sans qu'aucune règle ne réponde :
   le bloc de totaux sortait en tableau nu, aligné à gauche, sans filet, et le
   total dû ne se distinguait pas de la ligne de TVA au-dessus. */
.totaux-facture {
  width: 100%; max-width: 360px; margin-left: auto; border-collapse: collapse;
}
.totaux-facture th, .totaux-facture td {
  padding: 5px 0; text-align: left; font-weight: 400;
  color: var(--encre-douce); vertical-align: baseline;
}
.totaux-facture td.montant {
  text-align: right; color: var(--encre); font-variant-numeric: tabular-nums;
}
.totaux-facture .discret { color: var(--encre-attenuee); font-size: 12.5px; }

/* Le total dû est le seul chiffre que le client cherche, et le seul qu'on ne
   veut pas le voir confondre avec la TVA juste au-dessus. */
.total-final th, .total-final td {
  border-top: 1px solid var(--bordure); padding-top: 9px;
  color: var(--encre); font-weight: 500;
}
.totaux-facture .total-final td.montant,
table.liste tfoot .total-final td.montant {
  font-size: 1.15rem; color: var(--primaire);
}
table.liste tfoot th, table.liste tfoot td {
  font-weight: 400; text-transform: none; letter-spacing: 0;
  color: var(--encre-douce); font-size: inherit;
}
table.liste tfoot .total-final th, table.liste tfoot .total-final td {
  color: var(--encre); font-weight: 500;
}

/* Le détail du calcul : replié, il ne doit pas peser autant que le total ;
   ouvert, il se lit comme un décompte, ligne à ligne. */
.detail-calcul {
  margin-top: 14px; padding-top: 10px; border-top: 1px solid var(--bordure);
}
.detail-calcul > summary {
  cursor: pointer; color: var(--encre-douce); font-size: 13px;
  transition: color var(--duree-court) var(--entree);
}
.detail-calcul > summary:hover { color: var(--encre); }
.detail-calcul > summary::marker { color: var(--encre-attenuee); }
/* Même ouverture que les blocs du calculateur : le décompte apparaît d'où il
   vient, sous son titre, au lieu de surgir sans origine. */
.detail-calcul[open] > *:not(summary) {
  animation: bloc-ouvre var(--duree-moyen) var(--sortie);
}
.detail-calcul ul {
  margin: 8px 0 0; padding-left: 18px;
  color: var(--encre-douce); font-size: 13px;
}
.detail-calcul li { padding: 1px 0; }
.detail-calcul pre {
  margin: 8px 0 0; padding: 10px 12px;
  background: var(--fond); border: 1px solid var(--bordure);
  border-radius: var(--rayon);
  font-size: 12.5px; line-height: 1.55;
  white-space: pre-wrap; overflow-x: auto;
}

/* Le tableau de saisie des lignes de facture : les champs remplissent leur
   colonne, et la cellule les laisse respirer — `table.liste` rogne par défaut,
   ce qui convient à une liste et pas à un formulaire. */
#lignes-facture td { white-space: normal; overflow: visible; }
#lignes-facture input { height: 30px; padding: 5px 8px; }

/* Le tableau des lignes d'une facture émise. `table.liste` interdit le retour
   à la ligne : bon pour une liste, où chaque cellule est courte. Ici la
   désignation identifie la pièce et dépasse la colonne de quarante signes —
   le tableau mesurait 854 px dans une carte de 776 et passait sous la colonne
   de droite. Les largeurs fixes rendent la place au libellé, qui s'enveloppe. */
table.lignes-emises { table-layout: fixed; }
table.lignes-emises td:first-child {
  white-space: normal; overflow-wrap: anywhere;
}
/* Le total dû ne se coupe jamais. Sa colonne est dimensionnée pour lui, mais
   une devise plus longue ou un montant à sept chiffres reprendrait deux
   pixels de trop : plutôt que l'ellipse — qui transforme un montant en
   demi-montant sans le dire — le chiffre déborde vers la gauche, où la
   cellule « Total » lui laisse quatre colonnes de vide. */
table.lignes-emises tfoot .total-final td.montant { overflow: visible; }


/* La clé d'API, affichée une seule fois. Elle doit se sélectionner d'un geste
   et se relire sans ambiguïté : en police proportionnelle, un « l » ressemble
   à un « 1 », et une clé recopiée à un caractère près ne marche jamais. */
.cle-api-secret {
  display: block;
  padding: 10px 12px;
  margin: 6px 0;
  background: var(--surface);
  border: 1px solid var(--bordure);
  border-radius: 6px;
  font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
  font-size: 13px;
  line-height: 1.5;
  word-break: break-all;
  user-select: all;
}

/* ======================= Révision responsive du 08.09.2026 ===============
   Mesurée par scripts/banc_responsive.py à quatre formats : téléphone debout
   (390 × 844), téléphone couché (844 × 390), tablette (768 × 1024), bureau
   (1440 × 900). Ces règles vivent en fin de feuille : elles doivent battre
   les règles de composant écrites plus haut, à spécificité égale.
   ========================================================================= */

/* --- Entre 720 et 960 px (tablette debout, téléphone couché) ------------
   Fil d'Ariane, recherche et compte se partagent 700 px : le fil se coupe
   avec des points, la recherche garde un minimum, le nom se tronque. Sans
   cela, « Déconnexion » sortait du cadre à 768 px sur toute fiche au titre
   long. */
@media (max-width: 960px) {
  .fil-ariane { flex: 0 1 auto; min-width: 0; }
  .fil-ariane .courant { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
  .recherche-globale { min-width: 110px; flex: 1 1 110px; }
  .bandeau-compte { flex: none; gap: 6px; }
  .bandeau-compte > span { max-width: 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .bandeau-compte .btn { padding: 0 8px; }
}

/* --- Les rangées du calculateur, sous 720 px -----------------------------
   Six cellules en ligne ne tiendront jamais sur 390 px : le sélecteur et les
   champs se réduisaient à 18 px. Chaque rangée devient une carte de trois
   lignes : la source, le libellé, puis quantité · prix · total · croix. Le
   gabarit reste le MÊME tableau (Alpine clone la rangée) : c'est la grille
   qui replace les cellules. Les largeurs en ligne des cellules demandent
   `!important` — un style en ligne ne se bat pas autrement. */
@media (max-width: 720px) {
  .bloc-corps table, .bloc-corps tbody { display: block; width: 100%; }
  .ligne-calcul {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.3fr) minmax(0, 1.3fr) 34px;
    gap: 6px 8px; align-items: center;
    padding: 10px 0; border-bottom: 1px solid var(--bordure);
  }
  .ligne-calcul:last-child { border-bottom: 0; }
  .ligne-calcul > td { display: block; width: auto !important; padding: 0; height: auto; border: 0; }
  .ligne-calcul > td:has(select), .ligne-calcul > td:has(.selecteur),
  .ligne-calcul > td:has(input[name="libelle"]) { grid-column: 1 / -1; }
  .ligne-calcul > td.montant { text-align: right; }
}

/* --- Cibles tactiles (WCAG 2.5.8 : 24 px au moins) -----------------------
   Une case à cocher native fait 13 px ; 16 px partout, 24 px au doigt. Les
   liens posés en bloc (fil d'Ariane, faits d'une fiche, cellules) gagnent
   une boîte de 32 px par un remplissage compensé d'une marge négative : la
   mise en page ne bouge pas d'un pixel, seule la zone qui répond grandit. */
.contenu input[type="checkbox"], .contenu input[type="radio"] {
  width: 16px; height: 16px; margin: 0; vertical-align: middle; flex: none;
}
.contenu input[type="file"] { min-height: 32px; }
@media (pointer: coarse) {
  .contenu input[type="checkbox"], .contenu input[type="radio"] { width: 24px; height: 24px; }
  table.liste .col-coche { width: 44px; }
  .contenu select, .contenu textarea,
  .contenu input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]) { min-height: 32px; }
  .contenu .btn, .contenu button:not(.onglet) { min-height: 32px; }
  details > summary { padding-top: 4px; padding-bottom: 4px; }
  .fil-ariane a, .faits dd a, .contenu td a, .contenu dd a, .index-chiffres .libelle a {
    display: inline-block; padding: 6px 0; margin: -6px 0;
  }
  .miniature-retirer {
    display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px;
  }
}

/* --- Colonne unique : le panneau de résumé cesse de coller -----------------
   Sous 1100 px la fiche n'a plus qu'une colonne ; un panneau collant de
   240 px confisquait 60 à 74 % d'un téléphone couché et masquait le
   formulaire qu'il résume. Écrit ici, après la règle collante, pour la battre. */
@media (max-width: 1100px) {
  .panneau-resume { position: static; }
}

/* --- Les rangées d'une nouvelle vente, sous 720 px --------------------------
   Même geste que le calculateur : type et objet sur la première ligne, la
   désignation sur la deuxième, coût · prix · croix sur la troisième. L'en-tête
   du tableau part : chaque champ porte son propre texte d'invite. */
@media (max-width: 720px) {
  .tableau-lignes, .tableau-lignes tbody { display: block; width: 100%; }
  .tableau-lignes thead { display: none; }
  .ligne-vente {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr) 34px;
    gap: 6px 8px; align-items: center;
    padding: 10px 0; border-bottom: 1px solid var(--bordure);
  }
  .ligne-vente:last-child { border-bottom: 0; }
  .ligne-vente > td { display: block; width: auto !important; padding: 0; height: auto; border: 0; }
  .ligne-vente > td:has(input[name="ligne_designation"]) { grid-column: 1 / -1; }
  .ligne-vente > td.montant { text-align: right; }
}

/* --- Liens posés dans une phrase de fiche, au doigt --------------------------
   Le nom du client sous le titre d'une vente, le numéro de la facture liée :
   des liens de 21 px de haut. Même compensation que plus haut : la boîte qui
   répond grandit, la ligne ne bouge pas. */
@media (pointer: coarse) {
  /* Un lien qui EST un composant garde sa boîte : la rangée d'une zone perdait sa
     grille et ses marges, la pastille d'un filtre son rembourrage — le texte
     collait au bord, sur toutes les listes filtrées (banc du 16.09.2026).
     Repayé le 19.09.2026 : la ligne d'une réserve, d'un avis de défaut, d'une échéance
     redevenait une phrase collée au bord de sa carte, et le banc ne le voit pas (rien ne
     déborde). `tests/test_liens_composants.py` balaye désormais les gabarits : toute classe
     de lien que cette feuille EMPILE EN COLONNE s'ajoute ici. Pas les autres : le banc complet
     du même jour a montré que `.fonction`, `.nom` ou `.appoint` DOIVENT à cette règle leur
     retour à la ligne et leur hauteur au doigt — les excepter défait des écrans validés. */
  .contenu a:not(.btn):not(.onglet):not(.entree):not(.pagination a):not(.miniature-choix):not(.zone-rangee):not(.filtre-pastille):not(.ligne-reserve):not(.tuile-module) {
    display: inline-block; padding: 6px 0; margin: -6px 0;
  }
  .filtre-pastille { display: inline-flex; align-items: center; min-height: 44px; padding: 0 14px; }
}

/* --- Deux grilles sorties des styles en ligne (GABARIT-4) -----------------
   Les cases à publier du passeport, et le champ + bouton d'une proposition de
   fiche : en classe, la requête média peut les replier. */
.grille-cases { display: grid; grid-template-columns: 1fr 1fr; gap: 2px 8px; margin-bottom: 8px; }
.grille-champs--et-bouton { grid-template-columns: 1fr auto; align-items: end; }
@media (max-width: 480px) {
  .grille-cases, .grille-champs--et-bouton { grid-template-columns: 1fr; }
}

/* Un champ sur toute la largeur de la grille. En classe et non en style en
   ligne : `grid-column: span 2` en ligne créait, sur une grille réduite à une
   colonne (téléphone), une seconde piste implicite de 0 px où les champs
   suivants se plaçaient — sélecteurs de 22 px (Kronos, 08.09.2026). */
.champ--large { grid-column: 1 / -1; }

/* Une colonne de prose (th.prose / td.prose) se laisse écraser lettre par
   lettre plutôt que de faire déborder le tableau : « Marque et cuvée » sur
   14 px de large et 246 de haut (Baccus, 08.09.2026). Avec un minimum, le
   tableau déborde franchement et son cadre défile. La barre de statut d'une
   fiche passe à la ligne au lieu de sortir du cadre (/vrac/1 à 768 px). */
table.liste th.prose, table.liste td.prose { min-width: 180px; }
.barre-statut { flex-wrap: wrap; min-width: 0; }

/* ==========================================================================
   Au doigt, dans la rue — checkup du 13.09.2026
   Mesuré par scripts/parcours_telephone.py, qui JOUE les gestes des trois rôles
   au lieu de photographier des pages au repos. Le banc rendait 0 constat sur
   les sept formats : aucun des défauts ci-dessous ne se voit sans toucher.
   ========================================================================== */

/* Safari (iPhone, iPad) agrandit la page au toucher de tout champ dont le texte
   fait moins de 16 px, et ne la rend pas : compter ses signatures, signaler un
   incident, saisir un billet, chercher — chaque saisie obligeait à pincer
   l'écran pour revenir. Tous les champs étaient à 14 px (`font: inherit`).
   Et une cible qu'on vise debout, d'une main, mesure 44 px, pas 32. */
@media (pointer: coarse) {
  /* `!important`, et c'est le seul cas : la règle vient de la plateforme, pas du
     composant. `.champ select { font: inherit }` et les tailles posées sur un
     filtre ou une cellule la battaient en spécificité — les sélecteurs et les
     zones de texte restaient à 14 px, mesurés par le parcours. */
  input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="hidden"]):not([type="submit"]):not([type="button"]),
  select, textarea { font-size: 16px !important; }
  .champ input:not([type="checkbox"]):not([type="radio"]):not([type="file"]), .champ select, .champ-nu { height: 44px; }
  .contenu select,
  .contenu input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]):not([type="file"]) { min-height: 44px; }
  .contenu .btn, .contenu button:not(.onglet) { min-height: 44px; }
  .recherche-globale input { height: 38px; }
}

/* Le bouton d'envoi d'un formulaire vivait dans l'en-tête, AU-DESSUS des champs.
   Une fois le dernier champ rempli — clavier ouvert, qui mange la moitié de
   l'écran — il fallait remonter le chercher. Sous 720 px, les actions d'un
   formulaire à en-tête passent après ses champs, sous le pouce. Un sélecteur qui
   balaye plutôt qu'une liste : remise, déplacement, campagne, tiers, poste — et
   le formulaire écrit demain. */
@media (max-width: 720px) {
  form:has(> .fiche-entete > .fiche-actions) { display: flex; flex-direction: column; }
  form > .fiche-entete:has(> .fiche-actions) { display: contents; }
  form > .fiche-entete > .fiche-titre { margin-bottom: 12px; }
  form > .fiche-entete > .fiche-actions { order: 1; margin-top: 12px; }
  form > .fiche-entete > .fiche-actions .btn { flex: 1 1 0; justify-content: center; }
}

/* Un avis sur un téléphone : le titre, puis le texte, puis l'action. En rangée,
   « Codex à signer » se pliait mot par mot dans une colonne de 50 px à côté
   de son texte (tableau de bord du chef, planche du parcours). */
@media (max-width: 720px) {
  .avis { flex-direction: column; align-items: stretch; gap: 4px; }
  .avis-actions { margin-left: 0; margin-top: 8px; }
  .avis-actions .btn { flex: 1 1 auto; justify-content: center; }
}

/* Le message de confirmation, fixé en haut, recouvrait le titre de l'écran qu'il
   confirmait — le numéro de la feuille qu'on vient de remettre, « Mon pointage »
   — et ne partait qu'à la page suivante. Sur un téléphone il vit au-dessus de
   la barre basse, près du pouce ; une confirmation s'efface après cinq secondes,
   une erreur reste jusqu'au toucher (oikos.js). */
@keyframes flash-sort { to { opacity: 0; visibility: hidden; } }
@media (max-width: 720px) {
  .flashes { top: auto; bottom: calc(66px + env(safe-area-inset-bottom, 0px)); }
  .flash-success, .flash-info {
    animation: flash-entre var(--duree-long) var(--sortie), flash-sort .4s ease 5s forwards;
  }
}
/* Couché, 375 px de haut : au-dessus de la barre basse, le message prenait la
   place du contenu. Il se pose sur la recherche du bandeau, le temps de se lire. */
@media (max-width: 720px) and (max-height: 520px) {
  .flashes { top: 6px; bottom: auto; }
  .flash { padding: 6px 12px; }
}

/* Les heures à valider, sous 720 px : une carte par journée — jour et durée,
   dialogueur, puis début · pause · fin, puis note et « Valider » sous le pouce.
   Sept colonnes dont quatre champs ne tenaient qu'en défilant de côté, et
   « Valider » sortait de l'écran à chaque ligne : le geste du chef, le soir.
   Jusqu'à 960 px et non 720 : sur un téléphone couché large ou une tablette
   debout, la barre latérale revient et le tableau ne tient toujours pas —
   « Valider » restait hors du cadre (planche du banc, 13.09.2026). */
@media (max-width: 960px) {
  .contenu table.liste-heures, table.liste-heures tbody { display: block; }
  table.liste-heures thead { display: none; }
  table.liste-heures tr {
    display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
    grid-template-areas:
      "jour jour travaille"
      "dialogueur dialogueur dialogueur"
      "debut pause fin"
      "validation validation validation";
    gap: 6px 10px; padding: 12px 14px; border-bottom: 1px solid var(--bordure);
  }
  /* `table.liste td` pose hauteur, marge interne et `overflow: hidden` : sans la
     même spécificité, les champs de 44 px étaient rognés dans leur cellule. */
  table.liste-heures td { display: block; padding: 0; border: 0; height: auto; overflow: visible; white-space: normal; }
  table.liste-heures td.heures-jour { grid-area: jour; font-weight: 500; }
  table.liste-heures td.heures-travaille { grid-area: travaille; }
  table.liste-heures td.heures-dialogueur { grid-area: dialogueur; color: var(--encre-douce); }
  table.liste-heures td:nth-child(3) { grid-area: debut; }
  table.liste-heures td:nth-child(4) { grid-area: pause; }
  table.liste-heures td:nth-child(5) { grid-area: fin; }
  table.liste-heures td.heures-champ::before {
    content: attr(data-libelle); display: block; margin-bottom: 2px;
    font-size: 12px; color: var(--encre-attenuee);
  }
  /* `!important` contre les largeurs posées en ligne pour le tableau de bureau. */
  table.liste-heures td input { width: 100%; max-width: none !important; }
  table.liste-heures td.heures-validation { grid-area: validation; display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
  table.liste-heures td.heures-validation input[name="note"] { flex: 1 1 140px; width: auto; }
  table.liste-heures td.heures-validation .btn { margin-left: 0 !important; }
  table.liste-heures tr:has(> td[colspan]) { display: block; }
}

/* L'état vide d'une liste dans un tableau qui défile : centré sur six colonnes,
   son texte partait hors de l'écran (« Un incident se signale depuis le
   terrain, jamais cc… »). Borné à l'écran, il se lit en entier. */
@media (max-width: 720px) {
  td > .etat-vide { max-width: calc(100vw - 60px); }
}

/* Le bouton du pointage, en tête du tableau de bord de qui récolte : pleine
   largeur sur un téléphone, là où le pouce se pose en ouvrant l'application. */
.action-du-jour { margin: 0 0 16px; }
@media (max-width: 720px) {
  .action-du-jour .btn { display: flex; width: 100%; justify-content: center; }
}

/* Un champ qu'on atteint en défilant — tabulation, ancre, validation du
   navigateur — s'arrêtait SOUS une barre : sous la barre basse fixe, ou sous le
   bandeau collant quand le défilement l'amenait en haut. « Valider » d'une carte
   d'heures restait caché, et le toucher tombait sur la recherche (paysage court,
   parcours du 13.09.2026). La marge de défilement tient compte des deux. */
html { scroll-padding-top: calc(var(--bandeau) + 8px); }
@media (max-width: 720px) {
  html { scroll-padding-bottom: 72px; }
}
@media (max-width: 720px) and (max-height: 520px) {
  html { scroll-padding-bottom: 56px; }
}

/* Le numéro d'une feuille est un titre, pas une colonne de chiffres : en-tête en
   colonne sur un téléphone, `.tabulaire` le poussait contre le bord droit. */
.fiche-titre h1.tabulaire { text-align: left; }

/* Les faits d'une fiche sur un téléphone étroit : la colonne des libellés prend
   sa largeur maximale (« Nulles éliminées à la pré-vérification ») et les valeurs
   se pliaient mot par mot — le rapport d'opération que le comité reçoit (3.3.9)
   se lisait sur trois lignes par valeur. Deux cinquièmes pour le libellé. */
@media (max-width: 480px) {
  .faits { grid-template-columns: minmax(0, 2fr) minmax(0, 3fr); column-gap: 12px; }
}

/* =========================================================================
   Oikos — les écrans des chantiers et des métiers
   ========================================================================= */

/* L'en-tête d'un module : le nom, puis la phrase de son cahier qui dit à quoi
   il sert. Un sous-module parmi soixante-dix se reconnaît à son but. */
.entete-module { margin: 0 0 14px; }
.entete-module h1 { margin: 0 0 4px; }
.entete-module p { margin: 0; max-width: 72ch; }

/* Les tuiles des sous-modules d'un métier. `auto-fill` et non un nombre de
   colonnes : une grille figée ne se réduit pas sur un téléphone, et le style
   en ligne est interdit (auditer-socle GABARIT-4). */
.grille-modules {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr));
}
.tuile-module {
  display: flex; flex-direction: column; gap: 6px; min-height: 112px;
  padding: 14px 16px; background: var(--surface);
  border: 1px solid var(--bordure); border-radius: var(--rayon);
  color: var(--encre); text-decoration: none;
  transition: border-color var(--duree-court) var(--entree);
}
.tuile-module:hover { border-color: var(--primaire); color: var(--encre); text-decoration: none; }
.tuile-module:focus-visible { outline: 2px solid var(--primaire); outline-offset: 2px; }
.tuile-module .tuile-titre { display: flex; align-items: center; gap: 8px; font-weight: 500; color: var(--primaire-fonce); }
.tuile-module .tuile-but { color: var(--encre-douce); font-size: 13px; line-height: 1.45; }
.tuile-module .tuile-nombre { margin-top: auto; color: var(--encre-attenuee); font-size: 12.5px; }

/* Les filtres d'une liste : ils passent à la ligne au lieu de pousser la page. */
.filtres-liste { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.filtres-liste select, .filtres-liste input[type="search"] {
  height: 32px; min-width: 0; max-width: 100%; padding: 0 10px;
  border: 1px solid var(--bordure); border-radius: var(--rayon); font: inherit; background: var(--surface);
}
.filtres-liste input[type="search"] { width: 220px; }
@media (max-width: 720px) {
  .filtres-liste { width: 100%; }
  .filtres-liste select, .filtres-liste input[type="search"] { flex: 1 1 100%; width: 100%; height: 40px; }
}

/* Le résultat d'un calcul : la valeur, puis le détail ligne par ligne. */
.calcul { border-left: 3px solid var(--bordure); padding: 8px 0 8px 14px; margin: 0 0 12px; }
.calcul--conforme { border-left-color: var(--succes); }
.calcul--hors-fiche { border-left-color: var(--danger); }
.calcul h3 { margin: 0 0 4px; font-size: 14px; }
.calcul ol { margin: 6px 0 0; padding-left: 18px; color: var(--encre-douce); }
.calcul .valeur-calcul { font-size: 1.15rem; font-weight: 500; }

/* Un registre append-only se dit à l'écran, avant qu'on cherche le bouton
   « Modifier » qui n'existe pas. */
.bandeau-registre { margin: 0 0 14px; }

/* La localisation d'un chantier : bâtiments, niveaux, locaux, zones. */
.arbre-localisation, .arbre-localisation ul { list-style: none; margin: 0; padding: 0; }
.arbre-localisation ul { padding-left: 18px; border-left: 1px solid var(--bordure); margin-left: 6px; }
.arbre-localisation li { padding: 4px 0; }
.arbre-localisation .noeud { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; }
.formulaire-en-ligne { display: flex; flex-wrap: wrap; gap: 8px; align-items: flex-end; margin: 6px 0; }
.formulaire-en-ligne .champ { margin: 0; }
@media (max-width: 720px) {
  .formulaire-en-ligne > * { flex: 1 1 100%; }
}

/* Les droits par sous-module : un métier est un groupe de cases. */
.droits-metier { border: 1px solid var(--bordure); border-radius: var(--rayon); padding: 8px 12px; margin: 0 0 8px; }
.droits-metier legend { font-weight: 500; padding: 0 4px; }
.grille-cases-droits {
  display: grid; gap: 4px 16px;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 240px), 1fr));
}
.grille-cases-droits label { display: flex; gap: 8px; align-items: center; min-height: 32px; font-weight: 400; }

/* ————— La Colonne — seules classes autorisées (docs/MOTIF-COLONNE.md § 4) ————— */

/* Le portique — la connexion. Posé en fixe, derrière la carte, jamais devant. */
.col-portique {
  position: fixed; inset: 0; pointer-events: none;
  background: url("/static/img/motif/portique.svg") center/contain no-repeat;
  opacity: .5;
}

/* Le fût — la barre latérale. La barre est déjà positionnée (collante) : lui
   imposer `relative` la décollerait, et son pied repasserait sous le bord. */
.col-fut { overflow: hidden; }
.col-fut:not(.laterale) { position: relative; }
.col-fut::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: url("/static/img/motif/colonne-terre.svg") 50% 38%/640px auto no-repeat;
  opacity: .12;
}
.col-fut > * { position: relative; }

/* La colonne dressée — le vide de la zone de travail. En fixe : elle appartient
   à la fenêtre, pas au flux, sinon elle traverserait un tableau. */
.col-dressee {
  position: fixed; right: 56px; bottom: -40px; height: 640px;
  opacity: .2; pointer-events: none;
}
/* Le piège payé sur la planche du 13.09.2026 : une règle générale sur les
   enfants du contenu rendait la colonne relative ; elle entrait dans le flux et
   poussait l'état vide hors de l'écran. Toute règle générale l'exclut.
   `:where()` ramène la spécificité à zéro : un enfant qui a SA position (un
   bandeau collant) la garde, les autres passent au-dessus de la colonne. */
:where(.contenu > :not(.col-dressee)) { position: relative; z-index: 1; }

/* Le chapiteau — le signe : état vide, marque de la barre latérale. */
.col-chapiteau { width: 44px; height: 44px; }

/* L'ouverture — le début d'une section claire, alignée sur le texte. */
.col-ouverture { display: block; width: 160px; height: 24px; }

/* Le stylobate — le pied d'une section profonde, sur toute la largeur. */
.col-stylobate {
  position: absolute; left: 0; right: 0; bottom: 22px; height: 48px; pointer-events: none;
  background: url("/static/img/motif/stylobate-pierre.svg") center/100% 100% no-repeat;
  opacity: .55;
}

/* Le filigrane — le blanc d'une carte de fiche (demande de Johann du 14.09.2026 :
   « la colonne sobrement et élégamment aux endroits où il y a trop de blanc »).
   La carte réserve sa marge droite : le texte ne passe jamais sous la colonne. */
.col-filigrane { position: relative; }
.col-filigrane::after {
  content: ""; position: absolute; top: 18px; bottom: 18px; right: 44px; width: 96px;
  pointer-events: none;
  background: url("/static/img/motif/colonne-terre.svg") center/contain no-repeat;
  opacity: .16;
}
@media (min-width: 1100px) { .col-filigrane { padding-right: 150px; } }

/* Les formats (MOTIF-COLONNE.md § 7) : on retire, on n'ajoute jamais. */
@media (max-width: 1099px) {
  .col-filigrane::after { content: none; }
}
@media (max-width: 720px) {
  .col-portique, .col-dressee { display: none; }
  .col-fut::before { content: none; }
}
@media (max-height: 560px) {
  .col-portique { display: none; }
}

/* Un titre posé directement dans une carte (tableau de bord) : sans marge, il
   collait au filet de la carte — vu sur la planche du 14.09.2026. */
.carte > h2 { margin: 0; padding: 14px 16px 6px; font-size: 1rem; }
/* Le texte posé sous ce titre quand la carte n'a rien d'autre à montrer (« Rien à faire
   pour l'instant », tableau de bord) : la carte n'a aucune marge intérieure, et sans cette
   règle la phrase se tenait à 1 px du filet — mesuré au téléphone le 28.09.2026. Une classe
   plutôt que `.carte > h2 + p` : ce sélecteur doublerait la marge des cartes `.fiche-section`
   (contrats, métrés, stock, bibliothèque), qui ont déjà la leur. */
.carte > .carte-texte { margin: 0; padding: 4px 16px 14px; }
/* L'icône d'une tuile : un `svg` sans dimension prend toute la largeur libre —
   planche du 14.09.2026, des icônes de 140 px dans chaque tuile. */
.tuile-module .icone { width: 18px; height: 18px; flex: none; }

/* ===================== Commerce du bâtiment (cahier 02 § 1.2 à 1.6) ====================== */
/* Le prix d'un ouvrage : le seul chiffre de la fiche qu'on vient chercher. */
.chiffre-cle { font-size: 1.5rem; font-weight: 500; margin: 4px 0 12px; font-variant-numeric: tabular-nums; }
.case-a-cocher { display: flex; gap: 8px; align-items: center; min-height: 44px; }
/* Un chapitre de devis coupe le tableau en groupes lisibles, sans casser le défilement du cadre. */
.liste .ligne-chapitre th {
  text-align: left; text-transform: none; letter-spacing: 0; font-size: 13.5px;
  color: var(--encre); background: var(--primaire-tint); padding-top: 10px;
}
/* L'édition d'une position de devis se déplie sous sa ligne, jamais dans une fenêtre flottante. */
.liste .ligne-edition td { padding-top: 0; border-top: none; }
.liste .ligne-edition summary { color: var(--encre-attenuee); font-size: 13px; cursor: pointer; min-height: 32px; }
.liste .ligne-edition .grille-champs { margin: 10px 0; }
.liste .ligne-signalee td { background: var(--avert-tint); }
.liste-simple { list-style: none; margin: 0; padding: 0; }
.liste-simple li { padding: 6px 0; border-bottom: 1px solid var(--bordure); }
.liste-simple li:last-child { border-bottom: none; }
/* La saisie d'une situation : des champs de chiffres dans le tableau, assez larges pour
   « 1'250.500 », assez étroits pour tenir à côté du libellé. */
.liste-saisie input { width: 9.5em; max-width: 100%; }
details.avenant { border-top: 1px solid var(--bordure); padding: 10px 0; }
details.avenant > summary { cursor: pointer; min-height: 32px; }
.fiche-actions form { display: inline-flex; margin: 0; }
.carte-entete { flex-wrap: wrap; }
/* Au téléphone, la saisie d'une situation passe en une carte par position (cahier 02
   § 1.6) : les champs de cumul sous le libellé, pleine largeur, et leur étiquette —
   masquée au lecteur de tableau — redevient visible. */
@media (max-width: 720px) {
  .liste-saisie thead { display: none; }
  .liste-saisie, .liste-saisie tbody, .liste-saisie tr, .liste-saisie td { display: block; width: 100%; }
  .liste-saisie tr { border-bottom: 1px solid var(--bordure); padding: 8px 0; }
  table.liste.liste-saisie td { height: auto; border: none; padding: 4px 0; text-align: left; white-space: normal; }
  .liste-saisie label.sr-only {
    position: static; width: auto; height: auto; margin: 0 0 4px; overflow: visible; clip: auto;
    white-space: normal; display: block; font-size: 13px; color: var(--encre-attenuee);
  }
  .liste-saisie input { width: 100%; }
  /* Sans en-tête de colonne, deux montants se confondraient : chaque cellule
     chiffrée dit ce qu'elle porte. */
  .liste-saisie td[data-libelle]::before {
    content: attr(data-libelle); display: block; font-size: 13px; color: var(--encre-attenuee);
  }
}
/* Les suppressions se déplient, jamais un bouton rouge posé au milieu d'une fiche. */
details.zone-sensible > summary { cursor: pointer; min-height: 32px; color: var(--encre-attenuee); }
details.zone-sensible form { margin-top: 8px; }
.ligne-avec-action { display: flex; align-items: center; justify-content: space-between; gap: 12px; }

/* ======================= Les actions à portée du doigt (16.09.2026) ========
   Rétroporté de Mydas le jour même. Là-bas, au téléphone debout, « Terminer »
   vivait à 873 px du bord droit de la liste des tâches, et le banc rendait
   zéro constat : le tableau défilait dans son cadre, et « hors cadre »
   exempte tout ce qu'un ancêtre rogne. Ici, le banc équipé a compté
   22 constats sur 4 écrans — tâches, utilisateurs (« Supprimer » hors de vue
   même au bureau), bibliothèque d'ouvrages, métrés.
   Une donnée peut défiler dans son cadre ; une action, non.
   ========================================================================= */

/* --- La liste empilable -----------------------------------------------------
   Une `table.liste--empilable` reste un tableau tant qu'elle tient dans son
   cadre. Quand elle n'y tient plus (classe `empilee`, mesurée EN TABLEAU par
   oikos.js), chaque rangée devient une pile : le titre en tête (`cel-titre`),
   un fait par ligne derrière le libellé de sa colonne (`data-libelle`, repris
   du `th` par le script), les actions en pied (`cel-actions`), la principale
   à droite, sous le pouce (`action-principale`). Aucun seuil en pixels : la
   même liste reste un tableau au bureau et s'empile dans une colonne de
   tableau de bord. Les faits passent sur deux colonnes dès que la pile a la
   largeur d'une tablette. */
table.liste td.cel-actions { width: 1%; text-align: right; white-space: nowrap; }
/* En tableau, les formulaires d'une cellule d'actions restent côte à côte :
   un `<form>` est un bloc, et deux gestes s'empilaient dans la rangée
   (Baccus, publications : un `display: flex` posé en ligne sur la cellule). */
table.liste td.cel-actions > form { display: inline-block; vertical-align: middle; }
table.liste td.cel-icone { width: 34px; }
.tableau-defilant.empilee { overflow: visible; }
.empilee > table.liste--empilable,
.empilee > table.liste--empilable > tbody { display: block; width: 100%; min-width: 0; }
.empilee > table.liste--empilable > thead { display: none; }
.empilee > table.liste--empilable > tbody > tr:not([hidden]) {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr));
  gap: 6px 28px; align-items: start;
  padding: 14px 16px; border-bottom: 1px solid var(--bordure);
}
.empilee > table.liste--empilable > tbody > tr:nth-last-child(1 of :not([hidden])) { border-bottom: 0; }
/* `width` en `!important` : une largeur posée en ligne sur une cellule
   (`style="width:220px"`) fixerait la largeur de sa ligne dans la pile — un
   style en ligne ne se bat pas autrement. */
.empilee > table.liste--empilable > tbody > tr > td {
  display: flow-root; width: auto !important; height: auto; min-width: 0; padding: 0; border: 0;
  white-space: normal; overflow: visible; overflow-wrap: anywhere; text-align: left;
}
.empilee > table.liste--empilable > tbody > tr > td:empty { display: none; }
.empilee > table.liste--empilable > tbody > tr > td:first-child { box-shadow: none; }
.empilee > table.liste--empilable > tbody > tr.ligne-retard { box-shadow: inset 3px 0 0 var(--danger); }
.empilee > table.liste--empilable > tbody > tr.ligne-urgence { box-shadow: inset 3px 0 0 var(--avert); }
/* Le libellé flotte dans la marge gauche de la cellule : la valeur garde sa
   mise en forme (liens, pastilles, retours à la ligne), et un libellé sur deux
   lignes allonge la cellule au lieu de chevaucher la suivante. */
.empilee > table.liste--empilable > tbody > tr > td[data-libelle] { padding-left: 116px; }
.empilee > table.liste--empilable > tbody > tr > td[data-libelle]::before {
  content: attr(data-libelle); float: left; width: 104px; margin-left: -116px;
  font-size: 12px; font-weight: 500; line-height: 1.75; letter-spacing: .06em;
  text-transform: uppercase; color: var(--encre-attenuee); overflow-wrap: normal;
}
.empilee > table.liste--empilable > tbody > tr > td.cel-titre,
.empilee > table.liste--empilable > tbody > tr > td.cel-actions,
.empilee > table.liste--empilable > tbody > tr > td[colspan] { grid-column: 1 / -1; }
.empilee > table.liste--empilable > tbody > tr > td.cel-titre { order: -1; padding-left: 0; font-size: 15px; }
.empilee > table.liste--empilable > tbody > tr > td.cel-titre::before,
.empilee > table.liste--empilable > tbody > tr > td.cel-actions::before { content: none; }
.empilee > table.liste--empilable > tbody > tr > td.cel-actions {
  order: 1; display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end;
  gap: 8px; padding: 6px 0 0;
}
.empilee > table.liste--empilable > tbody > tr > td.cel-actions:not(:has(*)) { display: none; }
.empilee > table.liste--empilable .cel-actions .action-principale { order: 1; margin-left: auto; }
.empilee > table.liste--empilable .cel-actions > details { flex: 1 1 100%; text-align: right; }
.empilee > table.liste--empilable .cel-actions > details[open] > :not(summary) { margin-top: 10px; text-align: left; }
/* L'icône ou la case de sélection partage la première ligne avec le titre ;
   les faits s'alignent sur le titre. */
.empilee > table.liste--empilable > tbody > tr > td:is(.cel-icone, .col-coche) {
  grid-row: 1; grid-column: 1 / -1; justify-self: start; width: auto;
  position: relative; z-index: 1;
}
.empilee > table.liste--empilable > tbody > tr:has(> td:is(.cel-icone, .col-coche)) > td.cel-titre { grid-row: 1; }
.empilee > table.liste--empilable > tbody > tr:has(> td:is(.cel-icone, .col-coche)) > td:not(.cel-icone, .col-coche, .cel-actions) { margin-left: 40px; }
/* Sans taille propre, l'icône d'une cellule qui se resserre sur son contenu
   tombait à 0 px : le bloc « Ce qu'on me demande » perdait la marque qui
   distingue une tâche d'un prêt. */
.empilee > table.liste--empilable > tbody > tr > td.cel-icone .icone { display: block; width: 18px; height: 18px; margin-top: 2px; color: var(--encre-douce); }
/* La case « tout cocher » vit dans l'en-tête : empilée, la liste garde cette
   rangée-là, et elle seule. */
.empilee > table.liste--empilable > thead:has(th.col-coche input) { display: block; }
.empilee > table.liste--empilable > thead:has(th.col-coche input) > tr {
  display: flex; padding: 8px 16px; border-bottom: 1px solid var(--bordure);
}
.empilee > table.liste--empilable > thead:has(th.col-coche input) th { position: static; width: auto; height: auto; padding: 0; border: 0; }
.empilee > table.liste--empilable > thead:has(th.col-coche input) th:not(.col-coche) { display: none; }
@media (pointer: coarse) {
  .empilee > table.liste--empilable .cel-actions :is(.btn, button) { min-width: 44px; min-height: 44px; justify-content: center; }
  .empilee > table.liste--empilable .cel-actions .action-principale .btn { min-width: 120px; }
  /* Un bouton posé en lien dans une cellule reste un bouton au doigt : la
     règle des liens de cellule (32 px par remplissage compensé, plus haut)
     lui retirait son remplissage latéral et son centrage — « Vendue » collé à
     ses bords sur la fiche d'un prêt de Mydas. */
  .contenu td a.btn { display: inline-flex; padding: 0 14px; margin: 0; }
}

/* --- Le formulaire de modification d'une tâche, en classes ------------------
   Ses largeurs vivaient en style en ligne (220, 120, 110, 160 px) : empilée
   au téléphone, la rangée d'édition ne pouvait pas se replier. */
.tache-edition { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 8px; padding: 6px 0; }
.tache-edition .champ { margin: 0; min-width: 0; }
.tache-edition .champ-long { flex: 2 1 220px; }
.tache-edition .champ-court { flex: 0 1 120px; }
.tache-edition .champ-moyen { flex: 0 1 160px; }
@media (max-width: 720px) {
  .tache-edition .champ { flex: 1 1 100%; }
}

/* --- Le fil d'Ariane entre 720 et 1100 px -------------------------------------
   Sur Mydas, à 768 px, « L'Hôtel des ventes / Demandes / Demande 4 » ne tenait pas : le
   titre courant s'écrasait à zéro, puis « Demandes » était rogné de 55 px,
   inatteignable. Sur tablette, le fil garde le dernier parent et la page
   courante ; la racine du module reste dans la navigation. */
/* 27.09.2026 : à 1024 px (iPad couché), « L'Hôtel des ventes / Demandes /
   Demande HV-2026-… » rognait encore « Demandes » de 61 px sur trois fiches de
   Mydas — les langues de l'interface, ajoutées le 24.09 au bandeau, ont pris la
   place qui restait. La coupe monte à 1100 px, le seuil de la fiche en colonne. */
@media (max-width: 1100px) {
  .fil-ariane > :not(:nth-last-child(-n+3)) { display: none; }
}

/* --- Ce qui passe à la ligne au doigt ----------------------------------------
   Rogné par la zone de contenu, sans défilement pour le rejoindre (Kronos,
   16.09.2026) : les numéros de page d'une longue liste au téléphone, et
   « Compléter la fiche » dans une ligne de « Mettre en vente » à 360 px — la
   cellule empilée gardait le `nowrap` des cellules de liste. */
.pagination { flex-wrap: wrap; row-gap: 6px; }
@media (max-width: 1024px) {
  .hv-publication tr.hv-ligne > td { white-space: normal; overflow: visible; }
}

/* ======================= Écrans métier, stock et achats (16.09.2026) ======
   Les composants des planches validées par Johann (docs/maquettes/). Écrits
   sur les jetons de la charte, sans une couleur en dur. */

:root {
  /* Le trait d'une étape à venir : la bordure du socle (#E6E3DC) ne tient pas
     1.3:1 sur blanc, un anneau à ce contraste ne se voit pas au soleil. */
  --trait-a-venir: color-mix(in srgb, var(--encre-attenuee) 55%, var(--surface));
  --trait-a-venir-clair: color-mix(in srgb, var(--encre-attenuee) 22%, var(--surface));
}

/* --- Marque d'étape : une forme ET un mot --------------------------------- */
/* La couleur seule ne dit rien à un daltonien, ni sur un écran de téléphone en
   plein soleil. */
.marque-etape {
  display: inline-flex; align-items: center; gap: 6px; min-width: 0;
  font-size: 12.5px; line-height: 1.3; color: var(--encre-douce); white-space: nowrap;
}
.marque-etape .icone { width: 16px; height: 16px; flex: none; stroke-width: 2; }
.marque-etape .texte { overflow: hidden; text-overflow: ellipsis; }
.marque-etape .forme { width: 16px; height: 16px; flex: none; display: inline-flex; align-items: center; justify-content: center; }
.marque-etape .forme::before { content: ""; box-sizing: border-box; border-radius: 50%; }
.marque-etape--fait .icone { color: var(--succes); }
.marque-etape--courante { color: var(--primaire); font-weight: 500; }
.marque-etape--courante .forme::before { width: 12px; height: 12px; border: 2px solid var(--primaire); box-shadow: inset 0 0 0 2px var(--surface); background: var(--primaire); }
.marque-etape--signal { color: var(--avert); font-weight: 500; }
.marque-etape--refus { color: var(--danger); font-weight: 500; }
.marque-etape--a-venir { color: var(--encre-attenuee); }
.marque-etape--a-venir .forme::before { width: 11px; height: 11px; border: 1.5px solid var(--trait-a-venir); }
.marque-etape--sans-objet { color: var(--encre-attenuee); }
.marque-etape--sans-objet .forme::before { width: 12px; height: 1.5px; border-radius: 1px; background: var(--trait-a-venir); }

/* --- Le tableau du chantier : zones × étapes ------------------------------- */
.resume-chantier { display: flex; flex-wrap: wrap; gap: 4px 20px; margin: 2px 0 14px; color: var(--encre-douce); }
.resume-chantier strong { font-weight: 500; color: var(--encre); font-variant-numeric: tabular-nums; }
.resume-chantier .en-alerte strong { color: var(--avert); }
.grille-zones th.col-etape, .grille-zones td.col-etape { width: 106px; max-width: 106px; }
/* Un en-tête d'étape passe à la ligne plutôt que de se couper : « FINITION O… » ne disait plus rien. Un mot seul trop
   long pour sa colonne (« ACCLIMATATION », « AKKLIMATISIERUNG ») se coupe à la syllabe, ou au pire à la lettre : il
   ne se cache plus. */
.grille-zones th.col-etape { letter-spacing: 0; padding-left: 6px; padding-right: 6px; white-space: normal; text-overflow: clip;
  overflow-wrap: anywhere; hyphens: auto; line-height: 1.25; vertical-align: bottom; }
/* Une ellipse de cellule se dessinait après un mot qui débordait de 6 px : un
   point parasite après « Commandé ». Moins de marge, et jamais d'ellipse. */
.grille-zones td.col-etape { white-space: normal; text-overflow: clip; padding-left: 6px; padding-right: 6px; }
.grille-zones td.col-etape .marque-etape { white-space: normal; align-items: flex-start; }
/* « Réceptionnée le 13.09 » : le mot dépassait de sa colonne et s'y coupait sans le dire. */
.grille-zones td.col-etape .marque-etape .texte { overflow-wrap: anywhere; hyphens: auto; }
.grille-zones td.col-zone { white-space: normal; height: auto; padding-top: 8px; padding-bottom: 8px; min-width: 230px; }
.grille-zones .col-zone .nom { display: block; font-weight: 500; color: var(--encre); }
.grille-zones .col-zone .chemin { display: block; font-size: 12.5px; color: var(--encre-attenuee); }
.legende-etapes { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 18px; padding: 10px 16px; border-top: 1px solid var(--bordure); }
@media (min-width: 721px) {
  .selecteur-chantier { width: auto; max-width: 280px; }
}

/* Au-dessous de 1200 px, huit colonnes d'étapes ne tiennent plus : chaque zone
   devient une rangée, son cycle une suite de segments, son signal une phrase. */
.vue-compacte { display: none; }
/* Neuf étapes (les sols) demandent une centaine de pixels de plus : à 1280 px, un numéro de lot se coupait dans sa
   cellule. Leur grille passe en rangées plus tôt. */
@media (max-width: 1339px) {
  .vue-large--neuf { display: none; }
  .vue-compacte--neuf { display: block; }
}
@media (max-width: 1199px) {
  .vue-large { display: none; }
  .vue-compacte { display: block; }
}
.liste-zones { list-style: none; margin: 0; padding: 0; }
.liste-zones > li + li { border-top: 1px solid var(--bordure); }
.zone-rangee {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 4px 12px;
  padding: 12px 16px; min-height: 44px; color: inherit; text-decoration: none;
}
.zone-rangee:hover { background: var(--fond); text-decoration: none; }
.zone-rangee .nom { font-weight: 500; color: var(--encre); }
.zone-rangee .chemin { font-size: 12.5px; color: var(--encre-attenuee); }
.zone-rangee .surface { font-size: 12.5px; color: var(--encre-douce); font-variant-numeric: tabular-nums; text-align: right; }
.zone-rangee .segments, .zone-rangee .etat-courant { grid-column: 1 / -1; }
.zone-rangee .segments { margin-top: 6px; }
.ligne-urgence > .zone-rangee { box-shadow: inset 2px 0 0 var(--avert); }
.ligne-retard > .zone-rangee { box-shadow: inset 2px 0 0 var(--danger); }
/* Autant de segments que d'étapes, comme la frise : fixés à huit, le neuvième segment des sols passait à la ligne. */
.segments { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 3px; }
.segments > span { height: 5px; border-radius: 3px; background: var(--trait-a-venir-clair); }
.segments > .fait { background: var(--succes); }
.segments > .courante { background: var(--primaire); }
.segments > .signal { background: var(--avert); }
.segments > .refus { background: var(--danger); }
.segments > .sans-objet { background: transparent; box-shadow: inset 0 0 0 1px var(--trait-a-venir-clair); }

/* Les lignes d'une paroi (réservations, composants) : un état, un nom qui passe à la ligne, ses gestes repliés. */
.liste-lignes { list-style: none; margin: 0 0 12px; padding: 0; border-top: 1px solid var(--bordure); }
.liste-lignes > li { display: grid; gap: 4px; min-width: 0; padding: 10px 2px; border-bottom: 1px solid var(--bordure); }
.liste-lignes .tete { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; min-width: 0; }
.liste-lignes .nom { font-weight: 500; color: var(--encre); overflow-wrap: anywhere; }
.historique { margin: 6px 0 0; padding-left: 18px; }
.historique > li + li { margin-top: 4px; }
.liste-lignes details > summary.btn { margin-top: 4px; }

/* --- Le dossier de zone : la frise du cycle -------------------------------- */
/* Autant de colonnes que d'étapes : huit au carrelage, neuf aux sols. Fixées à huit, la neuvième passait à la ligne. */
.cycle { list-style: none; margin: 0; padding: 16px 16px 14px; display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); }
.cycle > li { position: relative; min-width: 0; padding-right: 10px; }
/* Une marque sans icône (à venir, sans objet) n'a pas la ligne de base d'une icône : en ligne, elle descendait de deux
   pixels, et les libellés des étapes « sans objet » sous elle. En bloc, à la hauteur de l'icône, tout reste aligné. */
.cycle > li > .marque-etape { display: flex; height: 16px; }
/* Le trait qui relie une étape à la suivante dit l'ORDRE, qui est une information :
   une étape ne se fait pas avant la précédente sans décision. */
.cycle > li::after { content: ""; position: absolute; top: 8px; left: 24px; right: 4px; height: 1px; background: var(--bordure); }
.cycle > li.fait::after { background: color-mix(in srgb, var(--succes) 45%, var(--surface)); }
.cycle > li:last-child::after { display: none; }
.cycle .libelle { display: block; margin-top: 8px; font-size: 13px; font-weight: 500; color: var(--encre); }
.cycle .meta { display: block; font-size: 12px; color: var(--encre-attenuee); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cycle li.courante .libelle { color: var(--primaire); }
.cycle li.signal .libelle, .cycle li.signal .meta { color: var(--avert); }
.cycle li.a-venir .libelle { color: var(--encre-douce); font-weight: 400; }
.cycle-compact { display: none; padding: 14px 16px; }
.cycle-compact .etape-courante { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin-top: 10px; }
.cycle-compact .etape-courante .rang { color: var(--encre-attenuee); font-size: 12.5px; font-variant-numeric: tabular-nums; }
.cycle-compact details { margin-top: 6px; }
.cycle-compact summary { display: flex; align-items: center; min-height: 44px; color: var(--primaire); font-weight: 500; cursor: pointer; }
.cycle-vertical { list-style: none; margin: 0; padding: 0; }
.cycle-vertical li { display: flex; align-items: center; justify-content: space-between; gap: 10px; min-height: 40px; border-top: 1px solid var(--bordure); }
.cycle-vertical .libelle { font-weight: 500; }
@media (max-width: 720px) {
  .cycle { display: none; }
  .cycle-compact { display: block; }
}

/* --- Le signal et ses issues ------------------------------------------------ */
/* « Signaler, laisser décider, tracer » : le signal dit le fait, puis offre les
   issues. Aucune n'est cachée, aucune n'est imposée. */
.avis-signal { align-items: flex-start; flex-wrap: wrap; gap: 8px 12px; padding: 14px 16px; margin-top: 14px; }
.avis-signal > .icone { width: 20px; height: 20px; flex: none; margin-top: 1px; color: var(--avert); }
.avis-signal:not(.avis-avert):not(.avis-danger) > .icone { color: var(--encre-attenuee); }
.avis-signal .corps { flex: 1 1 320px; min-width: 0; }
.avis-signal .titre, .avis-signal .appoint { display: block; }
.avis-signal .appoint { margin-top: 2px; color: var(--encre-douce); max-width: 78ch; }
.avis-signal .avis-actions { flex: 1 1 100%; margin-left: 32px; flex-wrap: wrap; }
.avis-signal + .barre-actions, .onglets + .avis-signal, .ecart-haut { margin-top: 14px; }
@media (max-width: 720px) {
  /* Au téléphone, le socle passe l'avis en colonne : une base flexible de 320 px
     y devient une HAUTEUR, et creusait un vide sous le texte. */
  .avis-signal .corps, .avis-signal .avis-actions { flex: none; }
  .avis-signal .avis-actions { margin-left: 0; flex-direction: column; align-items: stretch; }
  .avis-signal .avis-actions .btn { min-height: 44px; justify-content: center; }
}

/* --- Les sections du cycle : une seule carte ---------------------------------- */
/* Des cartes séparées laissaient passer la colonne en filigrane par chaque
   interstice, en rayures (planche du 16.09.2026). */
.dossier-sections { margin-top: 14px; }
.etape-section + .etape-section { border-top: 1px solid var(--bordure); }
.etape-section > summary { list-style: none; cursor: pointer; min-height: 44px; }
.etape-section > summary::-webkit-details-marker { display: none; }
.etape-section > summary .chevron { width: 16px; height: 16px; flex: none; color: var(--encre-attenuee); transition: transform var(--duree-court) var(--entree); }
.etape-section[open] > summary .chevron { transform: rotate(90deg); }
.etape-section:not([open]) > summary { border-bottom: none; }
.etape-section .carte-entete .appoint { display: inline-flex; align-items: center; gap: 12px; min-width: 0; }
.etape-section .carte-entete .apercu { color: var(--encre-douce); font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* L'étape en cours se repère comme une rangée sélectionnée d'une liste. */
.etape-section.courante > summary { background: var(--primaire-tint); }
@media (max-width: 720px) {
  .etape-section .carte-entete { flex-wrap: wrap; row-gap: 4px; }
  .etape-section .carte-entete .appoint { margin-left: 28px; flex: 1 1 100%; justify-content: space-between; }
  .etape-section .carte-entete .apercu { display: none; }
  .dossier-sections .index-liste .rangee { flex-wrap: wrap; gap: 0 12px; }
  .dossier-sections .index-liste .rangee .cle { width: auto; flex: 1 1 100%; }
}
/* Piste plafonnée (garde-fou CSS-3) : sur un écran large, un calcul de trois
   lignes étiré sur 560 px se lit comme une ligne perdue. */
.calculs-grille { display: grid; grid-template-columns: repeat(auto-fill, minmax(290px, 420px)); gap: 4px 28px; }
.calcul .valeur-calcul { display: block; margin-top: 2px; font-variant-numeric: tabular-nums; }
.calcul .source { color: var(--encre-attenuee); }

.choix-radio { display: flex; flex-wrap: wrap; gap: 8px; border: 0; padding: 0; margin: 0 0 14px; min-width: 0; }
.choix-radio legend { padding: 0; margin-bottom: 6px; font-weight: 500; color: var(--encre-douce); }
.choix-radio label {
  display: inline-flex; align-items: center; gap: 8px; min-height: 44px; padding: 0 14px;
  border: 1px solid var(--bordure); border-radius: var(--rayon); background: var(--surface); cursor: pointer;
}
.choix-radio label:has(input:checked) { border-color: var(--primaire); background: var(--primaire-tint); color: var(--primaire-fonce); font-weight: 500; }

/* --- La preuve photographique ------------------------------------------------ */
.preuve { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; padding: 12px; border: 1px dashed var(--trait-a-venir); border-radius: var(--rayon); }
.preuve .emplacement-photo {
  width: 72px; height: 72px; flex: none; display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--bordure); border-radius: 3px; background: var(--surface); color: var(--encre-attenuee);
}
.preuve .emplacement-photo .icone { width: 22px; height: 22px; }
.preuve .consigne { flex: 1 1 220px; min-width: 0; color: var(--encre-douce); }
.preuve .btn { min-height: 44px; }
.preuve a.emplacement-photo { overflow: hidden; }
.preuve .emplacement-photo img { display: block; width: 100%; height: 100%; object-fit: cover; }

/* Les pièces jointes d'une étape vivent DANS sa section : le bloc partagé est
   une carte, et une carte dans la carte du dossier dessinait un double cadre. */
.dossier-sections .carte { border: 0; border-top: 1px solid var(--bordure); border-radius: 0; }
/* Un geste replié derrière son bouton : sans le marqueur natif, avec un souffle
   quand il s'ouvre — collé au bouton, le premier champ semblait en faire partie. */
details > summary.btn { list-style: none; width: fit-content; }
details > summary.btn::-webkit-details-marker { display: none; }
details[open] > summary.btn { margin-bottom: 12px; }

/* --- Achats et saisies au téléphone ------------------------------------------- */
.ecart { color: var(--avert); font-weight: 500; }
td .actions-ligne { display: inline-flex; flex-wrap: wrap; gap: 6px; }
.ligne-commandee { color: var(--encre-douce); }
.ligne-reception { padding: 14px 16px; }
.ligne-reception + .ligne-reception { border-top: 1px solid var(--bordure); }
.ligne-reception .article { font-weight: 500; }
.ligne-reception .attendu { color: var(--encre-attenuee); font-size: 12.5px; }
.ligne-reception .grille-champs { margin-top: 10px; }
@media (max-width: 720px) {
  .ligne-reception .grille-champs { grid-template-columns: 1fr 1fr; gap: 0 12px; }
  .contenu .btn-large-telephone { width: 100%; min-height: 48px; justify-content: center; }
}

/* --- Les équipes et les heures (17.09.2026, cahier 02 § 2.1 et § 2.4) ---------
   Au bureau, une grille de cartes ; au téléphone, une carte par personne pour le
   jour (docs/RESPONSIVE.md § 3 : jamais une grille de sept colonnes). La rangée
   de saisie se replie en une colonne sous 720 px, sans cadre défilant. */
.cartes-equipes { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 320px), 1fr)); gap: 14px; }
.cartes-equipes > .carte, .carte-equipe { min-width: 0; margin: 0; }
.carte-equipe .membres, .carte .membres { list-style: none; margin: 0; padding: 0 18px 12px; }
.carte .membres li { padding: 7px 0; border-top: 1px solid var(--bordure); display: flex; flex-wrap: wrap; gap: 6px 8px; align-items: center; }
.carte .membres li:first-child { border-top: 0; }
.chef-equipe { padding: 0 18px 8px; margin: 0; }
/* Le chiffre qu'on vient chercher en ouvrant un chantier (20.09.2026) : une bande de
   montants sous le titre, avant tout le reste. Elle se replie en colonnes au doigt —
   quatre nombres côte à côte au téléphone ne se lisent pas. */
.bandeau-argent { padding: 14px 18px; }
.chiffres-chantier {
  /* Piste PLAFONNÉE (piège CSS-3 de la recette) : sans plafond, cinq montants
     s'étalent sur 560 px chacun au bureau et le chiffre se perd au milieu du vide. */
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 200px));
  gap: 10px 24px; margin: 0;
}
.chiffres-chantier > div { min-width: 0; }
.chiffres-chantier dt { font-size: 13px; color: var(--encre-attenuee); margin: 0 0 2px; }
.chiffres-chantier dd { margin: 0; font-size: 17px; font-weight: 500; overflow-wrap: anywhere; }
/* Le montant se lit SOUS son libellé : `.tabulaire` l'alignait au bord droit de sa piste, à cent pixels du
   mot qui le nomme. Au téléphone, libellé et montant se partagent la ligne, comme sur un bulletin : empilés,
   cinq libellés et cinq montants prenaient tout le premier écran, avant même les onglets. */
.chiffres-chantier dd.tabulaire { text-align: left; }
@media (max-width: 560px) {
  .bandeau-argent { padding: 10px 16px; }
  .chiffres-chantier { grid-template-columns: minmax(0, 1fr); gap: 4px; }
  .chiffres-chantier > div { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
  .chiffres-chantier dt { margin: 0; }
  .chiffres-chantier dd, .chiffres-chantier dd.tabulaire { font-size: 16px; text-align: right; }
}

/* Le volet d'ajout d'une ligne de travaux (décision K2) : le formulaire complet est
   replié tant qu'il y a déjà des lignes, pour que la LISTE reste ce qu'on voit du
   chantier. Le triangle natif disparaît — le bouton dit déjà qu'on peut ouvrir. */
.volet-ajout { margin-top: 14px; }
.volet-ajout > summary { list-style: none; display: inline-flex; }
.volet-ajout > summary::-webkit-details-marker { display: none; }
.volet-ajout .grille-champs { margin-top: 12px; }
.reglage-equipe { padding: 0 18px 14px; }
.reglage-equipe > summary { list-style: none; display: inline-flex; }
.reglage-equipe > summary::-webkit-details-marker { display: none; }
.reglage-equipe .grille-champs { margin-top: 10px; }
.entete-heures { display: flex; flex-wrap: wrap; gap: 12px 24px; align-items: flex-start; justify-content: space-between; }
.onglets-heures { display: flex; gap: 8px; flex-wrap: wrap; }
.barre-jour { gap: 8px; }
.barre-jour input[type="date"], .barre-jour select { min-height: 36px; }
.cartes-personnes { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 340px), 1fr)); gap: 14px; }
.cartes-personnes > .carte-personne { min-width: 0; margin: 0; }
.cartes-personnes > .barre-enregistrer { grid-column: 1 / -1; justify-content: flex-end; }
.lignes-heures { list-style: none; margin: 0; padding: 0 18px; }
.lignes-heures li { display: flex; flex-wrap: wrap; gap: 6px 12px; align-items: center; padding: 8px 0; border-top: 1px solid var(--bordure); }
.lignes-heures li:first-child { border-top: 0; }
.lignes-heures .actions-ligne { margin-left: auto; }
.lignes-heures .correction { flex: 1 1 100%; }
.lignes-heures .correction > summary { list-style: none; display: inline-flex; }
.lignes-heures .correction > summary::-webkit-details-marker { display: none; }
.saisie-heure { display: grid; grid-template-columns: 1fr 1fr; gap: 0 12px; padding: 12px 18px 4px; border-top: 1px solid var(--bordure); margin-top: 8px; }
.saisie-heure > * { min-width: 0; }
.saisie-heure .champ--large { grid-column: 1 / -1; }
.saisie-heure .champ-case { display: flex; align-items: center; gap: 8px; min-height: 44px; margin-bottom: 8px; }
.saisie-heure .champ-case input { width: 26px; height: 26px; margin: 0; }
.grille-semaine th a, .grille-semaine td { white-space: nowrap; }
.jours-semaine { list-style: none; margin: 0; padding: 0 18px 8px; }
.jours-semaine li { display: grid; grid-template-columns: 1fr auto; gap: 2px 14px; padding: 9px 0; border-top: 1px solid var(--bordure); align-items: baseline; }
.jours-semaine li:first-child { border-top: 0; }
.jours-semaine li > .texte-attenue:last-child { grid-column: 1 / -1; font-size: 12.5px; }
.carte-ma-semaine .carte-entete .btn { margin-left: auto; }
.detail-calcul { margin: 0; padding-left: 18px; }
.detail-calcul li { padding: 3px 0; }
.grille-periodes .actions-ligne { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.grille-periodes .actions-ligne form { display: inline; }
@media (max-width: 720px) {
  /* Mesuré le 17.09.2026 : les actions des périodes se cherchaient en glissant de
     289 px. Les deux dates se replient, le mois, l'état et le geste restent. */
  .grille-periodes .col-date { display: none; }
  .grille-periodes td, .grille-periodes th { white-space: normal; }
  .cartes-personnes, .cartes-equipes { grid-template-columns: 1fr; }
  .saisie-heure { grid-template-columns: 1fr; }
  .saisie-heure input, .saisie-heure select { min-height: 44px; font-size: 16px; }
  .barre-jour { flex-wrap: wrap; }
  .barre-jour input[type="date"] { flex: 1 1 160px; min-height: 44px; }
  .cartes-personnes > .barre-enregistrer .btn { width: 100%; justify-content: center; min-height: 48px; }
  .carte-ma-semaine .carte-entete { flex-wrap: wrap; }
  .carte-ma-semaine .carte-entete .btn { margin-left: 0; width: 100%; justify-content: center; }
}

/* --- Le parcours de journée (cahier 02 § 2.5) -----------------------------------
   Pensé pour le téléphone tenu d'une main, gants retirés : une étape par rangée,
   son bouton à droite, 44 px au moins. Rien n'est une grille à colonnes fixes :
   chaque rangée se replie d'elle-même quand la place manque. */
.parcours-journee, .parcours-semaine, .parcours-demander, .parcours-a-decider { margin-bottom: 14px; }
/* Une icône hors d'un bouton n'a pas de taille à elle : sans cette ligne, l'épingle du lieu
   remplit la carte (vu à la planche le 19.09.2026, que le banc rendait pourtant à zéro). */
:is(.parcours-lieu-titre, .parcours-rubrique, .parcours-heure) > .icone { width: 16px; height: 16px; flex: none; }
.parcours-journee > p, .parcours-demander > p { margin: 0; padding: 10px 18px 0; font-size: 13px; }
.parcours-etapes, .parcours-jours, .parcours-suivi, .parcours-demandes, .parcours-heures { list-style: none; margin: 0; padding: 0; }
.parcours-etapes { padding: 4px 18px 10px; counter-reset: etape; }
.parcours-etapes > li { display: flex; flex-wrap: wrap; gap: 8px 14px; align-items: center; padding: 12px 0 12px 38px; border-top: 1px solid var(--bordure); position: relative; counter-increment: etape; min-width: 0; }
.parcours-etapes > li:first-child { border-top: 0; }
.parcours-etapes > li::before { content: counter(etape); position: absolute; left: 0; top: 12px; width: 26px; height: 26px; border-radius: 50%; border: 1px solid var(--bordure); display: grid; place-items: center; font-size: 12.5px; color: var(--encre-douce); background: var(--surface); }
.parcours-etapes > li.est-validee::before { background: var(--succes-tint); border-color: var(--succes); color: var(--succes); }
.parcours-etapes > li.est-prochaine::before { background: var(--accent-tint); border-color: var(--accent); color: var(--accent-texte); }
.parcours-etape-texte { display: flex; flex-direction: column; gap: 2px; flex: 1 1 200px; min-width: 0; overflow-wrap: anywhere; }
.parcours-etape-titre { font-weight: 500; }
.parcours-heure { display: inline-flex; align-items: center; gap: 6px; font-variant-numeric: tabular-nums; font-weight: 500; color: var(--succes); }
.parcours-heure .texte-attenue { font-weight: 400; }
.parcours-valider { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; justify-content: flex-end; flex: 1 1 220px; min-width: 0; }
.parcours-valider .btn { min-width: 112px; justify-content: center; }
.parcours-remarque { flex: 1 1 160px; min-width: 0; }
.parcours-remarque > summary { cursor: pointer; min-height: 44px; display: flex; align-items: center; font-size: 13px; color: var(--encre-douce); }
.parcours-remarque input { width: 100%; }
.parcours-jours { padding: 0 18px 8px; }
.parcours-jour { padding: 12px 0; border-top: 1px solid var(--bordure); min-width: 0; }
.parcours-jour:first-child { border-top: 0; }
.parcours-jour.est-aujourd-hui { border-left: 3px solid var(--accent); padding-left: 12px; margin-left: -15px; }
.parcours-jour-entete { display: flex; flex-wrap: wrap; gap: 4px 12px; align-items: center; justify-content: space-between; }
.parcours-jour-entete h3 { margin: 0; font-size: 15px; }
.parcours-jour p { margin: 4px 0 0; overflow-wrap: anywhere; }
.parcours-lieu { margin-top: 8px; padding: 10px 12px; border: 1px solid var(--bordure); border-radius: var(--rayon); background: var(--fond); min-width: 0; }
.parcours-lieu-titre { display: flex; flex-wrap: wrap; gap: 4px 8px; align-items: center; margin: 0; }
.parcours-rubrique { display: inline-flex; align-items: center; gap: 6px; margin-top: 8px; font-size: 12.5px; font-weight: 600; color: var(--accent-texte); text-transform: uppercase; letter-spacing: .04em; }
.parcours-travail ul, .parcours-prendre ul { margin: 4px 0 0; padding-left: 20px; overflow-wrap: anywhere; }
.parcours-demande { display: flex; flex-wrap: wrap; gap: 4px 8px; align-items: center; font-size: 13px; }
.parcours-demander > summary { list-style: none; cursor: pointer; min-height: 44px; }
.parcours-demander:not([open]) > summary { border-bottom: 0; }
.parcours-demander > summary h2::before { content: "+ "; }
.parcours-demander[open] > summary h2::before { content: "− "; }
.parcours-demander > summary::-webkit-details-marker { display: none; }
.parcours-formulaire { padding: 10px 18px 14px; }
.parcours-ligne { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr)); gap: 0 14px; margin: 0 0 8px; padding: 8px 12px 0; border: 1px solid var(--bordure); border-radius: var(--rayon); min-width: 0; }
.parcours-ligne legend { padding: 0 6px; font-size: 12.5px; color: var(--encre-douce); }
.parcours-ligne :is(select, input) { width: 100%; min-width: 0; }
.parcours-suivi { padding: 0 18px 8px; }
.parcours-suivi > li { padding: 12px 0; border-top: 1px solid var(--bordure); min-width: 0; }
.parcours-suivi > li:first-child { border-top: 0; }
.parcours-suivi > li > p { margin: 2px 0 6px; font-size: 13px; overflow-wrap: anywhere; }
.parcours-suivi-entete { display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: center; }
.parcours-suivi-entete a { display: inline-flex; align-items: center; min-height: 44px; }
.parcours-heures { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 230px), 1fr)); gap: 6px 14px; }
.parcours-heures > li { display: flex; flex-wrap: wrap; gap: 2px 10px; justify-content: space-between; align-items: baseline; padding: 6px 10px; border: 1px solid var(--bordure); border-radius: var(--rayon); font-size: 13px; min-width: 0; }
.parcours-heures > li.est-validee { border-color: color-mix(in srgb, var(--succes) 45%, var(--bordure)); background: var(--succes-tint); }
.parcours-heures-titre { overflow-wrap: anywhere; min-width: 0; }
.parcours-heures-remarque { flex-basis: 100%; }
.parcours-demandes { padding: 0 18px 8px; }
.parcours-demandes > li { display: flex; flex-wrap: wrap; gap: 8px 14px; align-items: center; padding: 12px 0; border-top: 1px solid var(--bordure); min-width: 0; }
.parcours-demandes > li:first-child { border-top: 0; }
.parcours-demande-texte { display: flex; flex-direction: column; gap: 2px; flex: 1 1 220px; min-width: 0; overflow-wrap: anywhere; }
.parcours-decider { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; flex: 1 1 280px; min-width: 0; }
.parcours-decider input[type="text"] { flex: 1 1 160px; min-width: 0; }

/* La prise du matériel cochée ligne à ligne (décision J5). Chaque ligne est une cible de 44 px : elle se coche
   au dépôt, d'une main, avec des gants. */
.parcours-coches { flex: 1 1 100%; margin: 0; padding: 4px 12px 8px; border: 1px solid var(--bordure); border-radius: var(--rayon); min-width: 0; }
.parcours-coches legend { padding: 0 6px; font-size: 12.5px; color: var(--encre-douce); }
.parcours-coche { display: flex; align-items: center; gap: 10px; min-height: 44px; cursor: pointer; overflow-wrap: anywhere; min-width: 0; }
.parcours-coche input { flex: 0 0 auto; width: 22px; height: 22px; accent-color: var(--accent); }
.parcours-coche span { min-width: 0; }

/* Le signalement « on fait quoi ? » (décisions J1 à J4) : une photo, ses cotes tracées à la main, une question. */
.parcours-question { margin-bottom: 14px; }
/* Le formulaire « on fait quoi ? » n'est plus un volet qui se déplie : il est ouvert,
   en tête du bloc (20.09.2026). Replié derrière une ligne grise dès qu'une demande
   existait, il laissait croire qu'on ne pouvait pas en poser. Sa ligne d'appel garde
   le poids de l'ancien résumé : c'est elle qui dit ce que le bloc sert à faire. */
.parcours-poser { padding: 0 18px 4px; }
.parcours-poser-titre {
  margin: 0; min-height: 44px; display: flex; align-items: center;
  font-weight: 500; color: var(--accent-texte);
}
.parcours-poser .parcours-formulaire { padding: 4px 0 14px; }
.parcours-poser textarea, .signalement-repondre textarea { width: 100%; min-width: 0; }
.signalements { list-style: none; margin: 0; padding: 0 18px 8px; }
.signalement { padding: 12px 0; border-top: 1px solid var(--bordure); min-width: 0; }
.signalement:first-child { border-top: 0; }
.signalement-entete { display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: center; }
.signalement-corps { display: flex; flex-wrap: wrap; gap: 10px 14px; align-items: flex-start; margin-top: 8px; min-width: 0; }
.signalement-vignette { flex: 0 0 auto; display: block; width: 112px; min-height: 44px; border-radius: var(--rayon); overflow: hidden; border: 1px solid var(--bordure); }
.signalement-vignette img { display: block; width: 100%; height: 84px; object-fit: cover; }
.signalement-dit { display: flex; flex-direction: column; gap: 8px; flex: 1 1 240px; min-width: 0; overflow-wrap: anywhere; }
.signalement-dit p { margin: 0; }
.signalement-question { font-size: 15px; }
.cotes-liste { display: flex; flex-wrap: wrap; gap: 4px 6px; align-items: center; font-size: 13px; }
/* Une icône n'a pas de taille à elle : hors d'un bouton, elle prend toute la largeur (planche du 20.09.2026). */
.cotes-liste .icone, .bandeau-questions .icone, .parcours-question .carte-entete .icone { flex: 0 0 auto; width: 16px; height: 16px; }
.signalement-fil { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.signalement-fil > li { display: flex; flex-direction: column; gap: 2px; padding: 6px 10px; border-left: 3px solid var(--bordure); font-size: 13px; min-width: 0; }
.signalement-fil > li.est-reponse { border-left-color: var(--succes); background: var(--succes-tint); border-radius: 0 var(--rayon) var(--rayon) 0; }
.signalement-texte { font-size: 14px; overflow-wrap: anywhere; }
.signalement-repondre, .signalement-suivre { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; min-width: 0; }
.signalement-repondre textarea { flex: 1 1 100%; min-height: 60px; padding: 8px 10px; border: 1px solid var(--bordure); border-radius: var(--rayon); background: var(--surface); color: inherit; font: inherit; }
.signalement-materiel ul { margin: 4px 0 0; padding-left: 20px; overflow-wrap: anywhere; }
.signalement-materiel .icone { flex: 0 0 auto; width: 16px; height: 16px; }
.parcours-vers { margin-bottom: 10px; }
.signalement-page { padding: 14px 18px 18px; display: flex; flex-direction: column; gap: 14px; }

/* La photo et ses cotes. Les traits vivent dans un SVG étiré sur l'image (millièmes de sa largeur et de sa
   hauteur) : `non-scaling-stroke` garde au trait son épaisseur, quelle que soit la forme de la photo. Les valeurs
   sont des étiquettes HTML placées en pour cent — un texte SVG étiré serait déformé. */
.cotes-cadre { position: relative; display: block; max-width: 100%; line-height: 0; }
.cotes-cadre img { display: block; max-width: 100%; height: auto; }
.cotes-cadre-grand { align-self: flex-start; border: 1px solid var(--bordure); border-radius: var(--rayon); overflow: hidden; }
.cotes-cadre-grand img { max-height: 70vh; width: auto; }
.cotes-traits { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.cotes-traits line { stroke: #FFD23F; stroke-width: 3; stroke-linecap: round; vector-effect: non-scaling-stroke; paint-order: stroke; filter: drop-shadow(0 0 1px rgba(0, 0, 0, .9)); }
.cotes-traits line.cote-apercu { stroke-dasharray: 6 5; }
.cote-etiquette { position: absolute; transform: translate(-50%, -50%); padding: 2px 7px; border-radius: 999px; background: rgba(20, 16, 12, .86); color: #FFD23F; font-size: 12.5px; font-weight: 600; line-height: 1.4; white-space: nowrap; pointer-events: none; font-variant-numeric: tabular-nums; }
/* Là où l'on trace : le doigt dessine au lieu de faire défiler. La hauteur est bornée pour qu'il reste toujours
   de la page à saisir autour de la photo — sinon un téléphone tenu debout n'aurait plus par où défiler. */
.cotes-cadre-trace { touch-action: none; cursor: crosshair; user-select: none; -webkit-user-select: none; }
.cotes-cadre-trace img { max-height: 56vh; pointer-events: none; -webkit-user-drag: none; }
.cotes-atelier { display: flex; flex-direction: column; gap: 8px; margin-bottom: 10px; min-width: 0; }
.cotes-atelier[hidden], .cotes-ligne[hidden] { display: none; }
.cotes-atelier > p { margin: 0; font-size: 13px; }
.cotes-ligne { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 130px), 1fr)); gap: 0 12px; align-items: end; margin: 0; padding: 6px 12px 10px; border: 1px solid var(--bordure); border-radius: var(--rayon); min-width: 0; }
.cotes-ligne legend { padding: 0 6px; font-size: 12.5px; color: var(--encre-douce); }
.cotes-ligne :is(select, input) { width: 100%; min-width: 0; }
.cotes-ligne .btn { min-height: 44px; justify-content: center; margin-bottom: 12px; }

/* Le bandeau de l'accueil : « un compteur les avertit à l'ouverture d'Oikos » (décision J3). Un lien entier, de 44 px. */
.bandeau-questions { display: flex; flex-wrap: wrap; gap: 6px 10px; align-items: center; min-height: 44px; margin: 0 0 14px; padding: 8px 14px; border: 1px solid color-mix(in srgb, var(--avert) 45%, var(--bordure)); border-radius: var(--rayon); background: var(--avert-tint); color: inherit; text-decoration: none; position: relative; min-width: 0; overflow-wrap: anywhere; }
.bandeau-questions > span:first-of-type { flex: 1 1 200px; min-width: 0; }
.bandeau-questions-suite { font-weight: 600; color: var(--accent-texte); }

/* La pastille du menu : ce qui attend CE compte (questions de chantier). Un nombre, jamais une alarme. */
.entree .entree-compte { margin-left: auto; min-width: 20px; padding: 1px 6px; border-radius: 999px; background: var(--avert-tint); color: var(--avert); font-size: 11.5px; font-weight: 600; text-align: center; font-variant-numeric: tabular-nums; }
/* Sur la saisie des heures : ce que les validations du parcours proposent, au-dessus des champs qu'elles remplissent. */
.proposition-heures { margin: 0 0 10px; font-size: 13px; overflow-wrap: anywhere; }

/* --- Le planning des équipes (cahier 02 § 2.2) ----------------------------------
   Au bureau, la semaine en grille ; sous 1100 px, UNE LISTE PAR JOUR, jamais une
   grille de sept colonnes : huit colonnes dans un cadre qui défile cachent le
   vendredi et ses « Retirer » à qui tient une tablette. Les deux lectures sont
   servies, une seule s'affiche. */
.planning-grille { display: none; }
.planning-jours { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 340px), 1fr)); gap: 14px; align-items: start; }
.planning-jours > .carte-jour { min-width: 0; margin: 0; }
.carte-jour.aujourd-hui { border-color: var(--accent); }
.lignes-planning { list-style: none; margin: 0; padding: 0 18px 6px; }
.lignes-planning li { display: flex; flex-direction: column; gap: 4px; padding: 10px 0; border-top: 1px solid var(--bordure); }
.lignes-planning li:first-child { border-top: 0; }
.sans-affectation { padding: 0 18px 12px; margin: 0; font-size: 13px; }
.affectation { display: flex; flex-wrap: wrap; gap: 4px 8px; align-items: center; min-width: 0; }
.affectation .lieu { font-weight: 500; overflow-wrap: anywhere; }
.affectation form { display: inline-flex; margin-left: auto; }
.affecter { margin: 14px 0; }
.affecter > summary { list-style: none; cursor: pointer; min-height: 44px; }
.affecter:not([open]) > summary { border-bottom: 0; }
.affecter > summary h2::before { content: "+ "; }
.affecter[open] > summary h2::before { content: "− "; }
.affecter > summary::-webkit-details-marker { display: none; }
.affecter > form { padding: 4px 18px 14px; }
.affecter .champ-case { display: flex; align-items: center; gap: 8px; min-height: 44px; }
.affecter .champ-case input { width: 26px; height: 26px; margin: 0; }
@media (min-width: 1101px) {
  .planning-grille { display: block; }
  .planning-jours { display: none; }
  /* `table.liste td` impose une ligne et des points de suspension : juste pour une
     référence, faux pour une case qui porte un chantier entier (vu à la planche le
     17.09.2026, « Halle logistique : sol i… »). D'où le sélecteur à sa hauteur. */
  table.liste.grille-planning { table-layout: fixed; }
  table.liste.grille-planning th, table.liste.grille-planning td { vertical-align: top; white-space: normal; overflow: visible; overflow-wrap: anywhere; height: auto; }
  table.liste.grille-planning thead th:first-child { width: 15%; }
  table.liste.grille-planning td { font-size: 13px; }
  table.liste.grille-planning tbody tr:hover { cursor: default; }
  .grille-planning .affectation { padding: 3px 0; }
  .grille-planning .affectation + .affectation { border-top: 1px dashed var(--bordure); margin-top: 3px; }
  /* Sept « Retirer » par rangée couvrent le planning : au bureau, le geste se montre
     sous le pointeur ou au clavier, et reste dans l'ordre de tabulation. Sans pointeur
     (grande tablette), il reste visible : un geste invisible au doigt n'existe pas. */
  @media (hover: hover) {
    .grille-planning .affectation form { opacity: 0; transition: opacity var(--duree-court) var(--entree); }
    .grille-planning td:hover .affectation form, .grille-planning .affectation form:focus-within { opacity: 1; }
  }
  .grille-planning .affectation .btn { padding: 2px 8px; min-height: 0; font-size: 12px; }
  .grille-planning .aujourd-hui { background: var(--accent-tint); }
  .grille-planning .fin-de-semaine { color: var(--encre-attenuee); }
}
@media (max-width: 720px) {
  .affecter input, .affecter select { min-height: 44px; font-size: 16px; }
  .lignes-planning .affectation .btn { min-height: 44px; }
}
@media print {
  .planning-grille { display: block !important; }
  .planning-jours { display: none !important; }
  .grille-planning { table-layout: fixed; width: 100%; font-size: 11px; }
  .grille-planning th, .grille-planning td { vertical-align: top; white-space: normal; border: 1px solid #999; }
}

/* --- Le journal de chantier (cahier 02 § 1.9) -----------------------------------
   Écrit au téléphone par le chef d'équipe : une colonne, des champs hauts, le
   geste principal en pleine largeur. Un rapport porte des PHRASES : rien n'y est
   tenu sur une ligne. */
.filtres-journal { gap: 8px; flex-wrap: wrap; }
.filtres-journal input[type="search"] { flex: 1 1 200px; min-width: 0; min-height: 36px; padding: 0 10px; }
.cartes-rapports { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 340px), 1fr)); gap: 14px; }
.carte-rapport { display: block; min-width: 0; color: inherit; text-decoration: none; transition: border-color var(--duree-court) var(--entree); }
.carte-rapport:hover, .carte-rapport:focus-visible { border-color: var(--accent); }
.corps-rapport { display: flex; flex-direction: column; gap: 4px; padding: 12px 16px 14px; overflow-wrap: anywhere; }
.corps-rapport .extrait { color: var(--encre-douce); font-size: 13.5px; }
.formulaire-rapport { padding-bottom: 6px; }
.formulaire-rapport > .grille-champs, .formulaire-rapport > .barre-actions, .formulaire-rapport > .note-photos { padding: 0 18px; }
.formulaire-rapport > .grille-champs:first-child { padding-top: 16px; }
.formulaire-rapport > .carte-entete + .grille-champs { padding-top: 14px; }
.formulaire-rapport textarea { width: 100%; resize: vertical; }
.travaux-du-jour { border: 0; border-top: 1px solid var(--bordure); border-bottom: 1px solid var(--bordure); margin: 4px 0 14px; padding: 12px 18px 8px; min-width: 0; }
.travaux-du-jour legend { padding: 0 6px 0 0; font-size: 12px; font-weight: 500; letter-spacing: .14em; text-transform: uppercase; color: var(--encre-douce); }
.ligne-travaux { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); gap: 0 12px; }
.ligne-travaux > .champ--large:only-child { grid-column: 1 / -1; }
.ligne-travaux--repli { display: none; }
.travaux-du-jour:has(.plus-de-lignes[open]) .ligne-travaux--repli { display: grid; }
.plus-de-lignes > summary { list-style: none; display: inline-flex; }
.plus-de-lignes > summary::-webkit-details-marker { display: none; }
.plus-de-lignes[open] > summary { display: none; }
.note-photos { font-size: 13px; margin: 8px 0 12px; }
.colonnes-rapport { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: 14px; margin: 14px 0; align-items: start; }
.colonnes-rapport > .carte { margin: 0; min-width: 0; }
.colonnes-rapport ~ .carte { margin-top: 14px; }
.lignes-rapport { list-style: none; margin: 0; padding: 0 18px 6px; }
.lignes-rapport li { display: flex; flex-direction: column; gap: 2px; padding: 9px 0; border-top: 1px solid var(--bordure); }
.lignes-rapport li:first-child { border-top: 0; }
.lignes-rapport li.total { font-weight: 500; }
/* Les gestes d'une ligne (présence d'un participant, suivi d'un point) : côte à côte, et à la ligne sans déborder. */
.gestes-ligne { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
/* Le planning de chantier (cahier 02 § 2.3) : la barre d'une phase est un SVG dont les entiers viennent du moteur pur.
   Au téléphone, la liste chronologique seule : une barre de 300 px de large ne dit rien qu'on ne lise mieux en dates. */
.barre-phase { display: none; width: 100%; height: 10px; margin: 4px 0 2px; }
.barre-phase .fond { fill: var(--bordure); }
.barre-phase .duree { fill: var(--accent); opacity: .35; }
.barre-phase .fait { fill: var(--accent); }
@media (min-width: 721px) { .barre-phase { display: block; } }
@media print { .barre-phase { display: block; } }
.prose-rapport { white-space: pre-line; overflow-wrap: anywhere; }
.vide-rapport { padding: 10px 18px 12px; margin: 0; }
/* Mesuré le 17.09.2026 : à 768 px, « Saisir les heures de ce jour » tenait sur une ligne dans le tiers
   de droite et poussait la page de 12 px. Le bouton se replie, et les deux colonnes attendent la place. */
.vide-rapport .btn { white-space: normal; text-align: left; }
@media (max-width: 1000px) { .colonnes-rapport { grid-template-columns: 1fr; } }
.champs-rapport { margin: 0; padding: 0 18px 8px; }
.champs-rapport > div { display: grid; grid-template-columns: minmax(0, 180px) minmax(0, 1fr); gap: 4px 16px; padding: 9px 0; border-top: 1px solid var(--bordure); }
.champs-rapport > div:first-child { border-top: 0; }
.champs-rapport dt { color: var(--encre-attenuee); font-size: 13px; }
.champs-rapport dd { margin: 0; }
.empreinte { overflow-wrap: anywhere; }
/* Vu à la planche le 17.09.2026 : hors de `.avis-signal`, l'icône d'un avis n'a aucune taille et
   remplissait l'écran. L'avis du rapport scellé reprend donc le signal, en vert. */
.avis-scelle > .icone { color: var(--succes); }
.complement { padding: 8px 18px 14px; }
.supprimer-brouillon { margin: 18px 0; }
.carte-jour-synthese { margin: 14px 0; }
.rapport-de-synthese { padding: 10px 18px; border-top: 1px solid var(--bordure); }
.rapport-de-synthese:first-of-type { border-top: 0; }
.rapport-de-synthese .tete { display: flex; flex-wrap: wrap; gap: 6px 10px; align-items: center; margin: 0 0 4px; font-weight: 500; }
.rapport-de-synthese .lignes-rapport { padding: 0; }
.champ-de-synthese { margin: 4px 0; }
@media (max-width: 720px) {
  .colonnes-rapport { grid-template-columns: 1fr; }
  .ligne-travaux { grid-template-columns: 1fr; }
  .champs-rapport > div { grid-template-columns: 1fr; }
  .formulaire-rapport input, .formulaire-rapport select, .formulaire-rapport textarea,
  .complement textarea, .filtres-journal input, .filtres-journal select { min-height: 44px; font-size: 16px; }
  .formulaire-rapport .barre-enregistrer { flex-direction: column-reverse; align-items: stretch; }
  .formulaire-rapport .barre-enregistrer .btn, .complement .btn { width: 100%; justify-content: center; min-height: 48px; }
  .plus-de-lignes > summary { min-height: 44px; }
}

/* --- La bascule FR / DE (décision G8) : deux lettres, une cible de doigt entière. */
.bascule-langue { display: inline-flex; margin: 0; }
/* Vu à la planche du 17.09.2026 : en petites capitales, « DE » se lisait à peine au bureau. */
.bascule-langue .btn { min-width: 44px; justify-content: center; font-weight: 600; letter-spacing: .04em; }

/* --- La signature au doigt (composant partagé, décision H6) ----------------------
   `touch-action: none` est la règle qui compte : sans elle, le doigt fait défiler la
   page au lieu de tracer. Le canevas a une hauteur fixe pour que le script mesure une
   surface stable ; il ne déborde jamais de son champ. */
.champ-signature { min-width: 0; }
.libelle-signature { display: block; font-size: 13px; color: var(--encre-douce); margin-bottom: 6px; }
/* Le pavé de signature se donne à voir : une ligne de signature et une invite dans le
   cadre, qui s'effacent au premier trait. Sans elles, le client à qui l'on tend le
   téléphone voit un rectangle vide et n'ose pas y poser le doigt. */
.cadre-signature { position: relative; }
.invite-signature {
  position: absolute; left: 0; right: 0; bottom: 34px; text-align: center;
  color: var(--encre-attenuee); font-size: 14px; pointer-events: none;
  transition: opacity var(--duree-court) var(--entree);
}
.cadre-signature::after {
  content: ""; position: absolute; left: 24px; right: 24px; bottom: 28px;
  border-bottom: 1px solid var(--bordure); pointer-events: none;
}
.signature-tracee .invite-signature, .signature-tracee .cadre-signature::after { opacity: 0; }
.canevas-signature { display: block; width: 100%; height: 170px; border: 1px dashed var(--encre-attenuee); border-radius: var(--rayon);
  background: var(--surface); touch-action: none; cursor: crosshair; }
.signature-tracee .canevas-signature { border-style: solid; border-color: var(--accent); }
.pied-signature { display: flex; flex-wrap: wrap; gap: 6px 12px; align-items: center; justify-content: space-between; margin-top: 6px; }
.pied-signature .aide { flex: 1 1 220px; margin: 0; }
.trace-recueilli { margin: 8px 0 0; }
.trace-recueilli img { display: block; max-width: min(100%, 320px); max-height: 120px; border: 1px solid var(--bordure); border-radius: 6px; background: #fff; }
.trace-recueilli figcaption { font-size: 12.5px; margin-top: 4px; }
.volet-signature { margin: 14px 0; }
.volet-signature > summary { list-style: none; cursor: pointer; min-height: 44px; }
.volet-signature > summary::-webkit-details-marker { display: none; }
.volet-signature:not([open]) > summary { border-bottom: 0; }
.volet-signature > summary h2::before { content: "+ "; }
.volet-signature[open] > summary h2::before { content: "− "; }
.volet-signature > form { padding: 12px 18px 14px; }
.volet-signature .champ-case { display: flex; align-items: center; gap: 8px; min-height: 44px; }
.volet-signature .champ-case input { width: 26px; height: 26px; margin: 0; }
.volet-signature textarea { width: 100%; resize: vertical; }
.doc-api { max-width: 980px; margin-top: 16px; }
.etapes-doc-api { margin: 0; padding-left: 20px; line-height: 1.7; }
@media (max-width: 720px) {
  .canevas-signature { height: 190px; }
  .pied-signature .btn { min-height: 44px; }
  .volet-signature .btn-large-telephone { width: 100%; justify-content: center; min-height: 48px; }
}

/* --- Le procès-verbal de réception (décision H5) */
.onglets-reception { margin: 4px 0 10px; }
.refus-signature { padding: 10px 18px 14px; }
.refus-signature li { border-top: 0; padding: 4px 0; }

/* --- Les réserves (cahier 02 § 1.8) ---------------------------------------------
   On fait le tour avec le client, téléphone en main : une réserve est une ligne
   qu'on touche en entier, et sa fiche met le geste attendu avant tout le reste. */
.groupe-reserves { margin: 14px 0; }
.lignes-reserves { list-style: none; margin: 0; padding: 0; }
.lignes-reserves li { border-top: 1px solid var(--bordure); }
.lignes-reserves li:first-child { border-top: 0; }
.ligne-reserve { display: flex; flex-direction: column; gap: 4px; padding: 12px 18px; color: inherit; text-decoration: none; min-width: 0; }
.ligne-reserve:hover, .ligne-reserve:focus-visible { background: var(--fond); text-decoration: none; }
.ligne-reserve .tete { display: flex; flex-wrap: wrap; gap: 6px 10px; align-items: center; }
.ligne-reserve .description { overflow-wrap: anywhere; }
.description-reserve { padding: 12px 18px 16px; margin: 0; }
.suivi-reserve > div { grid-template-columns: minmax(0, 110px) minmax(0, 1fr); }
.gestes-reserve { margin: 14px 0; padding-bottom: 8px; }
.geste-reserve { padding: 10px 18px 0; }
.geste-reserve > summary { list-style: none; display: inline-flex; min-height: 40px; align-items: center; }
.geste-reserve > summary::-webkit-details-marker { display: none; }
.geste-reserve[open] > summary { margin-bottom: 10px; }
.geste-reserve textarea { width: 100%; resize: vertical; }
.colonnes-photos { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; margin: 14px 0; align-items: start; }
.colonnes-photos > .carte { margin: 0; min-width: 0; }
.colonnes-photos ~ .carte { margin-top: 14px; }
.vignettes-reserve { list-style: none; margin: 0; padding: 12px 18px 14px; display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 180px)); gap: 10px; }
.vignettes-reserve li { display: flex; flex-direction: column; gap: 4px; min-width: 0; font-size: 12.5px; }
.vignettes-reserve img { display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: 6px; border: 1px solid var(--bordure); background: var(--fond); }
@media (max-width: 720px) {
  .colonnes-photos { grid-template-columns: 1fr; }
  .ligne-reserve { padding: 14px 16px; }
  .geste-reserve > summary { width: 100%; justify-content: center; min-height: 48px; }
  .geste-reserve input, .geste-reserve select, .geste-reserve textarea,
  .suivi-reserve + details input, .suivi-reserve + details select { min-height: 44px; font-size: 16px; }
  .geste-reserve .btn-large-telephone { width: 100%; justify-content: center; min-height: 48px; }
}

/* La sous-traitance (cahier 15 § 11) : le comparatif large défile dans son cadre, la colonne des positions
   reste figée ; au téléphone, une carte par position avec les offres. Une position non chiffrée se lit
   comme telle, jamais comme un zéro. */
.tableau-defilant table.liste.comparatif th[scope="row"] { position: sticky; left: 0; z-index: 1; background: var(--surface); box-shadow: 1px 0 0 var(--bordure); white-space: normal; min-width: 180px; max-width: 280px; }
.tableau-defilant table.liste.comparatif tbody th[scope="row"] { text-transform: none; letter-spacing: normal; font-size: inherit; font-weight: 500; color: var(--encre); }
.comparatif .non-chiffree, .comparatif-cartes .non-chiffree { color: var(--avert); font-style: italic; }
.comparatif tfoot th, .comparatif tfoot td { border-top: 2px solid var(--bordure); font-weight: 600; vertical-align: top; }
.comparatif-cartes { display: none; list-style: none; margin: 0; padding: 0; }
@media (max-width: 720px) {
  .comparatif-large { display: none; }
  .comparatif-cartes { display: block; }
  .comparatif-cartes > li { border-top: 1px solid var(--bordure); padding: 12px 16px; }
  .comparatif-cartes dl { display: grid; grid-template-columns: minmax(0, 1fr) max-content; gap: 4px 12px; margin: 8px 0 0; }
  .comparatif-cartes dt { min-width: 0; overflow-wrap: anywhere; }
  .comparatif-cartes dd { margin: 0; text-align: right; }
}
/* Le lien du tutoriel filmé, sous la ligne de provenance d'un module (17.09.2026).
   Discret mais touchable : 44 px de haut au doigt, comme toute cible de terrain. */
.lien-discret-tutoriel {
  display: inline-flex; align-items: center; min-height: 44px; margin-top: 2px;
  color: var(--accent-texte); font-size: 13px; text-decoration: none;
}
.lien-discret-tutoriel::before { content: "▸"; margin-right: 7px; }
.lien-discret-tutoriel:hover { text-decoration: underline; }

/* Un lien dont le texte est court — « V3 », un repère — reste une cible de 24 px : mesuré
   le 18.09.2026 sur la charpente, un lien de version faisait 18 px de large. */
.lien-court {
  display: inline-block; min-width: 24px; min-height: 24px; line-height: 24px;
}
/* Un fil d'Ariane de métier porte des repères de deux ou trois signes — « TA1 »,
   « B11 » — qui font des cibles de 21 px. Posé ICI et non sur chaque gabarit :
   le prochain écran à repère court n'aura rien à faire (mesuré le 18.09.2026). */
.fil-ariane a { display: inline-block; min-width: 24px; min-height: 24px; }

/* Le terrassement (cahier 01) : une fouille est un bloc dans la section de sa zone,
   et ses étapes se lisent en ligne, comme la frise d'une zone en plus court. */
.bloc-fouille {
  padding: 14px 0; border-top: 1px solid var(--bordure);
}
.bloc-fouille:first-of-type { border-top: 0; }
.bloc-fouille h3 { margin: 0 0 8px; font-size: 15px; }
.etapes-en-ligne { display: flex; flex-wrap: wrap; gap: 8px 14px; margin-bottom: 10px; }

/* ===================== Le profil d'affichage (EXPERIENCE-PROFILS.md, lot 1) ===================== */

/* « Sur quoi travaillez-vous ? » — une question, une colonne, une action.
   La mesure de lecture est bornée : une liste de choix qui court sur 1400 px
   oblige l'œil à repartir de la marge à chaque ligne, et on coche au hasard. */
.choix-metiers { max-width: 46rem; }
.choix-metiers h1 { margin: 0 0 6px; font-size: 1.35rem; }
.choix-metiers-intro { margin: 0 0 20px; max-width: 42rem; }

.liste-choix { list-style: none; margin: 0; padding: 0;
  border: 1px solid var(--bordure); border-radius: var(--rayon); background: var(--surface); }
.liste-choix > li + li { border-top: 1px solid var(--bordure); }

/* La ligne entière est la cible, pas la seule case : au doigt, viser un carré
   de 13 px pendant qu'on tient une échelle est une cible qu'on rate. */
.choix-metier {
  display: flex; gap: 12px; align-items: flex-start;
  min-height: 56px; padding: 12px 16px; cursor: pointer; font-weight: 400;
  transition: background-color var(--duree-court) var(--entree);
}
.choix-metier:hover { background: var(--fond); }
.choix-metier:focus-within { background: var(--primaire-tint); }
/* 24 px : le plancher d'une cible, mesuré au banc. La LIGNE entière reste
   cliquable — mais un banc mesure l'élément, et un doigt vise la case. */
.choix-metier input[type="checkbox"] { margin-top: 2px; width: 24px; height: 24px; flex: none; }
.choix-metier-corps { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.choix-metier-nom {
  display: flex; align-items: center; gap: 8px;
  font-weight: 500; color: var(--primaire-fonce);
}
.choix-metier-nom .icone { width: 16px; height: 16px; flex: none; }
/* Les écrans du métier : ils informent le choix, ils ne le concurrencent pas. */
.choix-metier-ecrans { color: var(--encre-douce); font-size: 13px; line-height: 1.5; }

.choix-metiers-note { margin: 14px 0 0; font-size: 13px; }
.choix-metiers-actions { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-top: 18px; }

/* L'en-tête : le métier courant EST le bouton qui le change. Le bandeau porte
   déjà le nom, la langue et la déconnexion ; un contrôle de plus le fait
   déborder en paysage, et c'est le nôtre qui se faisait couper. */
.bandeau-profil { gap: 6px; max-width: 16rem; }
.bandeau-profil .icone { width: 14px; height: 14px; flex: none; }
.bandeau-profil .profil-valeur {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Sous 1100 px, le nom du métier cède la place : le signe suffit à dire où
   cliquer, et le bandeau ne pousse plus la recherche hors de l'écran. */
@media (max-width: 1100px) {
  .bandeau-profil .profil-valeur { display: none; }
}

@media (max-width: 720px) {
  .choix-metiers h1 { font-size: 1.2rem; }
  .choix-metier { padding: 12px 14px; }
  /* Sous le pouce, l'action principale prend la largeur : une cible pleine
     largeur ne se rate pas, et c'est le dernier geste de l'écran. */
  .choix-metiers-actions { flex-direction: column; align-items: stretch; }
  .choix-metiers-actions .btn { width: 100%; justify-content: center; }
  /* Le nom du métier ne se tronque pas : il est ce qu'on lit pour décider. */
  .choix-metier-nom { flex-wrap: wrap; }
}

/* ===================== Le menu plié et « Toutes les fonctions » (lot 2) ===================== */

/* Un sous-module du métier choisi, sous son métier. Le décalage porte le rang ;
   le filet le rattache à son parent sans dessiner une seconde bordure épaisse. */
.laterale-menu .entree-sous {
  /* 28 px de haut : le banc mesurait 20 px, sous le plancher de 24 px des
     cibles. Un sous-module est une porte comme une autre — plus discrète, pas
     plus petite à viser. */
  padding-left: 34px; min-height: 28px; font-size: 13px; color: var(--encre-douce);
  position: relative;
}
.laterale-menu .entree-sous::before {
  content: ""; position: absolute; left: 19px; top: 0; bottom: 0;
  border-left: 1px solid var(--bordure);
}
.laterale-menu .entree-sous.active { color: var(--primaire-fonce); }

.toutes-fonctions { max-width: 68rem; }
.toutes-fonctions h1 { margin: 0 0 6px; font-size: 1.35rem; }
.toutes-fonctions-intro { margin: 0 0 18px; }

.toutes-fonctions-recherche {
  display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 24px;
  padding: 10px 12px; background: var(--surface);
  border: 1px solid var(--bordure); border-radius: var(--rayon);
}
.toutes-fonctions-recherche .icone { width: 16px; height: 16px; color: var(--encre-attenuee); flex: none; }
.toutes-fonctions-recherche input {
  flex: 1 1 14rem; min-width: 0; min-height: 36px;
  border: 0; background: transparent; font: inherit; color: var(--encre);
}
.toutes-fonctions-recherche input:focus { outline: none; }
.toutes-fonctions-recherche:focus-within { border-color: var(--primaire); }

.toutes-fonctions-groupe { margin-bottom: 26px; }
.toutes-fonctions-groupe h2 {
  margin: 0 0 10px; font-size: 12px; font-weight: 500; letter-spacing: .14em;
  text-transform: uppercase; color: var(--encre-douce);
}
.grille-fonctions {
  display: grid; gap: 8px;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 232px), 1fr));
}
/* Une fonction est une porte, pas une carte : une ligne, son signe, son nom, et
   le métier dont elle relève quand le nom seul ne suffit pas à la situer. */
.fonction {
  display: flex; align-items: center; gap: 10px; min-height: 44px;
  padding: 8px 12px; background: var(--surface);
  border: 1px solid var(--bordure); border-radius: var(--rayon);
  color: var(--encre); text-decoration: none;
  transition: border-color var(--duree-court) var(--entree);
}
.fonction:hover { border-color: var(--primaire); color: var(--encre); text-decoration: none; }
.fonction:focus-visible { outline: 2px solid var(--primaire); outline-offset: 2px; }
.fonction .icone { width: 16px; height: 16px; flex: none; color: var(--encre-attenuee); }
.fonction-nom { min-width: 0; overflow-wrap: anywhere; }
.fonction-parent { margin-left: auto; padding-left: 8px; color: var(--encre-attenuee); font-size: 12px; white-space: nowrap; }
.toutes-fonctions-vide { margin-top: 20px; }

@media (max-width: 720px) {
  .toutes-fonctions h1 { font-size: 1.2rem; }
  .toutes-fonctions-recherche .btn { flex: 1 1 auto; justify-content: center; }
  /* Le métier d'origine passe sous le nom : en colonne étroite, le pousser à
     droite écrasait le nom de la fonction, qui est ce qu'on vient lire. */
  .fonction { align-items: flex-start; flex-wrap: wrap; }
  .fonction .icone { margin-top: 3px; }
  .fonction-parent { margin-left: 26px; padding-left: 0; white-space: normal; }
}

/* ===================== Le tableau de bord par profil (lot 3) ===================== */

/* Ce que le métier a à faire, en tête de l'accueil. Le filet terre cuite le
   rattache à la maison sans répéter une carte de plus : l'écran en porte déjà. */
.bande-metier, .bande-chantier {
  background: var(--surface); border: 1px solid var(--bordure);
  border-radius: var(--rayon); margin-bottom: 10px;
}
.bande-metier--seule { padding: 16px; border-top: 2px solid var(--maison); }
.bande-metier-entete { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-bottom: 12px; }
.bande-metier-entete h2 {
  display: flex; align-items: center; gap: 8px;
  margin: 0; font-size: 1.05rem; color: var(--primaire-fonce);
}
.bande-metier-entete h2 .icone { width: 18px; height: 18px; }
.bande-metier-entete .btn { margin-left: auto; }

.bandes-metiers, .bandes-chantiers { margin-bottom: 18px; }
.bandes-chantiers > h2 {
  margin: 0 0 10px; font-size: 12px; font-weight: 500; letter-spacing: .14em;
  text-transform: uppercase; color: var(--encre-douce);
}
/* details/summary : natif, au clavier comme au doigt, sans une ligne de script. */
.bande-metier > summary, .bande-chantier > summary {
  display: flex; align-items: center; gap: 8px;
  min-height: 48px; padding: 10px 14px; cursor: pointer;
  font-weight: 500; color: var(--primaire-fonce); list-style: none;
}
.bande-metier > summary::-webkit-details-marker,
.bande-chantier > summary::-webkit-details-marker { display: none; }
.bande-metier > summary::after, .bande-chantier > summary::after {
  content: ""; margin-left: auto; width: 8px; height: 8px;
  border-right: 1.5px solid var(--encre-attenuee); border-bottom: 1.5px solid var(--encre-attenuee);
  transform: rotate(45deg); transition: transform var(--duree-court) var(--entree);
}
.bande-metier[open] > summary::after, .bande-chantier[open] > summary::after { transform: rotate(-135deg); }
.bande-metier > summary:hover, .bande-chantier > summary:hover { background: var(--fond); }
.bande-metier > summary .icone, .bande-chantier > summary .icone { width: 16px; height: 16px; flex: none; }
.bande-nom { min-width: 0; overflow-wrap: anywhere; }
.bande-corps { padding: 0 14px 14px; }

/* Les écrans du métier : des portes en ligne, pas des cartes. */
.bande-portes { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }
/* L'accueil d'une entreprise générale : ses chantiers, et rien d'autre. Les
   outils métier s'ouvrent depuis la page du chantier, où ils vivent avec ce que
   chacun y suit — recopiés ici, ils donnaient quinze lignes par chantier sur un
   téléphone, le mur que ce chantier démonte (planche du 18.09.2026). */
.liste-chantiers-accueil {
  list-style: none; margin: 0; padding: 0;
  border: 1px solid var(--bordure); border-radius: var(--rayon); background: var(--surface);
}
.liste-chantiers-accueil > li + li { border-top: 1px solid var(--bordure); }
.liste-chantiers-accueil a {
  /* 12 px : le nom d'un chantier est long et gras, et un signe de 16 px posé à
     10 px de lui se lit collé (planche du 18.09.2026). */
  display: flex; align-items: center; gap: 12px;
  min-height: 48px; padding: 10px 14px;
  color: var(--primaire-fonce); font-weight: 500; text-decoration: none;
}
.liste-chantiers-accueil a:hover { background: var(--fond); text-decoration: none; }
.liste-chantiers-accueil .icone { width: 16px; height: 16px; flex: none; color: var(--encre-attenuee); }
.porte {
  display: inline-flex; align-items: center; min-height: 36px; padding: 6px 12px;
  border: 1px solid var(--bordure); border-radius: 999px; background: var(--fond);
  color: var(--encre); font-size: 13px; text-decoration: none;
  transition: border-color var(--duree-court) var(--entree);
}
.porte:hover { border-color: var(--primaire); color: var(--primaire-fonce); text-decoration: none; }
.porte:focus-visible { outline: 2px solid var(--primaire); outline-offset: 2px; }
.porte-chantier { background: var(--primaire-tint); border-color: var(--filet-or-faible); color: var(--primaire-fonce); }

.bande-chantiers { list-style: none; margin: 0; padding: 0; }
.bande-chantiers li {
  display: flex; align-items: center; gap: 10px;
  min-height: 44px; padding: 6px 0; border-top: 1px solid var(--bordure);
}
.bande-chantiers li a { min-width: 0; overflow-wrap: anywhere; }
.bande-compte {
  margin-left: auto; padding-left: 8px; flex: none;
  color: var(--encre-douce); font-variant-numeric: tabular-nums; font-size: 13px;
}
.bande-vide { margin: 0; font-size: 13px; }

@media (max-width: 720px) {
  .bande-metier--seule { padding: 14px; }
  .bande-metier-entete .btn { margin-left: 0; width: 100%; justify-content: center; }
  /* Une porte pleine largeur au doigt : viser une pastille de 90 px sur un
     chantier, une main sur l'échelle, est une cible qu'on rate. Seulement dans
     une bande de MÉTIER, qui en porte quatre ou cinq.

     Dans une bande de CHANTIER, l'entreprise générale en porte quinze : empilées
     pleine largeur, elles refont exactement le mur de quarante entrées que ce
     chantier démonte (planche du 18.09.2026). Elles restent donc des pastilles
     qui passent à la ligne, à 44 px de haut. */
  .bande-metier .bande-portes, .mes-epingles .bande-portes { flex-direction: column; }
  .bande-metier .porte, .mes-epingles .porte {
    min-height: 44px; width: 100%; justify-content: center; border-radius: var(--rayon);
  }
  .bande-chantier .porte { min-height: 44px; }
}

/* Les fonctions dans la recherche (lot 5) : séparées des objets, et devant eux. */
.recherche-fonctions { margin-bottom: 24px; }

/* ===================== Les épingles (lot 4) ===================== */

.mes-epingles { margin-bottom: 18px; }
.mes-epingles > h2 {
  margin: 0 0 10px; font-size: 12px; font-weight: 500; letter-spacing: .14em;
  text-transform: uppercase; color: var(--encre-douce);
}
.porte-ajout { border-style: dashed; color: var(--encre-douce); background: transparent; }

/* La porte et son épingle : deux cibles distinctes, un seul objet à l'œil. Un
   bouton posé DANS le lien ferait ouvrir l'écran à qui voulait seulement
   épingler ; deux boîtes séparées font lire une colonne de boutons orphelins
   (planche du 18.09.2026). Le cadre est donc porté par la LIGNE, et l'épingle
   s'y range derrière un filet. */
.fonction-ligne {
  display: flex; align-items: stretch;
  background: var(--surface); border: 1px solid var(--bordure); border-radius: var(--rayon);
  transition: border-color var(--duree-court) var(--entree);
}
.fonction-ligne:hover { border-color: var(--primaire); }
.fonction-ligne .fonction {
  flex: 1 1 auto; min-width: 0; border: 0; border-radius: 0; background: transparent;
}
.fonction-ligne .fonction:hover { border: 0; }
.forme-epingle { display: flex; flex: none; border-left: 1px solid var(--bordure); }
.epingle {
  width: 44px; min-width: 44px; min-height: 44px;
  border: 0; border-radius: 0; background: transparent; color: var(--encre-attenuee);
}
.epingle:hover { color: var(--primaire-fonce); background: var(--fond); }
.epingle-posee { color: var(--primaire-fonce); background: var(--primaire-tint); }
.epingle .icone { width: 14px; height: 14px; }

/* Les droits d'un métier, repliés (EXPERIENCE-PROFILS.md) : quinze métiers ouverts
   font soixante-dix cases à lire d'un coup, et personne ne lit soixante-dix cases. */
details.droits-metier { padding: 0; }
details.droits-metier > summary {
  display: flex; align-items: center; gap: 8px;
  min-height: 44px; padding: 8px 12px; cursor: pointer;
  font-weight: 500; list-style: none;
}
details.droits-metier > summary::-webkit-details-marker { display: none; }
details.droits-metier > summary::before {
  content: ""; width: 7px; height: 7px; flex: none;
  border-right: 1.5px solid var(--encre-attenuee); border-bottom: 1.5px solid var(--encre-attenuee);
  transform: rotate(-45deg); transition: transform var(--duree-court) var(--entree);
}
details.droits-metier[open] > summary::before { transform: rotate(45deg); }
details.droits-metier > summary:hover { background: var(--fond); }
.droits-metier-nom { min-width: 0; overflow-wrap: anywhere; }
/* Le compte dit ce qu'il y a dedans sans l'ouvrir : « 0/5 » se lit d'un coup
   d'œil sur quinze lignes, « aucune case cochée » se lit quinze fois. */
.droits-metier-compte {
  margin-left: auto; padding-left: 8px; flex: none;
  color: var(--encre-douce); font-variant-numeric: tabular-nums; font-size: 13px; font-weight: 400;
}
details.droits-metier > .grille-cases-droits { padding: 0 12px 10px; }

/* « Se souvenir de mon choix » : la sortie de qui travaille toujours sur le même
   métier. Posée sous la liste et non à côté du bouton — c'est une décision sur
   les prochaines connexions, pas sur celle-ci. */
.choix-metiers-memoire { align-items: flex-start; margin-top: 14px; }
.choix-metiers-memoire input[type="checkbox"] { margin-top: 10px; width: 24px; height: 24px; flex: none; }
.choix-metiers-memoire > span { display: flex; flex-direction: column; gap: 2px; padding-top: 6px; }
.choix-metiers-memoire .texte-doux { font-size: 13px; }

/* --- Le bloc « Équipes » du tableau de bord (19.09.2026) ---------------------------------------
   Une ligne par personne : qui, où, et l'heure de chacune des étapes de sa journée. Les heures se
   lisent en colonnes alignées d'une personne à l'autre : c'est la comparaison qui fait le contrôle. */
.equipes-jours { display: flex; flex-wrap: wrap; gap: 8px; padding: 12px 18px 4px; }
.equipes-jours .btn-primaire { margin-left: auto; }
.equipes-lignes { list-style: none; margin: 0; padding: 0 18px 10px; }
.equipes-lignes > li { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); gap: 6px 22px; align-items: center;
  padding: 12px 0; border-top: 1px solid var(--bordure); min-width: 0; }
.equipes-lignes > li:first-child { border-top: 0; }
.equipes-lignes > li.est-hors-planning { grid-template-columns: minmax(0, 1fr); padding: 7px 0; }
.equipes-lignes > li.est-hors-planning strong { font-weight: 500; color: var(--encre-attenuee); }
.equipes-personne { display: flex; flex-wrap: wrap; gap: 2px 10px; align-items: center; min-width: 0; }
.equipes-personne a { display: inline-flex; align-items: center; min-height: 44px; }
.equipes-lieu { flex-basis: 100%; font-size: 13px; overflow-wrap: anywhere; }
.est-hors-planning .equipes-lieu { flex-basis: auto; }
.equipes-heures { list-style: none; margin: 0; padding: 0; display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 112px), 1fr)); gap: 6px; min-width: 0; }
.equipes-heures > li { display: flex; flex-direction: column; gap: 1px; padding: 7px 10px; min-width: 0;
  border: 1px solid var(--bordure); border-radius: var(--rayon); }
.equipes-heures > li.est-validee { border-color: color-mix(in srgb, var(--succes) 45%, var(--bordure)); background: var(--succes-tint); }
.equipes-etape { font-size: 12px; color: var(--encre-attenuee); overflow-wrap: anywhere; }
.equipes-heure { font-size: 15px; text-align: left; }
.equipes-heure.tabulaire { font-weight: 600; text-align: left; }
.equipes-par { font-size: 12px; overflow-wrap: anywhere; }
@media (max-width: 900px) {
  .equipes-lignes > li { grid-template-columns: minmax(0, 1fr); }
  .equipes-jours .btn-primaire { margin-left: 0; }
}
@media (max-width: 560px) {
  .equipes-jours .btn { flex: 1 1 calc(50% - 8px); justify-content: center; }
  .equipes-jours .btn-primaire { flex-basis: 100%; }
  .equipes-heures { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* --- Personnaliser le tableau de bord : chacun coche les blocs qu'il veut voir ----------------- */
.personnaliser > summary { cursor: pointer; list-style: none; }
.personnaliser > summary::-webkit-details-marker { display: none; }
.personnaliser-formulaire { padding: 4px 18px 16px; }
.personnaliser-formulaire > p { margin: 8px 0 12px; max-width: 72ch; font-size: 13px; }
.personnaliser-blocs { list-style: none; margin: 0 0 14px; padding: 0; display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr)); gap: 2px 22px; }
.personnaliser-blocs .champ-case { display: flex; align-items: flex-start; gap: 12px; min-height: 44px; padding: 8px 0; cursor: pointer; }
.personnaliser-blocs .champ-case input { width: 26px; height: 26px; margin: 0; flex: 0 0 auto; accent-color: var(--accent, #BA7A45); }
.personnaliser-blocs .champ-case > span { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.personnaliser-blocs .champ-case .texte-attenue { font-size: 12.5px; overflow-wrap: anywhere; }

/* ======================= Le chantier, poste de travail (décisions L1 à L11, 24.09.2026) ==================
   Une adresse par onglet. Le chiffre d'un onglet dit ce qui y attend ; l'aperçu dit, en tête, ce qu'il y a à
   faire et le bouton qui le fait. Aucun filet coloré de côté : le ton d'une attente se dit par son icône et
   son bouton, jamais par la couleur seule. En colonne unique sous 900 px. */
.chantier-reperes { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; }
.onglets-chantier { margin-top: 14px; }
.onglets-chantier .onglet { display: inline-flex; align-items: center; gap: 6px; }
.onglet-compte {
  display: inline-flex; align-items: center; justify-content: center; min-width: 20px; height: 20px;
  padding: 0 6px; border-radius: 999px; font-size: 11.5px; font-weight: 600; font-variant-numeric: tabular-nums;
  background: var(--info-tint); color: var(--info);
}
.onglet-compte--signal { background: var(--avert-tint); color: var(--avert); }
.chantier-onglet { margin-top: 16px; }
.titre-section { font-size: 13px; font-weight: 500; color: var(--encre-douce); margin: 0 0 8px; }
.etat-vide-texte { color: var(--encre-douce); max-width: 72ch; margin: 0; }
.entete-geste { margin-left: auto; }
.entete-geste form { margin: 0; }

/* L'aperçu : ce qui attend en pleine largeur, puis l'affaire et la semaine côte à côte. */
.apercu-grille { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; align-items: start; }
.apercu-grille > .carte { min-width: 0; }
.apercu-attentes { grid-column: 1 / -1; }
.attentes { list-style: none; margin: 0; padding: 0; }
.attente { display: flex; align-items: center; gap: 12px; padding: 12px 16px; border-bottom: 1px solid var(--bordure); }
.attente:last-child { border-bottom: 0; }
.attente-marque { display: inline-flex; flex: none; color: var(--primaire); }
.attente-marque .icone { width: 18px; height: 18px; }
.attente--signal .attente-marque { color: var(--avert); }
.attente--info .attente-marque { color: var(--encre-attenuee); }
.attente-texte { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.attente > .btn { flex: none; }
.demarrage { padding: 18px 16px; border-bottom: 1px solid var(--bordure); }
.demarrage-titre { font-size: 16px; font-weight: 500; color: var(--encre); margin: 0 0 4px; }
.demarrage .texte-attenue { max-width: 72ch; }
.semaine-chantier, .planning-chantier, .sur-place, .besoins { list-style: none; margin: 0; padding: 0; }
.semaine-chantier > li, .planning-jour {
  display: grid; grid-template-columns: 96px minmax(0, 1fr); gap: 4px 12px; padding: 10px 16px;
  border-bottom: 1px solid var(--bordure);
}
.semaine-chantier > li:last-child, .planning-jour:last-child { border-bottom: 0; }
.semaine-jour, .planning-date { color: var(--encre-douce); }
.planning-date { padding-top: 6px; }
.semaine-qui { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.planning-jour > ul { list-style: none; margin: 0; padding: 0; min-width: 0; }
.index-liste--serre { border-top: 0; }
.index-liste--serre .rangee { padding: 9px 16px; }
.index-liste--serre .rangee .cle { width: auto; flex: 1 1 auto; }
.postes-chantier { list-style: none; margin: 0; padding: 10px 16px 14px; display: flex; flex-wrap: wrap; gap: 6px 20px; }

/* Travaux et prix : le montant des lignes, métier par métier, avant le tableau. */
.chiffrage-resume { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 10px 32px; }
.chiffrage-total { display: flex; flex-wrap: wrap; gap: 10px 28px; margin: 0; }
.chiffrage-total dt { font-size: 13px; color: var(--encre-attenuee); margin: 0 0 2px; }
.chiffrage-total dd { margin: 0; font-size: 17px; font-weight: 500; overflow-wrap: anywhere; }
.chiffrage-total dd .texte-attenue { font-size: 12.5px; font-weight: 400; }
.chiffrage-metiers { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 4px 20px;
  color: var(--encre-douce); font-size: 13px; }
.chiffrage-metiers li { display: inline-flex; gap: 8px; }
.sous-bloc { margin-top: 12px; }
.liste .ligne-edition .sous-bloc h4 { font-size: 13px; margin: 0 0 6px; }

/* Le lieu d'une saisie : la liste, et le lieu nouveau décrit sur place (décision L7). */
.lieu-nouveau { margin-top: 8px; }
.lieu-nouveau > summary { display: inline-flex; align-items: center; gap: 6px; min-height: 32px; cursor: pointer;
  color: var(--primaire); list-style: none; }
.lieu-nouveau > summary::-webkit-details-marker { display: none; }
.lieu-nouveau > summary .icone { width: 16px; height: 16px; }
.lieu-nouveau > .grille-champs { margin-top: 8px; padding: 12px 12px 0; border: 1px dashed var(--bordure);
  border-radius: var(--rayon); background: var(--fond); }
.lieu-nouveau > .aide { margin: 6px 0 0; font-size: 12px; color: var(--encre-attenuee); max-width: 72ch; }
.ajout-noeud > summary { cursor: pointer; color: var(--primaire); font-size: 13px; min-height: 28px; }
.ajout-noeud .formulaire-en-ligne { margin-top: 6px; }

/* Le matériel : la liste du chef, ce qui est sur place, ce qui revient. */
table.liste td.cel-case { width: 1%; }
.case-seule { display: inline-flex; align-items: center; justify-content: center; min-width: 32px; min-height: 32px; }
.sur-place-ligne { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 8px 16px;
  padding: 12px 0; border-bottom: 1px solid var(--bordure); }
.sur-place-ligne:last-child { border-bottom: 0; }
.sur-place-quoi { display: flex; flex-direction: column; align-items: flex-start; gap: 3px; min-width: 0; flex: 1 1 260px; }
.sur-place-titre { font-weight: 500; overflow-wrap: anywhere; }
.sur-place-rapporter { margin: 0; }
.sur-place-rapporter .champ input { width: 120px; }

/* L'équipe : les besoins, qui est libre, le planning du chantier. */
.besoin { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 16px;
  padding: 12px 0; border-bottom: 1px solid var(--bordure); }
.besoin:last-child { border-bottom: 0; }
.besoin-quoi { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; min-width: 0; flex: 1 1 280px; }
.besoin-titre { font-weight: 500; }
.besoin-gestes { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.besoin-gestes form { margin: 0; }
.candidats-formulaire { margin-top: 18px; }
.candidats, .travaux-a-citer, .metiers-personne { border: 0; padding: 0; margin: 0; min-width: 0; }
.candidats legend, .travaux-a-citer legend, .metiers-personne legend {
  font-weight: 500; color: var(--encre-douce); margin-bottom: 8px; padding: 0; }
.candidats { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr)); gap: 8px; }
.candidats > legend { grid-column: 1 / -1; }
.candidat { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 2px 12px; align-items: start; padding: 12px;
  border: 1px solid var(--bordure); border-radius: var(--rayon); background: var(--surface); cursor: pointer; min-width: 0;
  transition: border-color var(--duree-court) var(--entree), background-color var(--duree-court) var(--entree); }
.candidat:hover { border-color: color-mix(in srgb, var(--primaire) 40%, var(--bordure)); }
.candidat:has(input:checked) { border-color: var(--primaire); background: var(--primaire-tint); }
.candidat:has(input:focus-visible) { outline: 2px solid var(--primaire); outline-offset: 2px; }
.candidat input { grid-row: 1 / span 2; width: 22px; height: 22px; margin: 1px 0 0; }
.candidat-nom { font-weight: 500; overflow-wrap: anywhere; }
.candidat-infos { grid-column: 2; display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
  font-size: 13px; min-width: 0; overflow-wrap: anywhere; }
.travaux-a-citer { margin: 18px 0 6px; }
.travaux-a-citer .champ-case, .cases-metiers .champ-case { display: flex; align-items: center; gap: 8px; min-height: 36px; }
.cases-metiers { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 210px), 1fr)); gap: 0 16px; }
.metiers-personne { margin-bottom: 14px; }

/* Devis et factures : l'acompte et la finale, côte à côte au bureau. */
.pieces-directes { margin: 12px 0 0; }
.gestes-facture { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr)); gap: 24px;
  margin-top: 18px; }
.geste-facture { min-width: 0; }
.besoins-planning .liste-simple li { border-bottom: 0; padding: 3px 0; }

@media (max-width: 900px) {
  .apercu-grille { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 560px) {
  .attente { flex-wrap: wrap; }
  .attente-texte { flex-basis: calc(100% - 30px); }
  .attente > .btn { margin-left: 30px; }
  .semaine-chantier > li, .planning-jour { grid-template-columns: minmax(0, 1fr); }
  .planning-date { padding-top: 0; font-weight: 500; }
  .entete-geste { margin-left: 0; width: 100%; }
  .entete-geste .btn { width: 100%; justify-content: center; }
}
@media (pointer: coarse) {
  .onglets-chantier .onglet { min-height: 44px; }
  .lieu-nouveau > summary, .ajout-noeud > summary { min-height: 44px; }
  .case-seule { min-width: 44px; min-height: 44px; }
  .travaux-a-citer .champ-case, .cases-metiers .champ-case { min-height: 44px; }
  .travaux-a-citer .champ-case input, .cases-metiers .champ-case input { width: 24px; height: 24px; }
}

/* === Suivi commercial, repris de Mydas le 26.09.2026 === */
/* Qualité du prospect et chronologie d'une affaire (25.09.2026) : les étoiles
   se lisent d'un coup d'œil, les échanges comme un fil daté. */
.qualite-prospect { display: grid; gap: 12px; margin-bottom: 18px; }
.etoiles { font-size: 22px; letter-spacing: 2px; color: var(--accent); margin-right: 10px; }
.chronologie { list-style: none; margin: 0; padding: 0; }
.chronologie > li { border-left: 2px solid var(--bordure); padding: 0 0 16px 14px; position: relative; }
.chronologie > li::before { content: ""; position: absolute; left: -6px; top: 4px; width: 10px; height: 10px; border-radius: 50%; background: var(--bordure); }
.chronologie > li.planifie::before { background: var(--accent); }
.chronologie > li.no_show::before { background: var(--danger); }
.chronologie .entete-echange { display: flex; flex-wrap: wrap; gap: 6px 10px; align-items: baseline; }

/* Suivi commercial : trois étapes côte à côte au bureau, empilées au
   téléphone. En classe, jamais en style en ligne (GABARIT-4). */

/* Voyage : frais puis acquisitions, chacun sur toute la largeur (25.09.2026). */

/* --- Suivi commercial en un tableau (retour du 25.09.2026) -------------------
   Une ligne par client, les cases à remplir dans la ligne même. Les champs
   restent compacts (30 px) pour tenir sept colonnes au bureau ; au téléphone,
   `liste--empilable` range chaque client en fiche, champs pleine largeur. */
.suivi-filtres { margin: 0 0 12px; }
.suivi-filtres input[type="search"] { min-width: 14em; }
.suivi-table td { vertical-align: top; }
.suivi-table .suivi-saisie { min-width: 11em; }
.suivi-table .suivi-saisie > * + * { margin-top: 6px; }
.suivi-table input, .suivi-table select {
  display: block; width: 100%; height: 30px; padding: 0 8px; font: inherit; font-size: 13px;
  border: 1px solid var(--bordure); border-radius: var(--rayon); background: var(--fond-carte);
}
.suivi-table input:focus-visible, .suivi-table select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.suivi-rangee { display: flex; gap: 6px; }
.suivi-rangee > * { flex: 1 1 0; min-width: 0; }
.suivi-rangee .suivi-heure { flex: 0 0 4.6em; }
.suivi-client { font-weight: 600; }
.suivi-compteurs { font-size: 12px; margin-top: 2px; }
.suivi-extrait { font-size: 12px; margin-top: 4px; max-width: 22em; }
.suivi-message { font-size: 12px; margin: 6px 0 0; }
.suivi-message .icone { width: 14px; height: 14px; vertical-align: -2px; }
.suivi-ajout { display: flex; flex-wrap: wrap; gap: 8px; padding: 12px; border-top: 1px solid var(--bordure); }
.suivi-ajout input { height: 32px; padding: 0 10px; font: inherit; border: 1px solid var(--bordure); border-radius: var(--rayon); flex: 1 1 12em; }
.suivi-aide { margin-top: 10px; max-width: 70ch; }
.suivi-masques { margin: 0; padding: 10px 12px; font-size: 13px; }
.ligne-suivi.ligne-retard > td:first-child { box-shadow: inset 2px 0 0 var(--danger); }
/* Les étoiles : cinq boutons radio, cibles de 26 px au doigt. L'ordre est
   inversé dans le code pour que « ~ » allume les étoiles de gauche. */
.notation { display: inline-flex; flex-direction: row-reverse; justify-content: flex-end; border: 0; margin: 4px 0 0; padding: 0; }
.notation input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.notation label { display: inline-flex; width: 26px; height: 26px; align-items: center; justify-content: center; cursor: pointer; color: var(--encre-attenuee); transition: color var(--duree-court) var(--entree); }
.notation label .icone { width: 18px; height: 18px; }
.notation input:checked ~ label, .notation label:hover, .notation label:hover ~ label { color: var(--accent); }
.notation input:checked ~ label .icone, .notation label:hover .icone, .notation label:hover ~ label .icone { fill: currentColor; }
.notation input:focus-visible + label { outline: 2px solid var(--accent); border-radius: 4px; }
/* Mesuré au banc le 25.09.2026 : en mise en page automatique, la largeur
   naturelle des champs faisait déborder la table dès le bureau, et
   `liste--empilable` la rangeait en fiches — le tableau demandé n'existait
   plus. Largeurs fixes à partir de 1100 px ; en dessous, les fiches. */
@media (min-width: 1100px) {
  .suivi-table { table-layout: fixed; width: 100%; }
  /* 26.09.2026, mesuré à 1440 px : « Motif si perdu », « Responsable » et le
     nom du commercial étaient rognés à 11 %, le titre « Budget · responsable »
     coupé net. Le dernier contact, du texte qui passe à la ligne, cède 2 %. */
  .suivi-table th:nth-child(1) { width: 16%; }
  .suivi-table th:nth-child(2) { width: 13%; }
  .suivi-table th:nth-child(3) { width: 18%; }
  .suivi-table th:nth-child(4) { width: 17%; }
  .suivi-table th:nth-child(5) { width: 13%; }
  .suivi-table th:nth-child(6) { width: 14%; }
  .suivi-table th:nth-child(7) { width: 9%; }
  /* `table.liste th` coupe tout titre à la ligne : plus spécifique ici. */
  table.liste.suivi-table th { white-space: normal; overflow: visible; text-overflow: clip; }
  .suivi-table .suivi-saisie { min-width: 0; }
  .suivi-table .btn { width: 100%; justify-content: center; padding-inline: 6px; }
}
.suivi-table input, .suivi-table select { min-width: 0; }
.suivi-filtres input, .suivi-filtres select {
  height: 32px; padding: 0 10px; font: inherit; border: 1px solid var(--bordure);
  border-radius: var(--rayon); background: var(--fond-carte);
}
@media (max-width: 600px) {
  .suivi-rangee { flex-wrap: wrap; }
  .suivi-rangee > select { flex-basis: 100%; }
}
/* Second passage du banc (25.09.2026) : étoiles rognées à côté du nom,
   « Enregistrer » coupé au bord, canal réduit à « App ». */
.suivi-table .notation { display: flex; }
.suivi-table .suivi-client { display: block; white-space: normal; overflow-wrap: anywhere; }
.suivi-table .cel-titre { white-space: normal; }
.suivi-prevu { display: flex; flex-wrap: wrap; gap: 4px 6px; align-items: baseline; }
.suivi-rangee--prochain { flex-wrap: wrap; }
.suivi-rangee--prochain > select { flex-basis: 100%; }
@media (min-width: 1100px) {
  .suivi-table th:nth-child(3), .suivi-table th:nth-child(4) { width: 18%; }
  .suivi-table th:nth-child(7) { width: 10%; }
  .suivi-table .btn { font-size: 13px; }
}
.suivi-table th { white-space: normal; }
.notation label ~ label { transition: color var(--duree-court) var(--entree); }
