﻿/* =========================================================================
   Baccus — feuille de style
   Charte : docs/MOTIF-VEINE-DOR.md. Interface neutre par obligation métier :
   la photographie de la bouteille est le seul élément saturé de l'écran, sans
   quoi on ne peut plus juger une étiquette ni un niveau. Séparation par
   bordures et contraste de fond, JAMAIS par ombre. L'ERP est carré. Deux
   graisses d'Inter seulement : 400 et 500.

   BORDEAUX ET OR depuis le 04.09.2026 — le noir chaud hérité du segment
   d'origine cède au bordeaux du vin rouge, motif compris, par la même méthode
   de recoloration. Le bordeaux et le doré sont des MOMENTS (connexion, états
   vides, passeport) et des aplats de marque, jamais le thème du quotidien, qui
   reste le blanc galerie. L'or garde ses deux emplois de la refonte
   précédente : il porte les LIENS — un lien bordeaux au milieu d'un texte
   presque noir se lit comme du texte souligné par accident — et il porte les
   fêlures du motif, qui étaient auparavant photographiées.

   Tous les ratios cités ci-dessous sont MESURÉS, jamais recopiés :
   `python scripts/mesurer_contrastes.py` les recalcule depuis cette feuille.
   ========================================================================= */

/* --- 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 BORDEAUX de la marque, présent partout dans le quotidien.
     Refonte du 04.09.2026 : le noir chaud hérité du segment d'origine cède au
     bordeaux du vin rouge. La TEINTE est à 349° et non à 5° : un rouge qui
     penche vers l'orange donne de la brique, et la brique n'est pas du vin.
     Sur cette valeur, le blanc tient 13.04:1 (AAA large et petit corps) et
     --or tient 4.98:1 juste à côté — la vibration que le noir chaud produisait
     est conservée, avec un point de contraste de plus. */
  --primaire:        #5A1A26;
  /* Survol. Il FONCE, à l'inverse du noir qu'il remplace : un noir ne peut que
     s'éclaircir, un bordeaux peut descendre vers la lie. Foncer est le bon
     sens ici pour une raison mesurable et non de goût — le blanc du bouton
     passe de 13.04:1 à 15.47:1 au survol, alors qu'éclaircir vers #7A2534 le
     ferait tomber à 9.81:1. L'état survolé d'un bouton ne doit jamais être
     celui où son libellé se lit le moins bien. */
  --primaire-survol: #45121D;
  /* Le lavis : même teinte, presque tout le blanc. --primaire y tient 11.37:1. */
  --primaire-tint:   #F7EDEF;

  /* Moments — aplats bordeaux, cadrés d'un filet doré. Jamais le quotidien.
     Plus profond que --primaire : un aplat plein doit s'enfoncer davantage
     qu'un bouton, sinon les deux se confondent sur le même écran. */
  --bordeaux-profond: #3E1019;
  --or:              #C29A4B;
  --filet-or:        rgba(194, 154, 75, .55);
  --filet-or-faible: rgba(194, 154, 75, .22);
  /* Le texte doux de l'aplat (11.0:1 dessus). Il a quitté le parchemin chaud
     du noir et or (#D8D1C4, teinte 39°) pour la teinte du vin : posé sur du
     bordeaux, un beige jaune paraît olive par contraste simultané — la même
     illusion, en miroir, que celle qui fait bleuir un noir neutre à côté d'or
     (voir `scripts/recolorer_motif.py`). Saturation identique, 0.18. */
  --sur-bordeaux:    #DFD1D3;

  /* Quotidien — le blanc galerie, ni crème ni gris. */
  /* Neutralisé le 25.08.2026 : #1A1D1B portait une pointe de vert (le vert
     était le canal dominant). Invisible à l'œil, mais c'était la dernière
     trace de l'ancienne identité dans le texte courant. Contraste inchangé. */
  --encre:           #1B1A17;   /* ~16: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-survol est son état survolé, ajouté le 25.08.2026 : l'or PORTE
     les liens (voir la règle `a`), et un lien sans état survolé distinct ne
     dit pas qu'il est cliquable. La règle survit au passage au bordeaux : un
     lien en --primaire au milieu d'un texte en --encre ne tient que 1.33:1,
     soit deux couleurs qu'on distingue à peine hors contexte. */
  --accent:          #A8792B;
  --accent-texte:    #8A6220;
  --accent-survol:   #6B4B18;
  --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é. Inchangées par la
     refonte du 04.09.2026 : ce sont des conventions d'interface, pas des
     couleurs de marque, et les repeindre coûterait de la lisibilité pour rien.
     --danger (#A3312B, teinte 3°) reste distinct de --primaire (#5A1A26,
     teinte 349°) — et surtout les deux ne paraissent jamais l'un sur l'autre :
     le danger vit sur son lavis clair, le bordeaux sur un bouton plein. Les
     confondre demanderait de poser un message d'erreur DANS un aplat de
     moment, ce qu'aucun écran ne fait. */
  --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;
  --ligne-tableau:   36px;
  --barre-laterale:  220px;
  --bandeau:         48px;
  /* La largeur utile de la zone de travail. 1680 px laisse passer les tableaux
     les plus larges du produit (le registre d'achat en compte onze colonnes)
     sans qu'un formulaire à deux champs s'étale sur un mètre d'écran. */
  --contenu-max:     1680px;

  /* 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; }
/* Le lien porte l'OR, pas le noir de la marque — et c'est la seule décision
   de la refonte du 25.08.2026 qui ne soit pas un simple remplacement de
   couleur. Le vert sortant se distinguait de l'encre ; un noir ne le peut
   pas : --primaire (#131110) et --encre (#1B1A17) sont le même gris à l'œil,
   et tous les liens du produit auraient disparu dans leur paragraphe.
   Dans un système noir et or, c'est l'or qui signale ce qui répond au clic.
   --accent-texte et non --accent : #A8792B ne tient pas 4.5:1 en petit corps. */
