/* =========================================================================
   Mydas — feuille de style
   Charte : docs/DESIGN.md (V3 « taille émeraude »). Interface neutre par
   obligation gemmologique : la photo de la pierre est le seul élément saturé
   de l'écran. Séparation par bordures et contraste de fond, JAMAIS par ombre.
   L'ERP est carré. Deux graisses d'Inter seulement : 400 et 500.

   Le vert profond et le doré sont des MOMENTS (connexion, états vides,
   passeport), jamais le thème du quotidien. Trois usages du doré, pas un de
   plus : filet de cadre sur fond sombre, arêtes du picto, montant mis en scène.
   ========================================================================= */

/* --- 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é — le vert de la marque, présent partout dans le quotidien. */
  --primaire:        #006039;
  --primaire-fonce:  #004B2E;
  --primaire-tint:   #E9F0EA;

  /* Moments — aplats forts, cadrés d'un filet doré. Jamais le quotidien. */
  --vert-profond:    #04331F;
  --or:              #C29A4B;
  --filet-or:        rgba(194, 154, 75, .55);
  --filet-or-faible: rgba(194, 154, 75, .22);
  --sur-vert:        #C9DCD1;

  /* 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:          #A8792B;
  --accent-texte:    #8A6220;
  --accent-tint:     #F7F1E4;

  /* 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:      #006039;
  --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(--or); }

/* 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, poids, carat, 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, MYDAS en capitales espacées. */
.wordmark { display: inline-flex; align-items: center; gap: 12px; text-decoration: none; }
.wordmark .picto { width: 26px; height: 26px; flex: none; }
.wordmark .lettres {
  font-family: var(--police-marque); font-weight: 400;
  font-size: 1.2rem; letter-spacing: .16em; text-indent: .16em;
  color: var(--primaire); 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(--or); }

/* ======================= 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; }
.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; }
.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 de pierre 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 pierres
   de Mydas) : ~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
   midas.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); }
/* La ligne d'où l'on revient (midas.js, `retrouverPlace`) : teintée 2,4 s, le
   temps que l'œil la retrouve, puis la transition de fond ci-dessus l'efface.
   Une classe posée puis retirée plutôt qu'une animation : sous
   prefers-reduced-motion une animation durerait .01 ms, et le repère
   disparaîtrait avant d'avoir été vu. Le fond ne reste pas — une teinte
   durable fausserait le jugement des vignettes (DESIGN.md). */
table.liste tbody tr.ligne-retrouvee { background-color: 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 de pierres, 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); }
.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. */
.tableau-defilant { overflow-x: auto; }

/* 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 midas.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; }
/* Une mention sous un montant s'enveloppe dans la largeur que le montant donne
   à sa colonne ; le montant, lui, ne se coupe jamais. Tenue sur une ligne par
   le nowrap des cellules, la seule mention « Sans prix d'achat, hors volume »
   faisait défiler tout le rapport « Activité par tiers » sur une tablette, où
   il tenait sans elle (banc du 25.09.2026). */
table.liste td .mention-montant { display: block; 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; }

/* L'en-tête de l'accueil d'un module (24.09.2026) : son nom, ce qu'il fait, et
   la flèche de son tutoriel. Le titre reprend la taille des fiches ; la
   description se lit d'un trait sans repousser le tableau hors de l'écran. */
.entete-module { margin: 0 0 12px; }
.entete-module h1 { font-size: 1.35rem; margin: 0 0 4px; }
.module-description { margin: 0; max-width: 72ch; color: var(--encre-attenuee); line-height: 1.5; }
.hv-titre .module-description { margin-top: 6px; }
/* La flèche du tutoriel, repère discret mais cible franche. Au doigt, 44 px de
   haut comme toute cible de terrain (repris d'Oikos, 17.09.2026) ; à la souris,
   32 px suffisent et laissent le tableau remonter. */
.lien-discret-tutoriel {
  display: inline-flex; align-items: center; min-height: 32px;
  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; }
@media (pointer: coarse) { .lien-discret-tutoriel { min-height: 44px; } }
.liste-tutoriels { list-style: none; margin: 10px 0 0; padding: 0; display: grid; gap: 8px; }
.liste-tutoriels li { display: grid; }
.liste-tutoriels .texte-attenue { font-size: 13px; line-height: 1.45; }