a { color: var(--accent-texte); text-decoration: none; }
a:hover { color: var(--accent-survol); 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); }
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: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;
}
/* Sur un aplat sombre, l'anneau passe à l'or. `.moment` ne suffisait pas :
   la section tarif et l'appel final de la vitrine sont des aplats bordeaux
   sans cette classe, et l'anneau y sortait à 1,25:1 — invisible sur le
   bouton qui engage le plus gros montant de la page. */
.moment :focus-visible,
.tarif-public :focus-visible,
.site-cta :focus-visible,
.fonction-large :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, millimètre, 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, BACCUS 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:hover .lettres { color: var(--primaire-survol); }

/* 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;
}
.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
     (Mydas, 08.09.2026). */
  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 ----------------------------------------------------- */
/* La zone de travail, et sa BORNE HAUTE — ajoutée le 25.08.2026.

   Toutes les media queries de cette feuille étaient des `max-width` : rien
   n'avait jamais été dessiné au-dessus de 1280 px. Sur l'écran de 3836 px du
   client, la conséquence n'est pas cosmétique : un champ de saisie faisait
   1300 px, une zone de texte davantage, et les colonnes d'un tableau
   s'écartaient au point que l'œil ne rattachait plus une valeur à sa ligne.
   Un texte se lit mal au-delà d'une centaine de caractères par ligne ; un
   formulaire, plus mal encore.

   La borne est posée sur les ENFANTS et non sur `.contenu` lui-même : le
   pseudo-élément qui porte la fêlure d'angle est en `fixed` et appartient à la
   fenêtre entière, pas à la colonne de lecture. */
.contenu { grid-area: contenu; padding: 20px; min-width: 0; }

/* Le lien d'une rangée de tableau — ajouté le 25.08.2026.

   Les vingt-cinq tableaux du produit ne s'ouvraient QU'À LA SOURIS : la rangée
   portait `data-href` et un écouteur `click`, rien d'autre. Un caviste au
   clavier tabulait de « Nouvelle bouteille » jusqu'au pied de page sans jamais
   atteindre une seule ligne, et ne pouvait ouvrir aucune fiche — ni bouteille, ni
   tiers, ni vente, ni intervention. Au lecteur d'écran, le tableau était
   annoncé comme une grille inerte.

   Un VRAI lien dans la première cellule, et non un `tabindex` sur la rangée :
   lui seul donne d'un coup le focus, la touche Entrée, le clic du milieu, le
   menu contextuel et l'annonce « lien ». `data-href` reste pour le confort de
   la souris — cliquer n'importe où dans la ligne.

   Il ne se voit pas tant qu'on ne le vise pas : dans un tableau dense, vingt
   libellés soulignés en doré feraient un sapin de Noël. */
.lien-ligne { color: inherit; text-decoration: none; }
.lien-ligne:hover { color: var(--primaire); text-decoration: underline; }
.lien-ligne:focus-visible {
  outline: 2px solid var(--primaire); outline-offset: 2px;
  border-radius: 2px; color: var(--primaire);
}

/* ======================= 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; }
/* Sous-titre posé dans la carte, sous son en-tête : le tableau de bord sépare
   « ce qu'on me demande » des « ordres envoyés ». La carte n'a pas de marge
   intérieure, seul son en-tête en porte une : sans cette règle, le sous-titre
   touche la bordure. Même mesure que les titres de carte d'Oikos. */
.carte > h3.legende { margin: 0; padding: 14px 16px 6px; }

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);
}
/* 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
   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. Rétroporté de Mydas, où le
   même défaut avait été payé. */
table.liste thead th { position: sticky; top: var(--bandeau); z-index: 10; }
/* `.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 : « Patek Philippe · Nautilus · 5711/1A »
   tenait la colonne « Objet » à 499 px, et seize écrans faisaient défiler la
   page entière de côté, leurs 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 est
     UN mot pour le navigateur. Sans lui, la colonne « E-mail » revenait à la
     ligne nulle part et poussait encore la page de seize pixels. */
  white-space: normal; overflow-wrap: anywhere;
}

/* Le même filet ouvre la carte de chaque tableau, au-dessus de la ligne de
   titres — jamais derrière une vignette d'étiquette ni une cellule de couleur,
   ce que la charte protège pour de bonnes raisons : une vignette d'étiquette
   se juge sur fond neutre, sans filet qui la traverse.

   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:hover { background: 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 une liste
   de 50 lignes) : ~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
   baccus.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: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 d'étiquettes, 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: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. */
/* La pagination ne pousse JAMAIS la page : elle passe a la ligne. La fenetre
   glissante borne deja le nombre de liens (partials/pagination.html), et ce
   retour a la ligne est la seconde ceinture — un libelle plus long dans une
   autre langue, un ecran plus etroit, et la barre de defilement horizontale
   reviendrait sans que personne l ait decide. */
.pagination { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 10px 12px; color: var(--encre-attenuee); }
.pagination-ellipse { padding: 2px 2px; color: var(--encre-attenuee); }
.pagination-total { margin-left: auto; font-size: 12px; }
.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 var(--duree-court) var(--entree),
              border-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-survol); border-color: var(--primaire-survol); 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-survol); }
.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; }
.btn:disabled { opacity: .45; cursor: not-allowed; }
.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 input, .champ select, .champ textarea {
  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 { border-color: var(--primaire); }

/* 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 ; sur Chrome elle devenait un rectangle de
   13 × 32 px, décalé de sept pixels sous le mot qu'elle étiquette.
   Rétroportage du 31.08.2026 — le défaut a été constaté sur Mydas, à l'écran
   « Nouveau fournisseur », et Baccus le portait au même endroit ainsi que sur
   « Rôles » du tiers et sur les SOIXANTE cases « rattacher » d'une fiche de
   lot. Six gabarits portaient déjà `style="width:auto;height:auto"` en ligne :
   c'est cette rustine recopiée qui rendait l'oubli invisible ailleurs. 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; }
.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); }
/* Le plafond de piste n'est pas cosmétique : sans lui, `1fr` étire chaque
   champ à toute la largeur disponible, et sur un grand écran on saisit une
   date dans une case de 560 px. Même forme que Mydas. */
/* `grid-auto-columns: 0` : dans une carte étroite — la colonne de droite
   d'une fiche, 331 px — `auto-fill` ne pose qu'UNE piste, et un champ qui en
   réclame deux (l'idiome que la règle CSS-1 de la recette interdit, encore
   présent dans une vingtaine de gabarits) en faisait naître une seconde. Cette
   piste implicite naissait en `auto`, à la largeur de son contenu, et sortait
   de la carte puis de l'écran : la fiche d'une fourniture poussait la page de
   45 px. Bornée à zéro, la demande d'une piste de plus se ramène à celle qui
   existe. La règle est inerte partout où la carte en tient déjà deux. */
.grille-champs {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 360px));
  grid-auto-columns: 0; gap: 0 16px;
}

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

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

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

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