/* ======================= É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 pierre. */
.moment {
  background: var(--vert-profond);
  color: #FFFFFF;
  position: relative;
}
.moment .doux { color: var(--sur-vert); }
.moment .valeur-or { color: var(--or); 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(--filet-or);
}
.cadre-or::before { inset: 20px; }
.cadre-or::after { inset: 26px; border-color: var(--filet-or-faible); }

/* ======================= 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 la fiche prêt, la fiche gemme et la fiche vente. 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
   gemme » 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; }
.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, stock métal, cours). */
/* 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); }
.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 pierre n'y est jugée. */
body.connexion { display: block; background: var(--vert-profond); }
.connexion-scene {
  min-height: 100vh; display: flex; align-items: center; justify-content: center;
  padding: 32px 16px; position: relative;
}
/* Filigrane : le picto en très grand, à 6 % — l'unique motif de la marque.
   Il ne compte pas comme un second picto : la charte le pose ainsi sur la
   couverture, emblème compris. */
.connexion-filigrane {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: min(720px, 88vw); color: var(--or); opacity: .06; pointer-events: none;
}
.connexion-filigrane svg { width: 100%; height: auto; display: block; }
/* 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: 372px;
  background: var(--surface); border: 1px solid var(--filet-or);
  border-radius: var(--rayon); padding: 36px 32px 32px;
}
.connexion-embleme { display: block; width: 56px; margin: 0 auto; color: var(--primaire); }
.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: .18em; text-indent: .18em; text-transform: uppercase;
  color: var(--primaire); line-height: 1;
}
.connexion-org {
  margin-top: 10px; text-align: center;
  font-size: 12px; font-weight: 500; letter-spacing: .14em;
  text-transform: uppercase; color: var(--encre-attenuee);
}
.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 pierre 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); }
.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 midas.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); }
  .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); }
  .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 (midas.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);
  }

  /* --- La liste des gemmes en cartes ------------------------------------- */
  /* 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-gemmes { display: block; overflow: visible; }
  .liste-gemmes thead { display: none; }
  .liste-gemmes tbody { display: block; }
  .liste-gemmes 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-gemmes td { display: block; padding: 0; border: none; height: auto; }
  /* Le geste de masse (cases à cocher) reste un geste de poste de travail. */
  .liste-gemmes td.col-coche { display: none; }
  .liste-gemmes td:nth-child(2) { grid-area: vignette; align-self: center; width: auto !important; }
  .liste-gemmes td:nth-child(2) .vignette { width: 56px; height: 56px; }
  .liste-gemmes td:nth-child(3) { grid-area: reference; font-weight: 500; }
  .liste-gemmes td:nth-child(4) { grid-area: espece; color: var(--encre-douce); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
  .liste-gemmes 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-gemmes td:nth-child(6) { display: none; }
  .liste-gemmes td:nth-child(7) { grid-area: origine; color: var(--encre-attenuee); font-size: 12px; }
  .liste-gemmes td:nth-child(8) { grid-area: statut; text-align: right; }
  .liste-gemmes 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-gemmes 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: #F2EFE8 url("/static/img/motif/web_fond-1920.webp") center / cover no-repeat;
}
@media (max-width: 1280px) {
  body.connexion { background-image: url("/static/img/motif/web_fond-1280.webp"); }
}
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. */
.vd-coin {
  position: absolute; left: 0; bottom: 0; width: 46%; height: 80%;
  background: url("/static/img/motif/web_coin-900.webp") left bottom / cover no-repeat;
  opacity: .38; mix-blend-mode: multiply; pointer-events: none;
  -webkit-mask-image: radial-gradient(ellipse at left bottom, #000 30%, transparent 72%);
  mask-image: radial-gradient(ellipse at left bottom, #000 30%, transparent 72%);
}

/* Trait organique — la veine comme filet. Page À propos uniquement dans
   l'app : la seule page où Mydas parle de lui. */
.vd-veine {
  height: 18px; width: 150px;
  background: url("/static/img/motif/web_veine-1000.webp") center / cover no-repeat;
  mix-blend-mode: multiply;
  -webkit-mask-image: linear-gradient(90deg, #000 0%, #000 60%, transparent);
  mask-image: linear-gradient(90deg, #000 0%, #000 60%, transparent);
}

/* 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 de pierre : 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 {
  /* La veine source fait 1000x76 : son or et son turquoise tiennent dans le
     tiers gauche, le reste est du marbre pâle. La réduire entière à 136 px la
     rendait grise et floue — une salissure, pas une signature. On cadre donc
     sur la partie vivante, à une échelle qui laisse les éclats lisibles. */
  width: 128px; height: 18px;
  background-size: 330px auto;
  background-position: -4px center;
  margin: 2px 0 14px 20px;
  opacity: 1;
  /* Réduit à cette taille, le marbre pâle du motif dominait et l'or virait au
     gris : une poussière, pas une veine. On remonte la saturation plutôt que la
     taille — la signature doit se lire sans prendre de place. */
  filter: saturate(1.75) contrast(1.18);
  /* Un fondu elliptique, et non le dégradé horizontal de la veine des
     « moments » : sans fondu vertical, le motif gardait deux arêtes franches et
     se lisait comme une photo rognée collée dans la barre. Ici il se dissout
     par tous les bords — le kintsugi affleure, il n'est pas posé. */
  -webkit-mask-image: radial-gradient(ellipse at center, #000 18%, transparent 74%);
  mask-image: radial-gradient(ellipse at center, #000 18%, transparent 74%);
}

/* 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 {
  /* Même cadrage que la variante « marque » — sur la zone vive du motif, à
     une échelle qui garde les éclats lisibles — mais fondu symétrique : la
     veine FERME la page au lieu de pointer vers la droite. */
  width: 168px; height: 20px;
  background-size: 380px auto;
  background-position: 6px center;
  margin: 26px auto 14px;
  filter: saturate(1.75) contrast(1.18);
  -webkit-mask-image: radial-gradient(ellipse at center, #000 16%, transparent 72%);
  mask-image: radial-gradient(ellipse at center, #000 16%, transparent 72%);
}

/* Pas de variante sombre ici : Mydas est en thème clair, délibérément — un
   fond sombre fausse l'appréciation des couleurs de pierre. 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 Mydas. 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: "";
  position: fixed; inset: 0;
  z-index: 0; pointer-events: none;
  background-image:
    linear-gradient(rgba(251, 250, 248, .62), rgba(251, 250, 248, .62)),
    url("/static/img/motif/web_fond-1920.webp");
  background-size: auto, cover;
  background-position: center, center top;
  background-repeat: no-repeat, no-repeat;
  clip-path: inset(var(--bandeau) 0 0 var(--barre-laterale));
}

/* 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 Mydas. */
.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-image:
    linear-gradient(rgba(255, 255, 255, .86), rgba(255, 255, 255, .86)),
    url("/static/img/motif/web_veine-1000.webp");
  background-size: auto, 1100px auto;
  background-position: center, right -80px top -6px;
  background-repeat: no-repeat, no-repeat;
  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: "";
  position: fixed; right: 0; bottom: 0;
  width: 520px; height: 380px;
  z-index: 0; pointer-events: none;
  background: url("/static/img/motif/web_coin-900.webp") right bottom / contain no-repeat;
  /* Le coin d'origine ouvre vers la droite : on le retourne pour qu'il entre
     par l'angle au lieu d'en sortir. */
  transform: scaleX(-1);
  transform-origin: right bottom;
  opacity: .30;
  mix-blend-mode: multiply;
  -webkit-mask-image: radial-gradient(ellipse at right bottom, #000 20%, transparent 75%);
  mask-image: radial-gradient(ellipse at right bottom, #000 20%, transparent 75%);
}
/* Le contenu passe au-dessus : sans cela le coin recouvrirait les dernières
   lignes d'un tableau, ce qui est précisément l'écueil de la « tranche en
   pied » écartée le 21.08. */
.contenu > * { position: relative; z-index: 1; }

/* 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 pierre n'y est jugée, aucun
   chiffre n'y est lu. */
.laterale { position: relative; }
.laterale::before {
  content: "";
  position: absolute; inset: 0;
  pointer-events: none;
  background: url("/static/img/motif/web_bordure-1100.webp") center / cover no-repeat;
  opacity: .20;
  mix-blend-mode: multiply;
}
.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--veine { position: relative; overflow: hidden; }
.etat-vide--veine::before {
  content: ""; position: absolute; left: 0; bottom: 0;
  width: 46%; height: 82%; max-width: 460px;
  background: url("/static/img/motif/web_coin-900.webp") left bottom / cover no-repeat;
  opacity: .38; mix-blend-mode: multiply; pointer-events: none;
  -webkit-mask-image: radial-gradient(ellipse at left bottom, #000 30%, transparent 72%);
  mask-image: radial-gradient(ellipse at left bottom, #000 30%, transparent 72%);
}
.etat-vide--veine > * { position: relative; z-index: 1; }
.etat-vide--veine .picto { opacity: .95; color: var(--accent); }

/* 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 Midas — la toute première pierre / vente / passeport 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é le 27.09.2026). */
.pilotage-tableau .tableau-defilant { position: relative; }
.pilotage-anomalies { margin: 0; padding: 0; list-style: none; }
/* Dans une carte, la barre de gravité se collait au filet de la carte ; sous
   la ligne de paiement de la fiche, la phrase touchait le tableau suivant. */
.carte > .pilotage-anomalies { padding: 12px 16px; }
.pilotage-section > .pilotage-anomalies { margin-bottom: 14px; }
.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; }
/* Le constat est une phrase. `table.liste td` la tenait sur une ligne : la
   colonne élargissait le tableau au-delà de son cadre, et au bureau la phrase
   sortait coupée au bord (« aucune souscr »), lisible seulement en faisant
   défiler le tableau — vu en production le 28.09.2026. */
.pilotage-caisse table.liste td.pilotage-constat { min-width: 260px; white-space: normal; }

/* 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: 80px; 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; }
  /* La dernière case ne s'étale que pour un compte impair (le registre en a
     cinq). À six, elle laissait la cinquième seule sur sa rangée, et les
     filets de la grille à trois colonnes (≤ 1100 px) survivaient ici. */
  .pilotage-indicateurs--six .pilotage-indicateur:nth-child(n) { border-left: 0; border-top: 1px solid var(--bordure); }
  .pilotage-indicateurs--six .pilotage-indicateur:nth-child(even) { border-left: 1px solid var(--bordure); }
  .pilotage-indicateurs--six .pilotage-indicateur:nth-child(-n+2) { border-top: 0; }
  .pilotage-indicateurs--six .pilotage-indicateur:last-child { grid-column: auto; }
  .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; }


/* ======================= 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) {
  .contenu a:not(.btn):not(.onglet):not(.entree):not(.pagination a):not(.miniature-choix) {
    display: inline-block; padding: 6px 0; margin: -6px 0;
  }
}

/* --- Deux grilles sorties des styles en ligne (GABARIT-4) -----------------
   Les cases à publier du passeport, et le champ + bouton d'une proposition de
   pierre : 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; }

/* ======================= L'Hôtel des ventes (09.09.2026) ===================
   La salle en cartes, l'en-tête du module avec ses onglets, les formulaires.
   Tout en classes : aucune disposition en style en ligne (GABARIT-4). */
.hv-entete { display: flex; align-items: flex-start; gap: 16px; flex-wrap: wrap; margin-bottom: 8px; }
.hv-entete h1 { margin: 0 0 4px; }
.hv-titre { flex: 1 1 320px; min-width: 0; }
.hv-sous-titre { margin: 0; color: var(--encre-attenuee); }
.hv-cloche { flex: none; }
.hv-compte {
  display: inline-block; min-width: 18px; padding: 0 6px; border-radius: 9px;
  background: var(--surface); color: var(--primaire); font-size: 12px; text-align: center;
}
.hv-onglets { margin-bottom: 16px; }
.hv-salles { margin-bottom: 12px; }
.hv-recherche { flex-wrap: wrap; }
.hv-recherche .champ-nu { height: 30px; padding: 0 10px; flex: 1 1 180px; min-width: 0; }
.hv-recherche .tabulaire { flex: 0 1 140px; }
.hv-inline { display: inline; }
.hv-expedition { display: inline-flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.hv-expedition input { max-width: 180px; }

.hv-grille {
  display: grid; gap: 14px;
  grid-template-columns: repeat(auto-fill, minmax(230px, 320px));
}
.hv-carte {
  display: flex; flex-direction: column; padding: 0; overflow: hidden; color: inherit; text-decoration: none;
  transition: border-color var(--duree-court) var(--entree);
}
.hv-carte:hover { text-decoration: none; border-color: var(--encre-attenuee); }
.hv-photo {
  display: flex; align-items: center; justify-content: center;
  aspect-ratio: 4 / 3; background: var(--fond); color: var(--encre-attenuee); overflow: hidden;
}
.hv-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.hv-photo .icone { width: 40px; height: 40px; }
.hv-photo--grande { aspect-ratio: 16 / 9; border-radius: var(--rayon); margin-bottom: 12px; }
.hv-corps { padding: 12px 14px 14px; display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.hv-corps h3 { margin: 0; font-size: 15px; font-weight: 500; overflow-wrap: anywhere; }
.hv-numero { font-size: 12px; color: var(--encre-attenuee); letter-spacing: .06em; }
.hv-faits { margin: 0; color: var(--encre-douce); font-size: 13px; overflow-wrap: anywhere; }
.hv-preuves { margin: 2px 0 0; display: flex; flex-wrap: wrap; gap: 4px; }
.hv-pied { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; margin-top: auto; padding-top: 8px; flex-wrap: wrap; }
.hv-prix { font-weight: 500; font-variant-numeric: tabular-nums; }
.hv-prix--grand { font-size: 1.6rem; margin-bottom: 4px; }
.hv-achat form { margin-top: 12px; }
.hv-description { white-space: normal; overflow-wrap: anywhere; }
.hv-liste-preuves { list-style: none; padding: 0; margin: 0 0 12px; display: grid; gap: 6px; }
.hv-liste-preuves .icone { vertical-align: -3px; color: var(--succes); }
.hv-prix-saisie { width: 120px; max-width: 100%; height: 30px; padding: 0 8px; border: 1px solid var(--bordure); border-radius: var(--rayon); font: inherit; text-align: right; }
.hv-formulaire { max-width: 760px; }
table.liste tr.hv-non-lue td { font-weight: 500; }

/* Téléphone : la salle passe à deux cartes, puis une ; les champs de
   recherche prennent toute la largeur — mesuré sur les sept formats. */
@media (max-width: 720px) {
  .hv-grille { grid-template-columns: repeat(auto-fill, minmax(150px, 220px)); gap: 10px; }
  .hv-recherche .tabulaire { flex: 1 1 120px; }
  .hv-cloche { width: 100%; justify-content: center; }
}
@media (max-width: 400px) {
  .hv-grille { grid-template-columns: 1fr; }
}

/* --- « Mettre en vente » au téléphone (11.09.2026) ---------------------------
   Le tableau défilait dans son cadre, règle de la maison, mais la colonne du
   prix — LE geste de l'écran — restait hors champ à droite : au portrait on
   voyait la case, la référence et l'objet, jamais le prix. Sous 720 px chaque
   ligne devient une carte : case · référence · preuve sur la première ligne,
   la description sur la deuxième, le champ du prix sur la troisième avec son
   étiquette (l'en-tête du tableau est parti avec lui). Le tableau reste un
   tableau au-delà de 1024 px : au téléphone couché (844 px), sur la
   tablette debout (768 px) et même couchée (1024 px, barre latérale
   ouverte), la même colonne était coupée au bord droit. Mesuré sur les
   sept formats. */
@media (max-width: 1024px) {
  .contenu table.liste.hv-publication, .hv-publication tbody { display: block; width: 100%; overflow: visible; }
  .hv-publication thead { display: none; }
  .hv-publication tr.hv-ligne {
    display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
    gap: 4px 10px; align-items: center;
    padding: 10px 0; border-bottom: 1px solid var(--bordure);
  }
  .hv-publication tr.hv-ligne:last-child { border-bottom: 0; }
  .hv-publication tr.hv-ligne > td { display: block; width: auto; height: auto; padding: 0; border: 0; min-width: 0; }
  .hv-publication .hv-cell-coche { grid-row: 1; grid-column: 1; }
  .hv-publication .hv-cell-ref { grid-row: 1; grid-column: 2; font-weight: 500; overflow-wrap: anywhere; }
  .hv-publication .hv-cell-preuve { grid-row: 1; grid-column: 3; }
  .hv-publication .hv-cell-desc { grid-column: 2 / -1; color: var(--encre-douce); overflow-wrap: anywhere; }
  .hv-publication .hv-cell-prix {
    grid-column: 2 / -1; display: flex; justify-content: space-between; align-items: center; gap: 8px;
  }
  .hv-publication tr.hv-ligne > td.hv-cell-prix--vide { display: none; }
  .hv-publication .hv-cell-prix::before { content: attr(data-etiquette); color: var(--encre-douce); font-size: 13px; }
  .hv-publication .hv-cell-prix .hv-prix-saisie { width: 150px; height: 40px; font-size: 16px; }
}

/* Le fil d'Ariane ne pousse plus le compte hors du bandeau (09.09.2026) : un
   titre long — « Saphir padparadscha 2 à 3 ct, certifié » — faisait déborder
   « Déconnexion » de 142 px à 1024 px. Le fil s'écrase avec des points de
   suspension, le compte garde sa place. */
.fil-ariane { min-width: 0; overflow: hidden; }
.fil-ariane .courant { overflow: hidden; text-overflow: ellipsis; }
.bandeau-compte { flex: none; }

/* --- Le paiement retenu de l'Hôtel des ventes (13.09.2026, ADR 0014) --------
   Les cases et les boutons radio d'un geste qui engage de l'argent (confirmer
   la réception, désigner le responsable d'un remboursement) portent leur
   phrase à côté de la case, sur toute la largeur : la case seule, collée à un
   libellé qui passe à la ligne, se manquait au doigt. Les montants s'alignent
   à droite dans leur liste ; la page publique du lien de réception garde le
   texte centré de la vitrine, mais pas ses formulaires, illisibles centrés. */
.hv-choix { display: flex; gap: 10px; align-items: flex-start; margin: 8px 0 12px; cursor: pointer; line-height: 1.45; }
.hv-choix input { flex: none; margin-top: 2px; }
.hv-choix span { min-width: 0; overflow-wrap: anywhere; }
.hv-choix-groupe { border: 0; padding: 0; margin: 12px 0; min-width: 0; }
.hv-choix-groupe legend { font-weight: 500; margin-bottom: 4px; }
.hv-parcours { margin: 12px 0 0; padding-left: 20px; display: grid; gap: 4px; }
.hv-etat > p { overflow-wrap: anywhere; }
.hv-etat h3 { margin: 16px 0 8px; font-size: 15px; }
.hv-etat .fiche-colonnes { margin-top: 12px; }
.hv-rembourser { margin-top: 16px; border-top: 1px solid var(--bordure); padding-top: 12px; }
.hv-rembourser summary { cursor: pointer; color: var(--encre-douce); }
.hv-rembourser form { margin-top: 8px; max-width: 760px; }
.hv-montants dd.montant { text-align: right; font-variant-numeric: tabular-nums; }
.hv-reception .faits { text-align: left; max-width: 560px; margin: 20px auto; }
.hv-reception .hv-reponses { text-align: left; margin-top: 20px; }
.hv-reception .avis { text-align: left; }
@media (max-width: 720px) {
  .hv-etat .barre-actions { flex-direction: column; align-items: stretch; }
  .hv-etat .barre-actions .btn, .hv-etat .barre-actions form { width: 100%; }
  .hv-etat .barre-actions form .btn { width: 100%; justify-content: center; }
}
/* Les icônes posées dans un texte de l'Hôtel gardent la taille du texte
   (13.09.2026). Une icône SVG sans règle de taille prend les 300 × 150 px par
   défaut d'un élément remplacé : la coche « Vérifiée » de l'adresse de contact
   et celle de « La preuve » en salle s'affichaient en coches géantes par-dessus
   les champs. Le banc ne le voit pas — rien ne déborde, la coche tient dans son
   cadre — la planche pleine page, si. */
.aide .icone, .texte-attenue .icone, .hv-liste-preuves .icone {
  width: 1em; height: 1em; vertical-align: -0.125em; flex: none;
}
/* Les avis passent à la ligne au lieu de s'écraser (15.09.2026). Sur la fiche
   d'un dossier douanier au téléphone debout, « Le dossier est complet », son
   explication et le formulaire d'expédition tenaient sur UNE rangée flex :
   quatre colonnes de 60 à 170 px, un mot par ligne, le champ de date collé au
   bord de l'écran. Le banc ne le comptait pas — rien ne sort du cadre — la
   planche, si. */
@media (max-width: 720px) {
  .avis { flex-wrap: wrap; }
  .avis .appoint { flex: 1 1 100%; }
  .avis-actions { flex: 1 1 100%; flex-wrap: wrap; margin-left: 0; }
}

/* ======================= Les actions à portée du doigt (16.09.2026) ========
   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, règle de la maison, et « hors cadre » exempte tout ce qu'un ancêtre
   rogne. Le banc compte désormais les actions hors de vue et les actions
   rognées : treize écrans au premier passage, jusqu'au dossier douanier au
   bureau (166 px). 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
   midas.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 », sur
     la fiche d'un prêt, collé à ses bords. */
  .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%; }
}

/* --- La barre de filtre d'une liste -----------------------------------------
   Le filtre des dossiers douaniers (type, recherche, « Filtrer ») tenait en
   `inline-flex` posé en style en ligne, sans retour à la ligne : au téléphone
   debout, « Filtrer » était rogné de 81 px par la zone de contenu — ni
   visible, ni atteignable en glissant. */
.barre-filtre { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px; min-width: 0; max-width: 100%; }
@media (max-width: 720px) {
  .barre-filtre { display: flex; flex: 1 1 100%; }
  .barre-filtre input[type="search"] { flex: 1 1 140px; min-width: 0; }
}

/* --- Le fil d'Ariane entre 720 et 1100 px -------------------------------------
   À 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; }
}

/* --- Les langues de l'interface (24.09.2026) ------------------------------------
   Deux boutons, les deux autres langues : chacun nomme la langue où il mène. Repris d'Oikos,
   où « DE » en petites capitales se lisait à peine au bureau. */
.bascule-langue { display: inline-flex; gap: 4px; margin: 0; }
.bascule-langue .btn { min-width: 44px; justify-content: center; font-weight: 600; letter-spacing: .04em; }

/* Entre 720 et 960 px (tablette debout, téléphone couché), les deux boutons de langue prennent la place du
   raccourci clavier, qu'aucun écran tactile ne tape : sans cela la recherche ne gardait que sa loupe, vu sur la
   planche allemande du 24.09.2026. Le champ rend alors la marge qu'il réservait au raccourci : sans elle,
   « Suchen… » s'y coupait en « Such » (planche du 25.09.2026). */
@media (max-width: 960px) {
  .recherche-globale kbd { display: none; }
  .recherche-globale input { padding-right: 10px; }
  .bascule-langue .btn { min-width: 36px; }
}

/* --- Coût de revient et marge (chantier du 25.09.2026) --------------------- */
/* Une quote-part pas encore figée se lit autrement qu'un fait : un frais
   ajouté demain la change. */
.provisoire { font-style: italic; color: var(--encre-douce); }
/* Le décompte garde ses retours à la ligne, mais se replie au téléphone au
   lieu d'emporter la page dans un défilement horizontal. */
.detail-calcul pre { white-space: pre-wrap; word-break: break-word; margin: 8px 0 0; font-size: 13px; }
.cout-lignes { width: 100%; }
.cout-lignes td { padding: 4px 0; }
.cout-lignes tr.total td { border-top: 1px solid var(--bordure); font-weight: 600; padding-top: 8px; }
/* Les pierres d'un lot : une liste qui se lit, pas un tableau de plus. */
.liste-simple { margin: 6px 0 12px; padding-left: 18px; }
.liste-simple li { padding: 3px 0; }
/* Carats d'une vente partielle de lot, sous le sélecteur de pierre. */
.champ-carats { display: block; width: 100%; max-width: 140px; margin-top: 6px; }

/* 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, #b08d3c); 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, #b08d3c); }
.chronologie > li.no_show::before { background: var(--danger, #a33); }
.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). */
.suivi-colonnes { display: grid; gap: 16px; grid-template-columns: repeat(3, minmax(0, 1fr)); margin-top: 16px; }
@media (max-width: 900px) { .suivi-colonnes { grid-template-columns: 1fr; } }

/* Voyage : frais puis acquisitions, chacun sur toute la largeur (25.09.2026). */
.fiche-colonnes.fiche-colonnes--pile { grid-template-columns: minmax(0, 1fr); }

/* --- 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, #fff);
}
.suivi-table input:focus-visible, .suivi-table select:focus-visible { outline: 2px solid var(--accent, #0b5b45); 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, #a33); }
/* 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, #9a9a9a); 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: #b08d3c; }
.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, #0b5b45); 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, #fff);
}
@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); }

/* Lots hétérogènes (26.09.2026) : la composition se saisit en rangées, et une
   pierre sort de sa ligne. Champs à la largeur de leur colonne, jamais plus :
   sinon la table déborde au bureau et midas.js l'empile en fiches. */
.composition-saisie input { width: 100%; min-width: 0; height: 32px; padding: 0 8px; font: inherit;
  border: 1px solid var(--bordure); border-radius: var(--rayon); background: var(--fond-carte, #fff); }
.composition-saisie td { vertical-align: middle; }
.extraction-ligne { display: flex; gap: 6px; align-items: center; justify-content: flex-end; flex-wrap: wrap; }
.extraction-ligne input { width: 4.5em; height: 32px; padding: 0 8px; font: inherit; text-align: right;
  border: 1px solid var(--bordure); border-radius: var(--rayon); }
/* Mesuré au banc, 1440 px : sans retour à la ligne, la composition débordait
   l'onglet de 760 px et s'empilait en fiches même au bureau. */
table.liste.composition-lot th, table.liste.composition-lot td { white-space: normal; }
table.liste.composition-lot .extraction-ligne { flex-wrap: nowrap; }
table.liste.composition-lot .extraction-ligne input { width: 3.5em; }
.detail-composition { margin-top: 18px; }
.detail-composition > summary { cursor: pointer; font-weight: 600; min-height: 24px; }
.detail-composition > form { margin-top: 10px; }

/* Composition au téléphone (26.09.2026, mesuré à 390 px) : huit rangées de
   quatre champs, libellés à gauche sur trois lignes, faisaient 1 600 px de
   formulaire. Deux champs par ligne, libellé au-dessus : moitié moins. */
.empilee > table.composition-saisie > tbody > tr:not([hidden]) { grid-template-columns: 1fr 1fr; gap: 8px 12px; padding: 10px 12px; }
.empilee > table.composition-saisie > tbody > tr > td[data-libelle] { padding-left: 0; }
.empilee > table.composition-saisie > tbody > tr > td[data-libelle]::before {
  float: none; display: block; width: auto; margin: 0 0 2px; line-height: 1.4; }
/* Le résumé d'un lot composé passe à la ligne : sans cela, sa longueur
   poussait le tableau des acquisitions hors de son cadre, empilé au bureau. */
.composition-resume { white-space: normal; }

/* Une pastille de filtre reste une cible de doigt dans toutes les langues : « All » ne fait que trois lettres,
   et sans largeur minimale le téléphone la réduisait à 19 px (banc responsive, anglais, 26.09.2026). */
.filtre-pastille { min-width: 24px; text-align: center; box-sizing: border-box; }
/* Au doigt, une règle générique retire le retrait latéral des liens : le mot touchait la bordure de la pastille. */
a.filtre-pastille { padding-inline: 10px; }

/* 29.09.2026 — Clés API dans Pilotage : les portées se lisent sur plusieurs
   lignes ; la case reste une cible de 24 px et ne repousse pas le texte. */
.champ .pilotage-cle-portee {
  display: flex; align-items: flex-start; gap: 10px; padding-block: 8px;
  cursor: pointer;
}
.pilotage-cle-portee span { min-width: 0; }
.champ .pilotage-cle-portee input[type="checkbox"] {
  width: 24px; height: 24px; margin: 0;
}

/* 29.09.2026 — Le statut allemand d'une entreprise suspendue débordait de
   3 px à 360 px : laisser la pastille revenir à la ligne dans sa cellule. */
.pilotage-cle-etat .pastille { white-space: normal; overflow-wrap: anywhere; }
/* Sur la planche allemande à 360 px, le libellé « Geltungsbereiche »
   chevauchait sa valeur dans la liste empilée. */
.empilee > table.pilotage-cles > tbody > tr > td[data-libelle]::before {
  overflow-wrap: anywhere; hyphens: auto;
}