/* ======================= Onglets ========================================= */
/* `flex-wrap` : les onglets des réglages sont neuf, et la dernière paire
   — « Éléments du full set », « Canaux de publication » — sortait de la
   fenêtre en poussant la page de 157 px de côté. Une barre d'onglets qui
   ne tient pas se replie ; elle ne déplace pas l'écran. */
.onglets { display: flex; flex-wrap: wrap; gap: 2px; border-bottom: 1px solid var(--bordure); margin-bottom: 0; }
.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: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 ================================= */
/* Voiles et ombres : du NOIR NEUTRE, jamais la couleur de marque.
   Ils portaient `rgba(19, 17, 16, …)` et `rgba(26, 29, 27, …)` — le noir
   de l'identité du 25.08 et l'encre verdâtre d'avant, recopiés à la main.
   Une ombre n'a pas de teinte : elle est la lumière en moins. Lui en donner
   une la fait dériver à chaque changement de charte, sans que personne ne
   pense à la suivre — c'est exactement ce qui s'est produit deux fois. */
.panneau-fond {
  position: fixed; inset: 0; background: rgba(0, 0, 0, .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; } }

/* ======================= Dialogue ======================================== */
.dialogue-fond {
  position: fixed; inset: 0; background: rgba(0, 0, 0, .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 au tableau de bord (24.09.2026, transposé de
   Mydas) : une carte, jamais une fenêtre modale qu'on ferme sans la lire. */
.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 : son nom, ce qu'il fait, et la flèche de
   son tutoriel. 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; }
/* La flèche du tutoriel, repère discret mais cible franche. Au doigt, 44 px de
   haut comme toute cible de terrain ; à 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 BORDEAUX cadré d'un double filet doré. Connexion et splash
   uniquement : jamais une surface de travail, jamais près d'une bouteille. */
.moment {
  background: var(--bordeaux-profond);
  color: #FFFFFF;
  position: relative;
}
.moment .doux { color: var(--sur-bordeaux); }
/* 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 .15s ease; }
.htmx-request .htmx-indicator, .htmx-request.htmx-indicator { opacity: 1; }
/* La roulette d'attente. Elle s'appelait `.indicateur` — un nom que les
   gabarits ont pris pour « le chiffre d'un indicateur », et vingt-six valeurs
   de KPI l'ont employé : le tableau de bord, les inventaires et tous les
   rapports affichaient leurs montants dans un cercle de 14 px, en rotation
   perpétuelle, le texte replié à un caractère par ligne par-dessus son propre
   libellé. Le nom est donc devenu impossible à confondre. Trouvé sur un
   enregistrement d'usage le 25.08.2026 ; un test tient désormais la règle. */
.roulette {
  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); } }
@media (prefers-reduced-motion: reduce) {
  .roulette { animation: none; border-top-color: var(--encre-attenuee); }
}

/* --- 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;
}
/* La bordure seule ne suffit pas : elle passe de gris à doré, un écart de
   teinte que beaucoup d'yeux ne lisent pas. L'anneau, si. */
.selecteur-saisie:focus-visible {
  border-color: var(--primaire);
  outline: 2px solid var(--primaire); outline-offset: 1px;
}
.selecteur-saisie:focus { border-color: var(--primaire); }
.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; }
/* Survol et focus étaient rendus À L'IDENTIQUE, et l'anneau supprimé. Un
   vendeur qui parcourait vingt clients au clavier n'avait pour repère qu'un
   fond beige à 1,19:1 — indiscernable du blanc sur un écran de bureau bien
   éclairé. Il validait au jugé, et rattachait la vente au mauvais client.
   L'`outline-offset` est NÉGATIF : `.selecteur-liste` défile en `overflow-y`,
   et un anneau posé à l'extérieur y serait rogné. */
.selecteur-option:hover { background: var(--primaire-tint); }
.selecteur-option:focus-visible {
  background: var(--primaire-tint);
  outline: 2px solid var(--primaire); outline-offset: -2px;
}
.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 dépôt, la fiche bouteille 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; }
/* `flex-wrap` : la ligne porte un nom de fichier, un horodatage avec son
   auteur (`nowrap`, et c'est voulu) et un bouton. Sur une fiche de bouteille au
   nom long, les trois ne tenaient pas et la ligne poussait la page 97 px hors
   de la fenêtre. Elle se replie maintenant plutôt que de déplacer l'écran. */
.documents-liste li {
  display: flex; flex-wrap: wrap; 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 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, poussait la page 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 (tableau de bord, rapports, inventaires, fiche tiers).
   `minmax(170px, 280px)` et non `1fr` : sur un écran large, `1fr` étalait six
   cartes à 586 px chacune pour y loger un nombre de six caractères. Le regard
   traversait un demi-mètre entre deux chiffres qu'il faut comparer. */
.indicateurs {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 280px));
  gap: 12px; margin-bottom: 16px;
}
/* En COLONNE, et ce n'est pas cosmétique : le libellé et la valeur sont deux
   `<span>`, donc en ligne par défaut. Ils s'affichaient côte à côte, et le
   `margin-top` de la valeur — écrit pour un bloc — n'y pouvait rien. */
.indicateur-carte {
  padding: 14px 16px;
  display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
}
.indicateur-carte .libelle {
  font-size: 12px; font-weight: 500; letter-spacing: .1em;
  text-transform: uppercase; color: var(--encre-attenuee);
}
.indicateur-carte .valeur {
  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 bordeaux 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. */
/* La piste est BORNÉE, et pas `1fr`. `auto-fit` sans plafond étale ce qu'on lui
   donne sur toute la largeur disponible : à 1680 px de zone utile, un compteur
   à deux chiffres se retrouvait au centre d'une case de 560 px — et de 1646 px
   quand la bande n'en portait qu'un seul. C'est le défaut déjà corrigé sur
   `.indicateurs`, et c'est le même correctif. `justify-content: start` garde
   les mesures groupées à gauche : elles se lisent de gauche à droite, pas
   comme une rangée d'icônes centrée. */
.index-chiffres {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 340px));
  justify-content: start;
  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); }
@media (max-width: 900px) {
  .index-chiffres .mesure { border-bottom: 1px solid var(--bordure); }
  .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); }

/* ======================= É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 BORDEAUX, double filet doré,
   emblème au centre. C'est le seul écran de travail qui a le droit d'être
   sombre — aucune bouteille n'y est jugée. */
body.connexion { display: block; background: var(--bordeaux-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; }
.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 {
  /* h1 depuis le 04.09.2026 : la page n'en avait aucun. La marge basse du
     navigateur est neutralisee pour que le rang change sans que la mise en
     page bouge d'un pixel. */
  margin: 20px 0 0; 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 bouteille 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-reference { 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); }
.mono-espace { font-variant-numeric: tabular-nums; }
/* Aucun bloc préformaté ne pousse la page : ce qui dépasse défile DANS le
   bloc. Le détail d'un chiffrage aligne ses montants sur des points de
   conduite — une seule « mot » pour le navigateur, que `pre-wrap` ne sait pas
   couper — et quatre fiches (bouteille, devis, événement, contenant)
   débordaient de 44 px chacune. Règle qui balaye : le bloc écrit demain entre
   dans le périmètre sans que personne y pense. */
pre { max-width: 100%; overflow-x: auto; overflow-wrap: anywhere; }

.separateur-v { width: 1px; align-self: stretch; background: var(--bordure); }
.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 baccus.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 (baccus.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);
  }

}

/* 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é, 25.08.2026) =============== */
/* Motif kintsugi BORDEAUX ET OR — docs/MOTIF-VEINE-DOR.md fait autorité.
   Règles inchangées : un geste par écran ; jamais sur une surface où l'on juge
   une bouteille ; jamais de bord d'image visible ; matière statique ; rien de
   simulé en code.

   Ce qui change le 25.08.2026 : les masters sont RECOLORÉS, pas redessinés.

   Le motif d'origine est un marbre pâle veiné de feuille d'or, avec une tache
   turquoise — couleur d'une identité verte qui n'est plus la nôtre. Un premier
   essai avait remplacé les images par un tracé vectoriel : rejeté, et à raison.
   Une fêlure dessinée à la main ne ressemble pas à une fêlure ; la valeur du
   motif est dans la photographie de la feuille d'or, son modelé et ses
   accidents. On garde donc les masters et l'on change leurs COULEURS
   (`scripts/recolorer_motif.py`, deux opérations, aucune retouche du dessin).

   DEUX JEUX, et il faut les deux :

   - `or_*`   — le fond clair d'origine, turquoise recoloré en or. Pour tout ce
                qui reste sur blanc : zone de travail, états vides, barre
                latérale, hero de la vitrine.
   - `bordeaux_*` — le marbre basculé au bordeaux du vin, la dorure allumée.
                    Pour les écrans « moments » et les sections sombres.

   Choisir le mauvais jeu se voit immédiatement : le fond clair posé sur du
   bordeaux donne un rectangle blanc, et l'inverse un rectangle sombre. C'est
   le « bord d'image visible » que la charte interdit depuis le premier jour. */

/* Écrans publics « moments » (connexion, première connexion, changement de
   mot de passe, passeport introuvable) : le marbre BORDEAUX, veiné d'or. La
   carte blanche se pose sur la zone calme du centre-haut. */
body.connexion {
  background: var(--bordeaux-profond) url("/static/img/motif/bordeaux_fond-1920.webp")
              center / cover no-repeat;
}
@media (max-width: 1280px) {
  body.connexion { background-image: url("/static/img/motif/bordeaux_fond-1280.webp"); }
}
body.connexion .connexion-filigrane { display: none; }
body.connexion .cadre-or::before,
body.connexion .cadre-or::after { display: none; }
/* L'ombre est plus dense qu'à l'époque du fond clair : sur du blanc une carte
   blanche se détachait d'un rien ; sur du bordeaux, sans ombre, elle paraît
   découpée au cutter. */
body.connexion .connexion-carte { box-shadow: 0 18px 50px rgba(0, 0, 0, .55); }

/* Coin fondu — l'angle doré qui entre par le bas-gauche d'une zone « moment ».
   Jamais derrière du texte : le contenu passe au-dessus. `multiply` fond le
   papier du master dans le fond clair — c'est ce qui évite le bord visible. */
.vd-coin {
  position: absolute; left: 0; bottom: 0; width: 46%; height: 80%;
  background: url("/static/img/motif/or_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ù Baccus parle de lui. */
.vd-veine {
  height: 18px; width: 150px;
  background: url("/static/img/motif/or_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 « centrée » — pied du passeport public. Le fondu part des deux
   bords : la veine FERME la page au lieu de pointer vers la droite. */
.vd-veine--centree {
  width: 168px; height: 20px;
  background-size: 380px auto;
  background-position: 6px center;
  margin: 26px auto 14px;
  filter: saturate(1.6) contrast(1.15);
  -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 « prefers-color-scheme » ici : Baccus est en thème clair,
   délibérément — un fond sombre fausse l'appréciation de la robe d'un spiritueux.
   Le bordeaux de l'identité est celui des « moments » et des aplats de marque,
   jamais celui de la zone de travail. Une règle « dark » se déclencherait chez
   qui a son système en sombre et repeindrait une interface restée claire. */

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

   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.

   Les deux emploient le jeu `or_` : la zone de travail reste CLAIRE, et y
   poser le jeu noir reviendrait à peindre l'écran de travail en sombre — ce
   que l'argument métier interdit.

   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, .82), rgba(251, 250, 248, .82)),
    url("/static/img/motif/or_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));
}
/* Le voile est passé de 62 % à 82 % de blanc le 25.08.2026, et le motif de la
   décision du 21.08 tient toujours — c'est son DOSAGE qui était réglé pour un
   écran de bureau ordinaire. En `cover` sur 3836 px, l'image de 1920 px est
   agrandie deux fois : sa bande dorée, prévue comme un pied, occupait alors la
   moitié basse de la fenêtre, et le carré-repère du master traversait la zone
   de travail. À 82 % le motif redevient ce qu'il devait être — une profondeur
   qu'on devine dans les gouttières, jamais une image qu'on regarde. */

/* 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 que Baccus vend à un négociant. */
.contenu .carte,
.contenu .fiche-section,
.contenu .bloc-pliable {
  background-color: var(--surface);
}
.contenu .carte { box-shadow: 0 1px 3px rgba(0, 0, 0, .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 {
  background-image:
    linear-gradient(rgba(255, 255, 255, .86), rgba(255, 255, 255, .86)),
    url("/static/img/motif/or_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.

   Ce qui rend le coin juste, et que les filets retirés 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.

   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/or_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.
   Et il est BORNÉ : voir le commentaire de `.contenu`. La marge automatique
   centre la colonne de lecture au lieu de la coller à gauche — sur un écran
   large, un contenu aligné à gauche laisse un désert à droite que l'œil lit
   comme une zone qui n'a pas fini de charger. */
.contenu > * {
  position: relative; z-index: 1;
  max-width: var(--contenu-max);
  margin-inline: auto;
}

/* La colonne de navigation reçoit le motif vertical. C'est la zone la plus
   sûre de l'écran : aucune bouteille 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/or_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/or_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 Baccus — la toute première bouteille / 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(0, 0, 0, .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(0, 0, 0, .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:hover { background: 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 voit 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 n'affiche
   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-depot` et non `pilotage-garde` : la page de garde de la console a
   été renommée par le balayage mécanique du vocabulaire, dans le gabarit
   seulement. La feuille est restée sur l'ancien nom pendant deux commits — et
   le bloc s'affichait donc entièrement sans style, ce que rien ne signalait
   sinon `tests/test_css_vivant.py`. Les noms se suivent, ou le style tombe. */
.pilotage-depot {
  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-depot-icone {
  width: 40px; height: 40px; display: grid; place-items: center;
  /* La bordure était #C9DDD0 — un vert pâle en dur, dernier survivant de
     l'ancienne identité dans la feuille. Elle suit désormais le jeu de
     variables, comme tout le reste. */
  color: var(--primaire); background: var(--primaire-tint); border: 1px solid var(--bordure);
  border-radius: var(--rayon);
}
.pilotage-depot-icone .icone { width: 21px; height: 21px; }
.pilotage-depot h1 { margin: 0 0 3px; font-size: 1.28rem; }
.pilotage-depot 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 {
  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: 1040px; 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: 20%; }
.pilotage-tableau table.liste th:nth-child(2) { width: 19%; }
.pilotage-tableau table.liste th:nth-child(3) { width: 12%; }
.pilotage-tableau table.liste th:nth-child(4) { width: 15%; }
.pilotage-tableau table.liste th:nth-child(5) { width: 17%; }
.pilotage-tableau table.liste th:nth-child(6) { width: 17%; }
.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; }
.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-depot { grid-template-columns: 40px minmax(0, 1fr); }
  .pilotage-registre { grid-column: 2; align-items: flex-start; }
  .pilotage-indicateurs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .pilotage-indicateur { border-left: 0; border-top: 1px solid var(--bordure); }
  .pilotage-indicateur:nth-child(1), .pilotage-indicateur:nth-child(2) { border-top: 0; }
  .pilotage-indicateur:nth-child(even) { border-left: 1px solid var(--bordure); }
  .pilotage-indicateur:last-child { grid-column: 1 / -1; }
  .pilotage-section-entete { align-items: stretch; flex-direction: column; }
  .pilotage-filtres { justify-content: flex-start; }
  .pilotage-filtres input { width: 100%; }
  .pilotage-filtres select { flex: 1; min-width: 150px; }
}

/* 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; }

/* ======================= 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; }

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

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

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

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

/* --- 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; }
}

/* Deux boutons de langue de plus dans le bandeau : à 1024 px, « Umsatz und Marge pro Kunde » dans le fil
   d'Ariane poussait « Abmelden » hors du cadre (planche allemande du 25.09.2026). Le fil se coupe avec des
   points à toute largeur — et seulement quand la place manque. */
.fil-ariane { min-width: 0; }
.fil-ariane .courant { min-width: 0; overflow: hidden; text-overflow: ellipsis; }

/* 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; }
/* Un lien de ligne sans valeur ne porte qu'un « — » : il reste une cible de doigt. */
.lien-ligne { display: inline-block; min-width: 24px; }
/* Un lien court dans une phrase ne se coupe pas en deux : « Set it » sur deux lignes faisait une cible de 20 px. */
.sans-coupure { white-space: nowrap; }

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

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

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

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

/* === Console de l'éditeur : caisse, sièges, fiche client, incidents, import ===
   Reprises de Deimos (lot 4 du chantier « console superadmin dans toute la gamme »). */
  display: grid; grid-template-columns: 40px minmax(0, 1fr) auto; align-items: center;
  gap: 16px; padding: 20px 0; border-bottom: 1px solid var(--bordure);
}
  width: 40px; height: 40px; display: grid; place-items: center;
  color: var(--primaire); background: var(--primaire-tint); border: 1px solid var(--bordure);
  border-radius: var(--rayon);
}
.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; }
.pilotage-anomalies { margin: 0; padding: 0; list-style: none; }
.pilotage-anomalie {
  padding: 2px 0 2px 9px; border-left: 2px solid var(--bordure);
  color: var(--encre-douce); font-size: 12px; line-height: 1.35;
}
.pilotage-anomalie + .pilotage-anomalie { margin-top: 5px; }
.pilotage-anomalie--danger { border-left-color: var(--danger); color: var(--danger); }
.pilotage-anomalie--avert { border-left-color: var(--avert); color: var(--encre); }
.pilotage-anomalie--info { border-left-color: var(--bordure); }
.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; }
.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; }
.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);
}
.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-sieges { margin: -8px 0 16px; color: var(--encre-douce); font-size: 13px; }
.jauge-sieges--pleine { color: var(--avert); }
.pilotage-sieges { display: flex; align-items: center; gap: 6px; margin: 0; }
.pilotage-sieges input { width: 64px; padding: 4px 8px; border: 1px solid var(--bordure); border-radius: var(--rayon); font: inherit; font-variant-numeric: tabular-nums; }
.pilotage-sieges input:focus { border-color: var(--primaire); }
.pilotage-sieges .btn { height: 28px; padding: 0 8px; font-size: 12px; }
@media (max-width: 760px) {
  .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; }
}

/* Le nom mène à la fiche : 24 px au doigt, comme toute cible (WCAG 2.5.8). */
a.pilotage-client-nom { display: inline-block; min-height: 24px; line-height: 24px; text-decoration: none; }
a.pilotage-client-nom:hover { text-decoration: underline; }
/* « Voir le rapprochement » est une cible comme une autre : 24 px au doigt. */
.pilotage-lien-caisse { display: inline-block; min-height: 24px; line-height: 24px; }
/* 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é : la page
   entière s'élargit au téléphone (mesuré sur Mydas le 27.09.2026). */
.pilotage-tableau .tableau-defilant { position: relative; }
