/* ============================================================
   Cockpit Husse Vendée : design system maquette
   DA : crème, cartes blanches arrondies, rouge Husse, patte
   ============================================================ */

/* ------------------------------------------------------------
   Police : Plus Jakarta Sans, servie PAR NOUS.
   🔴 08/09/2026 (Julien : « optimiser la vitesse de chargement, surtout sur
   mobile »). Jusqu'ici la police venait de Google Fonts : à CHAQUE page, le
   navigateur devait d'abord aller chercher une feuille de style chez
   fonts.googleapis.com (bloquante pour le rendu, et que Google ne laisse
   garder qu'un jour), puis les fichiers chez fonts.gstatic.com : deux
   domaines de plus à joindre en 4G avant de pouvoir peindre quoi que ce
   soit, et rien de tout ça n'entrait dans le cache du service worker
   (il ne touche pas au cross-origin, voir sw.js). Désormais la police est
   un asset comme un autre : deux fichiers (police VARIABLE, un seul fichier
   couvre les graisses 500 à 800), 49 ko en tout, mis en cache par le service
   worker dès l'installation, et jamais redemandés. Fichiers pris tels
   quels chez Google (licence OFL), mêmes plages unicode que les leurs :
   « latin » couvre le français courant, « latin-ext » le œ et consorts.
   ------------------------------------------------------------ */
@font-face {
  font-family: 'Plus Jakarta Sans';
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url(fonts/plus-jakarta-sans-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Plus Jakarta Sans';
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url(fonts/plus-jakarta-sans-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  --cream: #F8F4EF;
  --cream-deep: #F3EDE6;
  --card: #FFFFFF;
  --ink: #17150F;
  /* 11/09 (brief Astra B10) : --muted passait à 3,75:1 sur blanc, sous le
     seuil de 4,5:1 du texte courant ; il porte des libellés utiles (« Produits
     en stock », villes, dates). 5,5:1 sur blanc, 4,7:1 sur crème foncé. */
  --muted: #6F685E;
  /* --soft reste la teinte DÉCORATIVE (icônes, séparateurs, pointillés) ;
     un texte indicatif à lire (placeholder, « à choisir ») prend --soft-txt */
  --soft: #B4ADA2;
  --soft-txt: #7A7368;
  /* le texte orange (badges « en attente », titres de section) : var(--orange-txt)
     faisait 2,9:1 sur son fond clair, #B45309 fait 4,5:1 */
  --orange-txt: #B45309;
  --red: #E30613;
  --red-dark: #C00511;
  --red-soft: #FDECEB;
  --red-softer: #FBF1EE;
  --green: #23A55A;
  --green-soft: #E7F6EE;
  --orange: #EE8F1F;
  --orange-soft: #FDF2E1;
  --border: #EFE7DD;
  /* bulle d'un SMS reçu (Julien 27/08 : le blanc ressortait trop, il veut du jaune) */
  --sms-bg: #FCF4D2;
  /* texte d'un SMS reçu : rouge doux, assez foncé pour rester lisible sur les
     5 fonds de section (essai demandé par Julien le 27/08) */
  --sms-txt: #C00511;
  --sms-bd: rgba(190, 155, 45, .28);
  --shadow: 0 1px 2px rgba(23, 21, 15, .04), 0 10px 30px rgba(23, 21, 15, .06);
  --shadow-soft: 0 1px 2px rgba(23, 21, 15, .03), 0 6px 18px rgba(23, 21, 15, .04);
  --radius: 18px;
  /* 11/09 (brief Astra B11) : l'échelle commune des composants. Quatre rayons,
     deux hauteurs de commande : un filtre, un bouton, une recherche de même
     rang suivent la même règle, sur toutes les pages. */
  --r-sm: 9px; --r: 11px; --r-lg: 18px; --r-pill: 999px;
  --ctrl: 44px; --ctrl-lg: 48px;
  --sidebar-w: 264px;
}

* { margin: 0; padding: 0; box-sizing: border-box; }
/* 🔴 01/09/2026 (Julien) : « la période choisie n'apparaît que quand on clique
   sur Période choisie ». Elle s'affichait tout le temps, et ce n'était PAS un
   oubli de code : l'attribut « hidden » ne vaut qu'un « display: none » de la
   feuille du navigateur, que N'IMPORTE QUELLE règle de classe posant un
   display écrase. Recensé sur les 8 pages : SEPT éléments étaient dans ce cas,
   pas seulement les dates (les trois barres de pagination, le bouton
   « Modifier le tir », la pastille « Depuis … », une étiquette de filtre).
   Cette règle-ci rend à « hidden » le sens qu'il annonce, partout et une bonne
   fois. Vérifié avant de la poser : dans tout app.js, « hidden » ne sert jamais
   qu'à MASQUER, jamais à afficher. */
[hidden] { display: none !important; }

html {
  overscroll-behavior: none;
  overflow-x: hidden;
  scrollbar-gutter: stable;
  overflow-y: scroll;
  scroll-padding-top: 80px;
}
body { overflow-x: clip; }

body {
  font-family: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  background: var(--cream);
  color: var(--ink);
  font-size: 15.5px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
  min-height: 100dvh;
}

img { max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font-family: inherit; cursor: pointer; border: none; background: none; }

/* ---------- Layout ---------- */

.app { display: flex; min-height: 100dvh; }

.main {
  flex: 1;
  min-width: 0;
  padding: 34px 40px 56px;
  margin-left: var(--sidebar-w);
  /* 🔴 01/09 (Julien) : « Cockpit © 2026 doit toujours être au plus bas, pas
     remonté en fonction des blocs ». Sur une page courte (Tirs sans tir, Stock
     filtré), le pied suivait le dernier bloc et flottait au milieu du beige.
     Colonne flexible + marge élastique sur le pied : il descend au bas de
     l'écran quand le contenu ne le remplit pas, et reste sous le contenu quand
     la page est longue. */
  display: flex; flex-direction: column;
}
/* 🔴 01/09, MA RÉGRESSION, corrigée le jour même (Julien : « les largeurs des
   pages ne sont pas les mêmes, je veux la même largeur que la page Facture »).
   Rendre .container flexible pour coller le pied de page en a fait un ITEM de
   flex ; or son « margin: 0 auto » est une marge AUTOMATIQUE sur l'axe
   transversal, ce qui annule l'étirement par défaut et rend l'élément large
   comme son contenu. Mesuré aussitôt : 1156 px sur Factures, mais 961 sur
   l'Accueil, 870 sur Tirs, 624 sur Stock, 604 sur Robots. Le « width: 100% »
   rend la pleine largeur ; le « margin: 0 auto » continue de centrer sous le
   plafond de 1560 px. */
.main > .container { flex: 1 0 auto; display: flex; flex-direction: column; width: 100%; }
.app-footer { margin-top: auto; }

/* 1420px (26/08/2026, demande Julien) : les lignes du Récap du jour
   (« Gael CLEMENT DEVINEAU · Noirmoutier en L Ile · jeudi ») tenaient pas
   sur une ligne à 1180px alors que le fond beige a de la marge.
   1560px (29/08/2026) : la colonne Vendeur serrait le tableau des factures.
   Julien : « si besoin élargis l'ensemble, on a de la marge à gauche et à
   droite, mais harmonise sur toutes les sections ». Cette règle-ci est la
   SEULE largeur de page du cockpit : toutes les pages suivent d'un coup.
   Sur un écran 1920, 264 (menu) + 80 (marges) + 1560 = 1904 : la place est
   prise sans déborder. En dessous, la page était déjà plus étroite que ce
   plafond, rien ne bouge. */
.container { max-width: 1560px; margin: 0 auto; }

/* ---------- Sidebar ---------- */

.sidebar {
  position: fixed;
  inset: 0 auto 0 0;
  width: var(--sidebar-w);
  background: var(--card);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  padding: 26px 18px 20px;
  z-index: 40;
}

.brand {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 0 10px 24px;
}
.brand img { width: 34px; height: 34px; object-fit: contain; }
.brand span {
  font-size: 25px;
  font-weight: 800;
  color: var(--red);
  letter-spacing: -.5px;
}

.nav { display: flex; flex-direction: column; gap: 4px; }

.nav a {
  display: flex;
  align-items: center;
  gap: 13px;
  padding: 11.5px 14px;
  border-radius: 13px;
  font-weight: 600;
  font-size: 15.5px;
  color: var(--ink);
}
.nav a img { width: 21px; height: 21px; object-fit: contain; }
.nav a .ic-on { display: none; }
.nav a:hover { background: var(--cream); }
.nav a.active { background: var(--red); color: #fff; }
.nav a.active:hover { background: var(--red); }
.nav a.active .ic-off { display: none; }
.nav a.active .ic-on { display: block; }

/* « Signaler un souci » : dans la colonne de gauche, donc présent sur les 8
   pages (Julien 27/08), et bordé de rouge pour ne pas se confondre avec les
   entrées de menu. Il tient dans la colonne sans l'élargir. */
/* 🔴 28/08 (Julien) : « pas très au centre ». Le groupe icône + texte était
   pourtant centré au pixel près (mesuré) : l'œil, lui, centre le TEXTE, que
   l'icône poussait vers la droite. On compense donc optiquement par le padding
   droit, élargi de (largeur d'icône + gap) : le groupe glisse d'une demi-valeur
   vers la gauche et le libellé tombe au centre du bouton.
   ⚠️ Ne PAS sortir l'icône du flux ici : le bloc d'alignement en fin de fichier
   lui impose « position: relative » (harnais test-align-icones), un absolute
   serait écrasé. */
.nav-souci {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  width: 100%; box-sizing: border-box;
  margin: 14px 0 0; padding: 7.4px 40px 10.6px 12px;   /* remonte le texte de 1,6 px */
  /* ⚠️ NE PAS « corriger » ce 7,4/10,6 : il centre le TEXTE dans le cadre, et
     c'est la valeur validée par Julien le 28/08 (harnais test-centrage-pastille).
     Mesuré autrement, le groupe icône+texte tombe 1,75 px bas, parce que la
     règle d'alignement optique descend l'icône de 3,9 px sous sa boîte. Les
     deux mesures sont justes mais ne visent pas la même chose : on garde
     celle que Julien a tranchée. */
  border: 1.5px solid var(--red); border-radius: 12px;
  background: transparent; color: var(--red);
  font: inherit; font-size: 14px; font-weight: 700; line-height: 1.2;
  cursor: pointer;
}
.nav-souci img { width: 18px; height: 18px; object-fit: contain; flex: 0 0 auto; }
.nav-souci:hover { background: var(--red-soft); }

.sidebar-mascotte {
  margin: auto 6px 10px;
  text-align: center;
}
.sidebar-mascotte img { width: 160px; height: auto; }

.user-chip {
  display: flex;
  align-items: center;
  gap: 11px;
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 10px 12px;
  margin: 0 6px;
}
.user-chip .avatar {
  width: 38px; height: 38px;
  border-radius: 50%;
  background: var(--red);
  color: #fff;
  font-weight: 800;
  font-size: 16px;
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}
.user-chip b { display: block; font-size: 14.5px; line-height: 1.2; }
.user-chip small { color: var(--muted); font-size: 12.5px; }

/* ---------- En-tête de page ---------- */

.page-head {
  display: flex;
  /* 🔴 30/08 (Julien) : le bouton d'action d'une page (Nouveau client, État de
     TVA, Ajouter / recharger un produit) se cale sur le BAS de l'en-tête, donc
     juste au-dessus des blocs de contenu, et non en haut à côté du titre.
     flex-end suffit : le bloc titre + pastille est toujours le plus haut des
     deux, c'est lui qui donne la ligne de base. */
  align-items: flex-end;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 16px;
  /* 27/08 : la pastille de date a besoin d'air des deux côtés (Julien) */
  margin-bottom: 36px;
}
.page-head h1 {
  font-size: 34px;
  font-weight: 800;
  letter-spacing: -1px;
  line-height: 1.15;
}
/* Sous-titre de page en PASTILLE (demande Julien 27/08 : « la date c'est un
   élément important, plus gros, fond blanc texte noir, que ça ressorte »).
   Le texte vit dans .sub-txt : le JS le réécrit sans effacer l'icône. */
.page-head .sub {
  display: inline-flex; align-items: center; gap: 9px;
  margin-top: 16px; padding: 7.4px 15px 8.6px;   /* encre centrée : elle tombait 1,2 px trop bas */
  background: var(--card); border: 1px solid var(--border); border-radius: 18px;
  color: var(--ink); font-size: 16.5px; font-weight: 700;
  box-shadow: var(--shadow-soft);
}
.page-head .sub img { width: 20px; height: 20px; object-fit: contain; flex: 0 0 auto; }
.page-head .sub .sub-txt { min-width: 0; }
/* Une phrase sur deux lignes : l'icône se cale sur la PREMIÈRE ligne, ce que
   fait déjà l'alignement sur la baseline (règle générale en fin de feuille).
   L'ancien margin-top de 2 px s'ajoutait à ce calage et la faisait redescendre. */
/* 27/08 (Julien) : l'icône plaquée à gauche de la pastille, un espace, puis le
   texte aligné à gauche : sur deux lignes, les débuts de ligne se répondent. */
.page-head .sub { justify-content: flex-start; text-align: left; }
.page-head .sub .sub-txt { text-align: left; }
/* Sur DEUX lignes, le bas de l'encre n'est plus l'icône (calée sur la 1re
   ligne) mais la baseline de la seconde : le blanc bascule de 3,98 px dans
   l'autre sens, soit exactement le décalage optique de l'icône. On rattrape
   uniquement là où la coupe est active, donc au-dessus de 600 px : sous cette
   largeur le <br> est neutralisé et la pastille redevient un cas ordinaire. */
@media (min-width: 601px) {
  .page-head .sub:has(br) { padding-top: 9.4px; padding-bottom: 6.6px; }
}
/* le « Retour aux clients » de la fiche : même pastille, mais cliquable */
.page-head .sub .back-link { color: var(--ink); font-weight: 700; }
.page-head .sub:has(.back-link):hover { border-color: var(--red); }
.page-head .sub:has(.back-link):hover .back-link { color: var(--red); }
@media (max-width: 600px) {
  .page-head .sub { font-size: 15.5px; padding: 7px 13px; margin-top: 13px; }
  .page-head { margin-bottom: 28px; }
  /* Doctrine JBC : une coupe calée desktop explose en petite largeur et laisse
     un mot orphelin. Sous 600px on rend la main au navigateur ; les groupes
     insécables (&nbsp;) tiennent les fins de phrase.
     🔴 01/09 (Julien : « d'achatet = c'est collé, attention ») : masquer un
     <br> supprime le retour ET l'espace qu'il portait. Les QUATRE sous-titres
     du cockpit étaient collés (« d'achatet », « soir,rien », « envoiechaque »,
     « anticiper,d'un »), seul Stock avait été repéré. Règle à tenir :
     🔴 TOUT <br> de sous-titre s'écrit avec une ESPACE devant (« …d'achat <br>et… »).
     Elle est invisible quand le retour s'affiche (elle se replie en fin de
     ligne) et c'est elle qui sépare les deux mots quand il est masqué. */
  .page-head .sub br { display: none; }
  .page-head .sub.haut { align-items: center; }
}
/* Sous 360px (iPhone SE) la pastille mange trop de largeur et les phrases
   tombent à 2 mots par ligne : on resserre le cadre plutôt que le texte. */
@media (max-width: 360px) {
  .page-head .sub { font-size: 14.5px; padding: 6px 11px; gap: 7px; }
  .page-head .sub img { width: 17px; height: 17px; }
}

.head-tools { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }

/* 🔴 01/09 (Julien, captures iPhone) : « sur mobile, ça serait bien que le
   bouton État de TVA, la petite cloche ou Recharger soient au niveau du titre.
   Et la proposition (le Préparateur te propose…, le Caissier les génère…), je
   pense qu'on peut l'enlever, ce sera plus léger sur mobile. »
   Sur un téléphone, l'en-tête mangeait le premier écran : le titre, puis une
   pastille de trois lignes, puis le bouton d'action tout seul sur sa ligne,
   soit près de 240 px avant le premier chiffre. Le bouton remonte à côté du
   titre (la place y est libre, un titre de page ne fait jamais la largeur), et
   la phrase de présentation disparaît : elle explique le rôle d'une page qu'on
   ouvre dix fois par jour.
   Ce qui RESTE sous le titre : la pastille « à jour 14h37 », qui n'est pas de
   la décoration (elle dit de quand datent les chiffres et recharge au clic).
   Le sous-titre de l'ACCUEIL (la date du jour) et celui de la fiche client
   (« Retour aux clients ») ne portent pas .sub-deco : eux restent entiers.
   L'en-tête passe en grille pour ça : le bloc titre est neutralisé
   (display:contents) et ses deux enfants se placent eux-mêmes, sans qu'on ait
   à retoucher le HTML des huit pages. */
/* 🔴 01/09 (Julien) : « sur Stats, le titre est trop gros, faut que tu trouves
   une solution, peut-être le mettre en bas ». MESURÉ au pixel sur le rendu :
   sur statistiques.html, le bouton « Analyser mes chiffres » CHEVAUCHE le mot
   « Statistiques » à 320, 360, 390 ET 430 px (et sur factures/stock à 320 px).
   Une grille à deux colonnes ne sait pas renoncer : la colonne du titre ne
   descend pas sous son mot le plus long, le bouton reste dans la sienne, et
   les deux se marchent dessus. En rangée FLEX qui accepte de se replier, le
   bouton descend tout seul sur sa propre ligne, à droite, dès qu'il ne tient
   plus à côté du titre : aucune page n'est traitée à part, et un titre ou un
   libellé qui s'allongera un jour se rangera pareil. */
@media (max-width: 860px) {
  .page-head { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 12px; }
  .page-head > div:first-child { display: contents; }
  /* pas de min-width: 0 ici : le titre garde la largeur de son mot, pour que ce
     soit le BOUTON qui se replie et non le mot qui déborde sous lui */
  .page-head h1 { flex: 1 1 auto; }
  .page-head .head-tools { flex: 0 0 auto; margin-left: auto; justify-content: flex-end; }
  .page-head .sub { order: 3; flex: 0 0 100%; justify-self: start; margin-top: 0; }
  /* la phrase de présentation s'efface ; la pastille de fraîcheur reste, nue */
  .page-head .sub.sub-deco {
    background: none; border: 0; box-shadow: none; padding: 0; gap: 0;
  }
  .page-head .sub.sub-deco > img, .page-head .sub.sub-deco > .sub-txt { display: none; }
  /* un sous-titre entièrement vide (page sans pastille de fraîcheur) ne doit
     pas laisser sa gouttière de grille */
  .page-head .sub.sub-deco:not(:has(.fraicheur)) { display: none; }
  /* 01/09 soir (passe mobile) : un bout de sous-titre purement décoratif
     (« · Recherche en direct » sur Clients, l'année sur l'accueil) se replie
     dès qu'il ferait passer le sous-titre sur deux lignes. */
  .sub-deco-txt { display: none; }
}
@media (max-width: 520px) {
  /* 01/09 soir (passe mobile) : sous « Toutes les factures », le champ de
     recherche se retrouvait écrasé à 40 px à côté de « Tout télécharger »
     (une loupe seule, impossible à utiliser). Il prend sa propre ligne. */
  .bh-tools .search { flex: 1 1 100%; max-width: none !important; min-width: 0; }
}

/* 🔴 30/08 (Julien) : « plus bas Nouveau client, au-dessus du bloc principal ».
   Clients intercale une barre de recherche entre son en-tête et son tableau :
   le bouton vit DANS cette barre, poussé à droite, donc à l'aplomb du premier
   bloc comme les boutons des autres pages. */
.barre-outils {
  display: flex; gap: 14px; flex-wrap: wrap; align-items: center; margin-bottom: 20px;
}
.barre-outils .bo-fin { margin-left: auto; }
@media (max-width: 900px) {
  /* le bouton ne se retrouve pas seul et perdu au bout d'une ligne vide :
     sous cette largeur il reprend sa place au fil, collé aux filtres */
  .barre-outils .bo-fin { margin-left: 0; }
}

.bell {
  position: relative;
  width: 44px; height: 44px;
  border-radius: 50%;
  background: var(--card);
  border: 1px solid var(--border);
  display: flex; align-items: center; justify-content: center;
}
/* 🔴 29/08 (Julien, 2e passage : « toujours trop petite la cloche »). La
   cause n'est pas la taille de la boîte, c'est le PNG : le dessin n'occupe que
   80 % de sa largeur et 88 % de sa hauteur, le reste est transparent. À 23 px
   de boîte, la cloche VUE ne faisait que 18 x 20 px dans un rond de 44, soit
   41 % du rond. À 31 px elle en fait 25 x 27, environ 58 % du rond, la même
   proportion que les icônes des cartes. Mesurer le bbox alpha avant de
   retoucher une taille d'icône, ne pas régler à l'oeil. */
.bell img { width: 31px; height: 31px; }
.bell i {
  position: absolute; top: -3px; right: -3px;
  background: var(--red); color: #fff;
  font-style: normal; font-size: 11px; font-weight: 700;
  min-width: 19px; height: 19px; border-radius: 10px;
  display: flex; align-items: center; justify-content: center;
  padding: 0 4px;
  border: 2px solid var(--cream);
}

/* ---------- Cartes & KPI ---------- */

.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-soft);
}

.kpis {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 18px;
  margin-bottom: 22px;
}
.kpis.k3 { grid-template-columns: repeat(3, 1fr); }
.kpis.k2 { grid-template-columns: repeat(2, 1fr); }
/* 07/09 : la page Factures compte 5 cartes depuis la carte de l'exercice */
.kpis.k5 { grid-template-columns: repeat(5, 1fr); }

/* 29/08 (Julien) : « tasse plus ces cartes, trop de blanc » */
.kpi { padding: 19px 22px 17px; }
/* Si la rangée gagne quelques pixels à cause de la carte du tir, l'icône reste
   collée en haut et le nombre en bas : rien ne flotte au milieu (Julien 26/08). */
.kpis > .kpi { display: flex; flex-direction: column; }
.kpis > .kpi .label { margin-top: auto; }
.kpi .chip {
  width: 48px; height: 48px;
  border-radius: 50%;
  background: var(--red-soft);
  display: flex; align-items: center; justify-content: center;
  margin-bottom: 10px;
}
.kpi .chip img { width: 22px; height: 22px; }
.kpi .chip.green { background: var(--green-soft); }
.kpi .chip.orange { background: var(--orange-soft); }
.kpi .chip.blue { background: #EAF2FB; }
/* 🔴 27/08 (Julien) : « il me faut des couleurs pour les chiffres ». Une teinte
   par nature de chiffre, la même famille que les pastilles d'info : le jour en
   rouge de marque, le mois en vert, l'année en bleu ardoise, et ce qui attend
   un règlement en orange. */
.kpi .value.c-red { color: var(--red); }
.kpi .value.c-green { color: #146B3C; }
.kpi .value.c-blue { color: #1B4E85; }
/* 29/08 (Julien) : « orange plus saturé sur tout le site » */
.kpi .value.c-orange { color: var(--orange); }
.kpi .value.c-red small, .kpi .value.c-green small,
.kpi .value.c-blue small, .kpi .value.c-orange small { color: inherit; opacity: .5; }

/* Choix du produit habituel dans le catalogue, avec les vignettes (Julien 27/08) */
.fi-prod { display: block; width: 100%; }
.fi-prod-q { width: 100%; margin-bottom: 8px; }
.fi-prod-liste {
  display: block; max-height: 280px; overflow-y: auto;
  border: 1px solid var(--border); border-radius: 12px; background: var(--card);
  scrollbar-width: none; -ms-overflow-style: none;
}
.fi-prod-liste::-webkit-scrollbar { display: none; width: 0; }
.fi-prod-l {
  display: flex; align-items: center; gap: 11px; width: 100%;
  border: 0; border-bottom: 1px solid var(--border); background: none;
  font: inherit; text-align: left; padding: 8px 11px; cursor: pointer;
}
.fi-prod-l:last-child { border-bottom: 0; }
.fi-prod-l:hover { background: var(--red-soft); }
.fi-prod-l.on { background: var(--red); color: #fff; }
.fi-prod-l img, .fi-prod-l .prod-thumb {
  width: 36px; height: 36px; flex: 0 0 auto; border-radius: 8px;
  object-fit: contain; background: transparent; border: 0;
}
.fi-prod-l .prod-thumb.vide { background: #FFFFFF; border: 1px solid var(--border); }
.fi-prod-nom { flex: 1 1 auto; min-width: 0; font-size: 14.5px; font-weight: 700; line-height: 1.25; }
.fi-prod-nom small { display: block; font-size: 12px; font-weight: 600; color: var(--muted); }
.fi-prod-l.on .fi-prod-nom small { color: rgba(255, 255, 255, .72); }
.fi-prod-prix { font-size: 13.5px; font-weight: 800; color: var(--red); white-space: nowrap; }
.fi-prod-l.on .fi-prod-prix { color: #fff; }
.fi-prod-vide { padding: 14px; color: var(--muted); font-weight: 600; }
.fi-prod-pied { display: flex; justify-content: flex-end; margin-top: 8px; }
.fi-prod-vider {
  border: 1px solid var(--border); background: var(--card); color: var(--muted);
  font: inherit; font-size: 13.5px; font-weight: 700;
  padding: 6px 12px; border-radius: 9px; cursor: pointer;
}
.fi-prod-vider:hover { border-color: var(--red); color: var(--red); }

/* Boîte de dialogue maison : plus aucun confirm()/alert() du navigateur
   (Julien 28/08). Construite à la volée par app.js, rien dans le HTML. */
.dlg-fond {
  position: fixed; inset: 0; z-index: 1000;
  display: flex; align-items: center; justify-content: center; padding: 20px;
  background: rgba(23, 21, 15, .5);
}
.dlg {
  background: var(--card); border-radius: 20px; padding: 26px 24px;
  max-width: 400px; width: 100%; box-shadow: var(--shadow); text-align: center;
  /* 🔴 03/09 : pattern des modales JBC. Une boîte qui demande quelque chose
     (l'export et sa période) peut dépasser un écran de téléphone : le contenu
     défile DANS la boîte, SANS barre visible (« c'est pas joli »), et les deux
     boutons restent au fond, toujours atteignables. */
  display: flex; flex-direction: column; overflow: hidden;
  max-height: calc(100vh - 40px);
}
.dlg-scroll { overflow-y: auto; scrollbar-width: none; -ms-overflow-style: none; }
.dlg-scroll::-webkit-scrollbar { display: none; width: 0; }
.dlg h3 { font-size: 19px; font-weight: 800; margin-bottom: 9px; }
.dlg p { color: var(--muted); font-weight: 600; font-size: 15px; line-height: 1.5; margin-bottom: 20px; }
.dlg-btns { display: flex; gap: 10px; }
/* Le bouton d'action peut aussi être un LIEN (02/09 : les boîtes qui
   téléchargent un fichier, où seul un vrai lien garantit le téléchargement).
   Même habit exactement, d'où la même règle : un seul endroit à modifier.
   ⚠️ On écrit « .dlg-oui » et NON « a.dlg-oui » : ajouter le nom de la balise
   ferait gagner cette règle-ci contre la règle rouge du dessous, à un cran de
   moins, et le lien s'afficherait en gris sur fond carte. Même piège que la
   boîte de règlement du 01/09. */
.dlg-btns button, .dlg-btns .dlg-oui {
  flex: 1; font: inherit; font-size: 15.5px; font-weight: 800; cursor: pointer;
  padding: 11px; border-radius: 12px; border: 1px solid var(--border);
  background: var(--card); color: var(--muted);
  text-align: center; text-decoration: none;
}
/* 🔴 01/09 (Julien) : « mets le bouton Enregistrer direct en rouge avec texte
   blanc, Annuler : toujours texte rouge et bordure rouge, sur tous les menus ».
   La règle vivait en TROIS exemplaires, posés boîte par boîte (la note, l'achat
   de stock…), et toute nouvelle boîte repartait donc de l'habit générique :
   Enregistrer en encre foncée, ou pire, en rouge avec le texte GRIS. C'est le
   cas de la boîte de règlement, où « .dlg-btns button { color: var(--muted) } »
   l'emportait sur le blanc de « .btn », plus faible d'un cran. La règle est
   posée UNE fois, ici, sur les deux façons d'écrire le bouton d'action
   (« .dlg-oui » et « .btn ») : toute boîte de dialogue la reçoit d'office. */
.dlg-btns .dlg-oui, .dlg-btns .btn {
  background: var(--red); border-color: var(--red); color: #FFFFFF;
}
.dlg-btns .dlg-oui:hover, .dlg-btns .btn:hover {
  background: var(--red-dark); border-color: var(--red-dark); color: #FFFFFF;
}
.dlg-btns .dlg-non { border-color: var(--red); color: var(--red); background: var(--card); }
.dlg-btns .dlg-non:hover { border-color: var(--red-dark); color: var(--red-dark); }

/* 🔴 03/09 (Julien, boîte d'export du comptable) : une boîte peut porter
   son ICÔNE (rouge, dans son rond clair, au-dessus du titre) et un ENCART qui
   met en évidence l'essentiel, ici combien de pièces et de quelle date à
   quelle date. Habit générique, posé UNE fois : toute boîte qui passe `icone`
   ou `encart` à boiteDialogue le reçoit d'office.
   ⚠️ On écrit « .dlg .dlg-encart » (deux classes) et non « .dlg-encart » :
   « .dlg p », plus haut, vaut une classe + une balise et l'emporterait sinon,
   l'encart repartirait en gris avec la marge du texte courant. Même piège de
   spécificité que le bouton rouge du 01/09. */
.dlg-ic-rond {
  display: flex; align-items: center; justify-content: center;
  width: 46px; height: 46px; margin: 0 auto 12px;
  border-radius: 50%; background: var(--red-soft); color: var(--red);
}
.dlg-ic-rond .fi-ic { width: 24px; height: 24px; color: var(--red); }
.dlg .dlg-encart {
  background: var(--red-soft); color: var(--ink);
  border-radius: 12px; padding: 10px 14px; margin-bottom: 14px;
  font-size: 15px; font-weight: 700; line-height: 1.5;
}
.dlg .dlg-encart b { font-weight: 800; }
/* le compte au-dessus, la période en dessous : deux lignes de sens, jamais
   « 30 pièces » coupé de sa suite sur un petit téléphone (mesuré à 320 px) */
.dlg .dlg-encart .dlg-enc-n { display: block; font-size: 16px; }
.dlg .dlg-encart .dlg-enc-d { display: block; margin-top: 2px; }

/* 🔴 03/09 (Julien) : « c'est un peu contre-intuitif de dire : on sélectionne,
   et c'est parce qu'on a sélectionné qu'on va exporter ». La boîte d'export
   demande donc sa période elle-même, dans son corps, entre le titre et
   l'encart. Les étiquettes reprennent EXACTEMENT l'habit `.chip-f` de la page,
   rien de redéfini : une seule définition par composant. */
/* 🔴 03/09 (2e retour Julien) : « mets les tags au milieu, espace un peu pour
   que ce soit plus propre ». Tout le corps est CENTRÉ comme le reste de la
   boîte (titre, encart, texte) et respire : le bloc de choix se lit d'un coup,
   au lieu de faire un pavé collé à gauche. Les étiquettes gardent l'habit
   `.chip-f` de la page, rien n'est redéfini : seul l'espacement change. */
.dlg-corps { text-align: center; margin: 4px 0 18px; }
.dlg-lab {
  display: block; font-size: 12.5px; font-weight: 800; letter-spacing: .3px;
  text-transform: uppercase; color: var(--muted); margin-bottom: 12px;
}
/* En flux libre, six étiquettes se répartissent au hasard de leur longueur :
   mesuré 1+1+1+2+1 à 320 px et 2+3+1 à 430 px, avec « Dates précises » seule
   au bout. Une grille qui se remplit toute seule (deux colonnes quand la place
   y est, une seule sur un petit téléphone) donne des rangées TOUJOURS pleines,
   sans media query et sans toucher à l'habit `.chip-f`. */
.dlg-chips {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: 10px;
}
/* Texte centré dans sa cellule, et un peu moins de rembourrage qu'en pleine
   page : c'est la grille qui donne sa largeur à l'étiquette, le rembourrage
   n'a plus à la dessiner. Sans ça, « Le mois dernier » réclamait 131 px et le
   passage à deux colonnes se jouait à 5 px près sur un iPhone de 390. */
.dlg-chips .chip-f { justify-content: center; padding-left: 10px; padding-right: 10px; }
.dlg-dates {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; margin-top: 14px;
}
.dlg-dates .perso-l { flex: 1 1 130px; }
.dlg-coche {
  display: flex; align-items: flex-start; justify-content: center; gap: 9px;
  margin-top: 16px; text-align: left;
  font-size: 13.5px; font-weight: 600; line-height: 1.45; color: var(--muted);
  cursor: pointer;
}
.dlg-coche input { width: 17px; height: 17px; margin: 1px 0 0; accent-color: var(--red); flex: 0 0 auto; }
/* Pendant un comptage, ou quand la période ne donne rien, le lien ne doit PAS
   partir : il emporterait la période d'avant. Neutralisé, jamais caché, pour
   que la boîte garde ses deux boutons et sa hauteur. */
.dlg-btns .dlg-oui.off {
  background: var(--soft); border-color: var(--soft); color: #FFFFFF;
  pointer-events: none; cursor: default;
}
.dlg .dlg-encart .dlg-date { color: var(--red); }
/* la date, et le petit mot qui l'annonce, ne se coupent jamais */
.dlg .dlg-encart .dlg-borne, .dlg .dlg-encart .dlg-date { white-space: nowrap; }

/* Quantité et remise modifiables dans le panier (Julien 28/08) */
.pl-mod {
  display: inline-block; min-width: 34px; padding: 3px 8px; margin: -3px -8px;
  border-radius: 8px; cursor: text;
}
.pl-mod:hover, .pl-mod:focus-visible { background: var(--cream-deep); }
.pl-mod.vide { color: var(--soft-txt); }
.pl-in {
  width: 62px; max-width: 100%; font: inherit; font-size: 16px; font-weight: 700; text-align: right;
  padding: 4px 8px; border: 1.5px solid var(--red); border-radius: 8px; background: #fff;
}
.pl-in:focus { outline: none; box-shadow: 0 0 0 3px var(--red-soft); }

/* Quand le tir du jour est parti, la carte annonce le prochain : une date se
   lit moins gros qu'un compteur (Julien 28/08). */
.tir-card .t2.t2-date { font-size: 26px; letter-spacing: -.6px; }
.tir-card .t2.t2-date small { font-size: 17px; }

/* Écran de déverrouillage : quand la clé manque, on ne laisse pas croire que
   les chiffres de démonstration sont réels (Julien 28/08). */
#ecranCle {
  position: fixed; inset: 0; z-index: 999;
  display: flex; align-items: center; justify-content: center; padding: 22px;
  background: rgba(23, 21, 15, .62); backdrop-filter: blur(3px);
}
.cle-carte {
  background: var(--card); border-radius: 20px; padding: 28px 26px;
  max-width: 420px; width: 100%; box-shadow: var(--shadow); text-align: center;
}
.cle-carte h2 { font-size: 21px; font-weight: 800; margin-bottom: 10px; }
.cle-carte p { color: var(--muted); font-weight: 600; font-size: 15px; line-height: 1.5; margin-bottom: 18px; }
.cle-carte input {
  width: 100%; font: inherit; font-size: 16px; font-weight: 600;
  padding: 11px 14px; border: 1.5px solid var(--border); border-radius: 12px;
  background: #FFFFFF; margin-bottom: 12px; text-align: center;
}
.cle-carte input:focus { outline: none; border-color: var(--red); box-shadow: 0 0 0 3px var(--red-soft); }
.cle-carte button {
  width: 100%; font: inherit; font-size: 16px; font-weight: 800; cursor: pointer;
  padding: 12px; border: 0; border-radius: 12px; background: var(--red); color: #fff;
}
.cle-carte button:hover { background: var(--red-dark); }
.cle-carte small { display: block; margin-top: 14px; color: var(--muted); font-weight: 600; font-size: 13px; line-height: 1.45; }
html.sansacces .kpis .kpi .value, html.sansacces tbody[id] { visibility: hidden; }

/* Le panier de la fiche client : mêmes colonnes que dans Notion (Julien 28/08) */
/* 🔴 28/08 (Julien) : « ça bouge les colonnes quand je clique dedans ».
   Les largeurs sont FIGÉES : l'éditeur qui s'ouvre dans une cellule ne peut
   plus élargir sa colonne ni pousser les voisines. */
.panier-tbl { border-collapse: separate; border-spacing: 0 7px; table-layout: fixed; width: 100%; }
.panier-tbl th:nth-child(2) { width: 112px; }
.panier-tbl th:nth-child(3) { width: 110px; }
/* 🔴 01/09 soir : les deux colonnes de droite gagnent 8 px chacune. Les
   pastilles posées ce soir (remise, total) sont plus larges que le texte nu
   qu'elles remplacent, et la mesure au banc les donnait à 73 px pour 72 de
   place en Remise, 93 pour 90 en Total : elles débordaient sur la voisine.
   Remise prend 12 px (pas 8) : à 8 le pire cas « −99,99 % » tombait pile sur
   la limite, sans un pixel de marge.
   On élargit la colonne, on ne rétrécit pas le chiffre. */
.panier-tbl th:nth-child(4) { width: 112px; }
.panier-tbl th:nth-child(5) { width: 120px; }
.panier-tbl th:nth-child(6) { width: 48px; }
.panier-tbl th { font-size: 13px; }

/* la quantité se règle au chevron, sans rien déplacer (Julien 28/08) */
/* 🔴 01/09 soir (Julien) : « je veux bien que les flèches soient en rouge ».
   Elles étaient en --soft (gris sable) au repos et ne devenaient rouges qu'au
   survol : sur une rangée beige, personne ne voyait qu'on pouvait cliquer, et
   au doigt il n'y a pas de survol du tout. Elles sont rouges dès le repos, et
   le survol les fonce (--red-dark) pour garder une réaction au geste. */
.qte-box { display: inline-flex; align-items: center; gap: 7px; }
.qte-box.occupe { opacity: .5; pointer-events: none; }
.qte-fl { display: flex; flex-direction: column; gap: 1px; }
.qte-b {
  border: 0; background: none; padding: 0; cursor: pointer; color: var(--red);
  width: 20px; height: 13px; display: flex; align-items: center; justify-content: center;
}
.qte-b svg { width: 13px; height: 13px; display: block; }
.qte-b.haut svg { transform: rotate(180deg); }
.qte-b:hover { color: var(--red-dark); }
/* 🔴 Les pastilles sont plus larges que le texte nu : plutot que de voler
   30 px au nom du produit en elargissant les colonnes, on resserre le rembourrage
   des seules colonnes de chiffres (14 px -> 8/10). Mesure au banc a l'appui. */
.panier-tbl td.num { padding-left: 8px; padding-right: 10px; }
.panier-tbl td { vertical-align: middle; }
.panier-tbl tbody td { background: #FAF6EE; }

/* 🔴 01/09 soir (Julien) : « mets un peu de fonds colorés pour sortir un peu
   les infos, sinon on les lit moins bien ». La rangée était d'un seul beige
   d'un bout à l'autre : les quatre chiffres avaient exactement le même poids
   à l'œil. Trois pastilles, trois natures différentes, et le prix unitaire
   laissé nu (tout colorer, c'est ne rien mettre en avant) :
     · la QUANTITÉ en blanc bordé, l'habit d'un réglage qu'on touche ;
     · la REMISE en rouge doux, elle n'existe que sur quelques lignes et c'est
       justement l'info à repérer ;
     · le TOTAL avec .mnt-ttc, l'habit que porte déjà tout montant TTC dans
       Factures et dans l'Historique.
   Les deux valeurs éditables gardent leur pastille pendant la saisie : elle
   se retire quand l'éditeur s'ouvre, sinon on aurait un fond dans un fond. */
/* 🔴 Mesuré au banc : sans nowrap, « −23,08 % » se coupait EN DEUX LIGNES
   dans sa pastille. Le signe « − » (U+2212) est un point de coupure pour le
   navigateur : il restait seul en haut et « 23,08 % » passait dessous, la
   rangée doublait de hauteur. C'est la règle du zéro mot orphelin, appliquée
   à un nombre : un chiffre et son signe ne se séparent jamais. */
.panier-tbl .pl-qte {
  margin: 0; min-width: 32px; padding: 4px 9px; white-space: nowrap;
  font-variant-numeric: tabular-nums;
  background: var(--card); border: 1px solid var(--border);
  border-radius: 9px; font-weight: 800;
}
.panier-tbl .pl-qte:hover, .panier-tbl .pl-qte:focus-visible {
  background: var(--red-soft); border-color: #F5D3D0;
}
.panier-tbl .pl-remise {
  margin: 0; padding: 4px 9px; border-radius: 9px; white-space: nowrap;
  font-variant-numeric: tabular-nums;
  background: var(--red-soft); color: var(--red-dark); font-weight: 800;
}
.panier-tbl .pl-remise:hover, .panier-tbl .pl-remise:focus-visible {
  background: #FADFDC;
}
/* 🔴 04/09/2026 (signalement de Fabien : « j'ai fait payer 150 euros au lieu
   de 145, je ne sais pas comment modifier le prix payé ») : le TOTAL de la
   ligne s'édite au clic, comme la quantité et la remise. Il garde l'habit
   .mnt-ttc, celui que porte tout montant TTC du cockpit : ce n'est pas une
   nature nouvelle, c'est le même montant, devenu corrigeable. Le survol le
   dit par un liseré, pas par une couleur : la couleur est déjà prise, et
   changer la teinte au survol ferait croire à un changement de nature.
   Un prix FIXÉ À LA MAIN, lui, doit se repérer d'un coup d'œil sur une
   rangée : trait plein rouge, comme une pastille qu'on aurait entourée. */
.panier-tbl .pl-total { cursor: pointer; border: 1px solid transparent; }
.panier-tbl .pl-total:hover, .panier-tbl .pl-total:focus-visible { border-color: #F5D3D0; }
.panier-tbl .pl-total.negocie { border-color: var(--red); }
/* la remise n'agit plus quand un prix est posé à la main : elle s'efface au
   lieu de laisser croire qu'elle compte encore (le titre au survol l'explique) */
.panier-tbl .pl-remise.sans-effet {
  background: var(--cream-deep); color: var(--muted); text-decoration: line-through;
}
.panier-tbl .pl-mod:has(input) {
  background: none; border-color: transparent; padding: 0; min-width: 0;
}
.panier-tbl .mnt-ttc { font-size: 14.5px; }
.panier-tbl tbody td:first-child { border-radius: 12px 0 0 12px; }
.panier-tbl tbody td:last-child { border-radius: 0 12px 12px 0; }
.panier-tbl .prod-cell { gap: 11px; }
.panier-tbl .prod-thumb { width: 38px; height: 38px; }
/* Le récapitulatif du panier : colonne étroite collée à DROITE, sous les prix
   (Julien 28/08 : « plus pratique et moins large »). */
.panier-pied { display: flex; justify-content: flex-end; }
.panier-recap { width: 100%; max-width: 380px; }
.panier-recap .total-row { padding: 12px 0; }
/* 07/09 (lot B) : le total du panier hors taxes, sous le TTC */
.total-ht { text-align: right; color: var(--muted); font-size: 13px; font-weight: 600; margin-top: -2px; min-height: 16px; }
/* 07/09 : l'heure impérative et la précision, dans l'état « livraison validée » */
.pv-etat .pv-heure { color: var(--red); font-weight: 800; display: inline-flex; align-items: center; gap: 3px; }
.pv-etat .pv-heure .fi-ic, .pv-etat .pv-note .fi-ic { width: 13px; height: 13px; color: currentColor; }
.pv-etat .pv-note { display: inline-flex; align-items: center; gap: 4px; font-style: italic; color: var(--ink); }
.panier-liv { border-top: 1px solid var(--border); }
.panier-liv .fi-l { grid-template-columns: 20px auto minmax(0, 1fr); padding: 8px 0; }
.panier-liv .fi-l:last-child { border-bottom: 0; }
/* Livraison validée : le bloc s'estompe, mais reste modifiable : c'est armé,
   pas verrouillé (Julien 28/08). */
.panier-recap.arme .panier-liv { opacity: .55; }
.panier-cta { margin-top: 14px; }
.panier-cta .btn { width: 100%; justify-content: center; }
.panier-cta .btn img { width: 18px; height: 18px; }
.pv-etat {
  font-size: 14.5px; font-weight: 700; line-height: 1.5;
  color: #146B3C; background: var(--green-soft);
  padding: 11px 14px; border-radius: 12px; margin-bottom: 10px;
}
.pv-etat small { display: block; margin-top: 3px; font-size: 13px; font-weight: 600; color: #2f6a4a; }
.pv-etat.doux { color: var(--muted); background: var(--cream-deep); font-weight: 600; }
@media (max-width: 700px) {
  .panier-recap { max-width: none; }
}

/* Fiche produit ouverte au clic sur une ligne d'inventaire (Julien 28/08) */
.pf-tete { display: flex; align-items: center; gap: 16px; margin-bottom: 20px; }
.pf-img { width: 96px; height: 96px; flex: 0 0 auto; object-fit: contain; }
.pf-img.vide {
  background: #FFFFFF; border: 1px solid var(--border); border-radius: 12px;
  display: flex; align-items: center; justify-content: center; color: var(--soft);
}
.pf-img.vide .fi-ic { width: 40px; height: 40px; }
.pf-tete h3 { font-size: 19px; font-weight: 800; line-height: 1.25; margin-bottom: 9px; }
.pf-tete .tag { font-size: 12.5px; padding: 4px 10px; }
.pf-grille {
  /* 🔴 01/09 (Julien, 2e retour) : « les lignes et la hauteur bougent aussi ici
     quand je veux éditer ». MESURÉ au banc (maquette/_banc-produit.html) : le
     saut est d'abord HORIZONTAL. En `1fr 1fr`, une colonne de grille ne descend
     jamais sous la largeur mini de son contenu : le champ de saisie (113 px)
     étant plus large que la pastille qu'il remplace (64 px), la colonne
     s'élargissait de 11,7 px à l'ouverture, l'autre colonne se décalait
     d'autant, et TOUTE la fiche glissait sous les yeux. `minmax(0, 1fr)` coupe
     ce lien : les deux colonnes se partagent la place, quoi qu'on mette dedans. */
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 0 22px;
  border-top: 1px solid var(--border); padding-top: 6px; margin-bottom: 14px;
}
/* 🔴 30/08 (Julien) : « ces doubles traits sont bizarres, j'en veux qu'un
   entre ». Entre deux grilles il y avait DEUX filets : le bas de la dernière
   ligne de la première, et le haut de la seconde. Seule la première grille
   garde son filet de tête. */
.pf-grille + .pf-grille { border-top: 0; padding-top: 0; }
.pf-l {
  /* 🔴 01/09 (Julien, 2e retour) : `baseline` faisait descendre le libellé de
     1,5 px à l'ouverture (un champ de formulaire n'a pas la baseline du texte
     qu'il remplace). On centre : il n'y a plus de baseline à suivre.
     Et la réserve de 48 px, posée le matin même, était un PARI sur « la hauteur
     du plus grand des deux états » : elle tenait tant que l'éditeur restait sous
     le plancher, 2 px de plus et la ligne repartait. Comme sur la fiche client
     et le tableau des factures, on retire la variable plutôt que de la border :
     tout ce qui peut occuper la valeur d'une ligne (pastille teintée, champ de
     saisie, « … » d'attente) tient dans la MÊME boîte de 30 px. */
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 9px 0; border-bottom: 1px solid var(--border);
}
.pf-l > b {
  box-sizing: border-box; height: 30px;
  display: flex; align-items: center; justify-content: flex-end;
}
.pf-v { box-sizing: border-box; height: 30px; }
.pf-l b .pf-n {
  box-sizing: border-box; height: 26px; line-height: 1;
  padding-top: 0; padding-bottom: 0;
  display: inline-flex; align-items: center;
}
.pf-l .fi-attente { display: inline-flex; align-items: center; height: 26px; }
.pf-l span { color: var(--muted); font-weight: 600; font-size: 14px; }
/* 🔴 01/09 (Julien) : « j'ai pas envie que l'édition fasse passer sur deux
   lignes les titres des sections, ça doit pas bouger ». En ouvrant l'éditeur,
   le champ de saisie (92 px) s'ajoutait à la largeur du chiffre et poussait le
   libellé, qui se repliait : « Seuil de réappro » devenait deux lignes et toute
   la fiche sautait. Le libellé de gauche ne se replie plus, et c'est la valeur
   de droite qui cède la place (elle est de toute façon plus courte). */
.pf-l > span:first-child { white-space: nowrap; flex: 0 0 auto; }
.pf-l > b { min-width: 0; margin-left: auto; }
.pf-l b { font-size: 16px; font-weight: 800; font-variant-numeric: tabular-nums; }
.pf-l b small { font-size: 13px; font-weight: 700; color: var(--muted); }
.pf-l b.ok { color: #146B3C; }
.pf-l b.ko { color: var(--red); }
.pf-note { color: var(--muted); font-weight: 600; font-size: 13px; line-height: 1.5; }

/* 28/08 : icônes des lignes de la fiche produit (pack Tabler, comme le set),
   et chiffres corrigeables au clic, soulignés en pointillé comme la fiche
   client pour dire « ça se modifie ». */
.pf-l span .pf-ic { width: 15px; height: 15px; margin-right: 7px;
  vertical-align: -2.5px; }
/* 🔴 30/08 (Julien) : « j'aimerais une infobulle ou une sélection qui indique
   qu'on peut modifier le chiffre ». Au repos, le chiffre modifiable porte un
   soulignement pointillé discret : l'indice est PERMANENT, il ne se découvre
   plus au survol. Au survol, la valeur se pose sur sa pastille rouge douce et
   un crayon apparaît ; l'infobulle maison dit « Cliquer pour modifier ». */
.pf-v {
  cursor: pointer; position: relative;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 2px 4px; margin: -2px -4px; border-radius: 8px;
}
.pf-v::after {
  content: ''; width: 13px; height: 13px; flex: 0 0 auto;
  background: url('img/ic-pencil-grey.png?v=2') center/contain no-repeat;
  opacity: 0;
}
/* 🔴 01/09 (Julien, « pareil partout ») : ici aussi, plus de pointillé, juste
   le fond rouge doux et le crayon. Le fond se pose sur .pf-v, qui est le PARENT
   de la pastille teintée .pf-n : celle-ci se peint par-dessus et garde donc sa
   couleur (c'est ce qui avait fait renoncer au fond le 30/08, quand la règle
   visait le chiffre lui-même). */
.pf-v:hover { background: var(--red-soft); }
.pf-v:hover::after { opacity: .75; background-image: url('img/ic-pencil-red.png?v=2'); }
/* l'infobulle maison, posée au-dessus de la valeur */
.pf-v::before {
  content: 'Cliquer pour modifier';
  position: absolute; bottom: calc(100% + 7px); right: 0;
  padding: 5px 10px; border-radius: 9px;
  background: var(--ink); color: #FFFFFF;
  font-size: 12px; font-weight: 700; white-space: nowrap; letter-spacing: .2px;
  opacity: 0; pointer-events: none; z-index: 5;
}
.pf-v:hover::before { opacity: 1; }
/* 🔴 16px mini : sous cette taille Safari iOS zoome au tap et la page zoomée
   se met à défiler de côté (règle JBC). Le champ était à 15px. */
/* 🔴 01/09 (Julien) : « ça doit pas bouger ». Le champ de saisie était plus
   HAUT que la pastille qu'il remplace (padding de 7 px contre 3), donc la ligne
   grandissait de 13 px à l'ouverture et toute la fiche descendait d'un cran. On
   lui donne la hauteur de ce qu'il remplace. */
/* .pf-in porte AUSSI .fi-in (c'est app.js qui pose les deux) : à égalité de
   spécificité, .fi-in gagne parce qu'elle est déclarée plus bas dans la
   feuille. On monte donc d'un cran pour que ce réglage-ci s'applique. */
.pf-l .pf-in {
  /* le champ tient dans la boîte de 26 px de la pastille qu'il remplace, en
     comptant ses bordures (box-sizing) : plus de calcul de padding à la main.
     🔴 01/09 (Julien) : et il ne DÉPASSE JAMAIS sa colonne, sinon il se pose
     par-dessus le libellé, qui ne se replie pas. La largeur vient de app.js
     (en `ch`, d'après ce qu'on écrit dedans) ; ici on pose le plafond. */
  box-sizing: border-box; height: 26px; max-width: 100%;
  font-size: 16px; font-weight: 700; text-align: right;
  padding: 0 8px; line-height: 1;
}
.pf-l > b { min-width: 0; }
/* 🔴 01/09 (Julien) : « les chiffres ne sont pas bien alignés ensemble à droite ».
   MESURÉ : les pastilles modifiables finissaient à 662,5 px, les autres (Écart,
   Marge, Valeur en stock) à 681,5 : 19 px d'écart, soit très exactement le
   crayon du survol (13 px) et son espace (6 px). Ce crayon est un pseudo-élément
   qui prend sa place DANS la boîte même quand il est invisible : les lignes qui
   n'en ont pas gagnaient 19 px vers la droite, et la colonne partait en dents de
   scie. Même remède que sur la fiche client le matin même : on réserve la
   gouttière PARTOUT, le crayon n'étant peint que là où ça se modifie.
   Voisin : la mémoire « un pseudo-élément en ligne à opacity:0 prend quand même
   sa largeur ». */
.pf-l > b:not(:has(.pf-v)) { padding-right: 19px; }

/* Fonds colorés sur les chiffres de la fiche produit (Julien 30/08 : « ça
   manque de couleurs et de fonds colorés sur les chiffres des fiches stock »).
   Une teinte par NATURE, la même que partout ailleurs dans le cockpit : le
   prix TTC en rouge et le prix d'achat HT en bleu ardoise reprennent très
   exactement les pastilles du tableau des factures. */
.pf-l b .pf-n {
  display: inline-block; padding: 3px 10px; border-radius: 8px;
  font-weight: 800; font-variant-numeric: tabular-nums; white-space: nowrap;
}
.pf-n.n-rouge  { color: var(--red-dark); background: var(--red-soft); }
.pf-n.n-bleu   { color: #2F6F8F;        background: #EAF2FB; }
.pf-n.n-vert   { color: #146B3C;        background: var(--green-soft); }
.pf-n.n-orange { color: var(--orange-txt);        background: #FBF2E2; }
.pf-n.n-gris   { color: var(--muted);   background: var(--cream-deep); }
.pf-l b .pf-n small { color: inherit; opacity: .62; font-weight: 700; }
/* la pastille GARDE sa couleur même quand le chiffre est modifiable : c'est
   la teinte qui dit la nature du montant, l'indice de modification vit sur le
   trait et le crayon, pas sur le fond. */
@media (max-width: 600px) {
  .pf-grille { grid-template-columns: 1fr; }
  .pf-img { width: 74px; height: 74px; }
}

/* Recherche de produit dans la modale d'ajout au panier (Julien 28/08) */
.pp-q {
  width: 100%; font: inherit; font-size: 16px; font-weight: 600;
  padding: 10px 13px; border: 1.5px solid var(--border); border-radius: 11px;
  background: #FFFFFF; color: var(--ink); margin-bottom: 9px;
}
.pp-q:focus { outline: none; border-color: var(--red); box-shadow: 0 0 0 3px var(--red-soft); }
#ppListe { max-height: min(46vh, 400px); }

/* Notes vocales du signalement (Julien 28/08 : gros bouton rond centré,
   plusieurs notes possibles) */
.vocal { display: flex; flex-direction: column; align-items: center; gap: 10px; margin-top: 18px; }
.vocal-rond {
  width: 68px; height: 68px; border-radius: 50%;
  border: 0; background: var(--red); cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  box-shadow: 0 6px 18px rgba(227, 6, 19, .3);
}
.vocal-rond:hover { background: #C00511; }
.vocal-rond .vr-mic { width: 32px; height: 32px; display: block; }
/* en enregistrement : le micro laisse place au carré « stop », l'anneau pulse */
.vocal-rond .vr-stop {
  display: none; width: 22px; height: 22px; border-radius: 6px; background: #fff;
}
.vocal-rond.rec .vr-mic { display: none; }
.vocal-rond.rec .vr-stop { display: block; animation: pulseRec 1s ease-in-out infinite; }
.vocal-rond.rec { animation: pulseAnneau 1.6s ease-out infinite; }
.vocal-rond:disabled { background: var(--cream-deep); box-shadow: none; cursor: default; }
.vocal-rond:disabled .vr-mic { opacity: .45; filter: grayscale(1); }
@keyframes pulseRec { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }
@keyframes pulseAnneau {
  0% { box-shadow: 0 0 0 0 rgba(227, 6, 19, .35); }
  100% { box-shadow: 0 0 0 16px rgba(227, 6, 19, 0); }
}
.vocal-etat {
  color: var(--muted); font-weight: 700; font-size: 14px;
  font-variant-numeric: tabular-nums; text-align: center;
}
/* 🔴 01/09 (Julien) : « trop court », quand le bouton est relâché avant d'avoir
   parlé. Le message prend l'ambre de tout ce qui réclame un geste dans le
   cockpit (le « à compléter » des fiches) : il se lit comme une consigne, pas
   comme une erreur. */
.vocal-etat.avert { color: var(--orange); }
/* la pile des notes enregistrées : une carte par vocal, lecteur + croix */
.vocal-liste { width: 100%; display: flex; flex-direction: column; gap: 8px; }
.vocal-note {
  display: flex; align-items: center; gap: 10px;
  background: var(--cream, #FAF6EE); border: 1px solid var(--border);
  border-radius: 12px; padding: 8px 10px;
}
.vocal-note .vn-num {
  font-size: 13px; font-weight: 800; color: var(--red); white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.vocal-note audio { height: 34px; min-width: 0; flex: 1; }
.vocal-x {
  border: 0; background: var(--cream-deep); color: var(--muted); flex: 0 0 auto;
  width: 30px; height: 30px; border-radius: 50%; cursor: pointer; font-weight: 800;
}
.vocal-x:hover { background: var(--red-soft); color: var(--red); }

/* La photo du produit dans l'inventaire (rapatriée du CRM Husse, 27/08) */
.prod-cell { display: flex; align-items: center; gap: 12px; }
.prod-thumb {
  width: 42px; height: 42px; flex: 0 0 auto; border-radius: 9px;
  object-fit: contain; background: transparent; border: 0;
}
.prod-thumb.vide {
  background: #FFFFFF; border: 1px solid var(--border);
  display: inline-flex; align-items: center; justify-content: center; color: var(--soft);
}
.prod-thumb.vide .fi-ic { width: 20px; height: 20px; }
.rowlink[data-pid] { cursor: pointer; }

/* Raccourcis de période au-dessus d'un tableau (Julien 27/08) */
.chips-periode { display: flex; flex-wrap: wrap; gap: 8px; margin: 2px 0 18px; }
.chip-f {
  border: 1px solid var(--border); background: var(--card); color: var(--muted);
  font: inherit; font-size: 14px; font-weight: 700;
  min-height: 40px; padding: 0 14px; border-radius: var(--r); cursor: pointer;
  display: inline-flex; align-items: center; gap: 7px;
}
.chip-f:hover { border-color: var(--red); color: var(--red); }
/* 🔴 02/09 (Julien) : étiquette choisie = fond clair « un peu » teinté, bordure
   et compteur dans la couleur de l'étiquette, texte à l'encre (même règle
   que les étiquettes de la page Clients, « sur les autres pages du même genre »). */
.chip-f.on { background: var(--red-soft); border-color: var(--red); color: var(--ink); }
.chip-f b { font-weight: 800; color: var(--red); font-variant-numeric: tabular-nums; }
/* 🔴 29/08 (Julien) : « rajoute des tags reçu et en attente, puis par vendeur
   un tag Fabien / Dépôt / Gabin ». Chaque étiquette porte SA couleur une fois
   choisie, exactement celle de la pastille correspondante dans le tableau :
   on relie l'étiquette à ce qu'elle filtre sans lire le mot. */
.chips-filtres { display: flex; flex-wrap: wrap; align-items: stretch; gap: 10px 12px; margin-bottom: 18px; }
.chips-groupe { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
/* 29/08 (Julien) : « mets STATUT et VENDEUR en rouge pour qu'ils ressortent
   mieux dans le menu tags ». Le gris se fondait dans les étiquettes. */
.chips-lib { font-size: 12.5px; font-weight: 800; letter-spacing: .3px;
             text-transform: uppercase; color: var(--red); margin-right: 2px; }
.chip-f.c-recu.on { background: #DFECE5; border-color: #187A42; }
.chip-f.c-recu.on b { color: #187A42; }
.chip-f.c-attente.on { background: #FAECDC; border-color: var(--orange-txt); }
.chip-f.c-attente.on b { color: var(--orange-txt); }
.chip-f.c-retard.on { background: var(--red-soft); border-color: var(--red); }
.chip-f.c-retard.on b { color: var(--red); }
.chip-f.c-fabien.on { background: #F4E2DD; border-color: #B3320B; }
.chip-f.c-fabien.on b { color: #B3320B; }
.chip-f.c-depot.on { background: #DFE8F3; border-color: #1A5EA8; }
.chip-f.c-depot.on b { color: #1A5EA8; }
.chip-f.c-gabin.on { background: #E0ECE5; border-color: #1F7A43; }
.chip-f.c-gabin.on b { color: #1F7A43; }
.chip-f.c-recu:not(.on):hover    { border-color: #187A42; color: #187A42; }
.chip-f.c-attente:hover { border-color: var(--orange-txt); color: var(--orange-txt); }
.chip-f.c-fabien:not(.on):hover  { border-color: #B3320B; color: #B3320B; }
.chip-f.c-depot:hover   { border-color: #1A5EA8; color: #1A5EA8; }
.chip-f.c-gabin:hover   { border-color: #1F7A43; color: #1F7A43; }
.chip-f.on:hover { color: var(--ink); }

/* 🔴 29/08 (Julien) : la bande « CA HT par poste » sous les cartes KPI de
   Factures et Statistiques : le résumé d'un coup d'œil, avant les barres.
   Un segment par poste, dans sa teinte, avec le mois et l'année. */
/* 🔴 14/09 : la bande passe du flex à une GRILLE dont le nombre de colonnes est
   DÉCIDÉ, jamais subi. Depuis que la pastille porte quatre colonnes (période,
   CA, factures, panier), un flex qui se replie tout seul posait 3 postes sur
   une rangée et le 4e en dessous, tout seul : exactement le défaut que Julien
   avait fait corriger le 01/09. La règle est donc : ou bien TOUS les postes
   tiennent sur une rangée, ou bien on descend d'un cran franc (4 → 2 → 1).
   Jamais d'orphelin, à aucune largeur.

   🔴 C'est une CONTAINER QUERY, pas une media query, et ce n'est pas un
   raffinement : la largeur de la carte ne suit pas celle de la fenêtre. La
   barre latérale disparaît à 860 px, donc la carte fait 721 px dans une
   fenêtre de 768 et seulement 665 px dans une fenêtre de 1024. Une media query
   sur la fenêtre serait fausse des deux côtés de ce saut. On mesure donc la
   CARTE. Comme un élément n'est jamais son propre conteneur, les pastilles
   vivent dans `.pb-grille`, à l'intérieur de la bande qui, elle, est le
   conteneur.

   Les seuils sortent de la mesure, et ils ont été REMESURÉS le 14/09 au soir
   quand Julien a demandé le « HT » sur chaque montant : la pastille la plus
   large est passée de 250 à 288 px, et à 292 px le jour où le compte annuel de
   Fabien passera les 1 000 factures (le banc joue ce cas). D'où 2 × 292 + 12 =
   596, 3 × 292 + 24 = 900, 4 × 292 + 36 = 1204 px de contenu de carte. Un
   seuil laissé à son ancienne valeur aurait serré le texte sans rien casser de
   visible au banc, sauf la ligne « aucun contenu à l'étroit dans sa case ». */
.postes-bande {
  container-type: inline-size;
  display: block; padding: 15px 18px; margin-bottom: 18px;
}
.pb-grille {
  display: grid; grid-template-columns: 1fr;
  align-items: stretch; gap: 10px 12px;
}
@container (min-width: 600px) {
  .pb-grille[data-n="2"], .pb-grille[data-n="4"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@container (min-width: 900px) {
  .pb-grille[data-n="3"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@container (min-width: 1204px) {
  .pb-grille[data-n="4"] { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
/* 🔴 14/09 : le libellé passe AU-DESSUS des postes, à toutes les largeurs (il
   ne le faisait que sous 860 px). MESURÉ : les quatre pastilles demandent
   1000 px et la carte en offre 1081 sur un écran de 1440 ; le libellé resté
   sur leur rangée en mangeait 118 et repoussait le 4e poste en dessous, tout
   seul, exactement le défaut que Julien avait fait corriger le 01/09.
   Au-dessus, il se lit comme le titre du petit tableau. */
.pb-lib { display: block; margin-bottom: 10px;
          font-size: 12.5px; font-weight: 800; letter-spacing: .3px;
          text-transform: uppercase; color: var(--red); }
/* 🔴 29/08 (Julien) : « Fabien a ses CA dans le même fond que lui, pas
   séparé ». Le segment entier est LA pastille : fond du poste (posé inline),
   nom dans la teinte du poste, chiffres en encre sur ce même fond. */
/* 🔴 14/09 (Fabien) : « il me faudrait le nombre de facturations sur le mois
   par poste et donc le panier moyen hors taxe qui en découle ». La pastille
   porte deux colonnes de plus. MESURÉ au banc avec le nom resté à côté des
   chiffres : 287 px par pastille, donc 1146 px de contenu pour 1081 px de
   carte (3 rangées au lieu d'une), et un SCROLL HORIZONTAL à 320 et 360 px
   (body 358 pour 305 de large). Le nom passe donc AU-DESSUS de ses chiffres :
   la pastille devient un petit tableau, période / CA / factures / panier, et
   les colonnes s'alignent d'un poste à l'autre. */
.pb-seg { display: inline-grid; justify-items: start;
          row-gap: 3px; white-space: nowrap;
          padding: 8px 12px; border-radius: 11px; }
.pb-nom { font-weight: 800; font-size: 15px; white-space: nowrap; }
/* Les quatre colonnes du petit tableau : la période, son CA, le nombre de
   factures, le panier moyen. Une rangée par période. */
.pb-duo { display: grid; grid-template-columns: auto auto auto auto;
          align-items: baseline; gap: 2px 8px; }
/* 🔴 14/09 SOIR : avec le « HT » sur chaque montant, la rangée de quatre cases
   demande 264 px, soit 288 px avec la marge de la pastille. Sur un iPhone de
   320 px la carte n'en offre que 241 : MESURÉ au banc, la page repartait en
   SCROLL HORIZONTAL (body 321 pour 305). Sous cette largeur, les quatre cases
   se replient donc en deux rangées de deux, la période et son montant, puis le
   compte et son panier. Rien n'est retiré, surtout pas le HT : c'est la forme
   qui s'adapte, et elle redevient une rangée dès que la place existe (390 px
   de téléphone donnent 309 px de case, c'est assez). */
@container (max-width: 287px) {
  .pb-duo { grid-template-columns: auto auto; justify-content: space-between;
            width: 100%; gap: 3px 10px; }
}
.pb-p { color: var(--muted); font-weight: 600; font-size: 12.5px; white-space: nowrap; }
/* Le compte de factures et le panier moyen : deux colonnes de CONTEXTE, à
   droite du CA. Le chiffre du mois garde la vedette, ces deux-là se lisent
   après, en encre discrète, mais leurs NOMBRES restent en encre pleine :
   c'est ce que Fabien vient chercher. */
.pb-pm { color: var(--muted); font-weight: 600; font-size: 11.5px;
         white-space: nowrap; }
.pb-pm b { color: var(--ink); font-weight: 800;
           font-variant-numeric: tabular-nums; }
.pb-pm.vide { color: var(--muted); opacity: .6; }
/* 🔴 01/09 (Julien, capture iPhone) : « Fabien est à côté, je le souhaite à la
   ligne comme les autres ». Acquis gardé : le libellé prend toute la largeur
   et les postes s'alignent dessous, en colonne sur un téléphone. */
@media (max-width: 860px) { .postes-bande .pb-grille { gap: 8px 10px; } }
/* 🔴 01/09 (Julien, même capture) : sur téléphone les trois pastilles de poste
   s'arrêtaient chacune où son texte s'arrêtait, en marches d'escalier au
   milieu de la carte. Elles prennent la largeur entière, le nom à gauche et
   les deux montants alignés à droite : trois lignes qui se lisent en colonne,
   comme un petit tableau. */
@media (max-width: 520px) { .postes-bande { padding: 14px 16px; } }
.pb-duo > b { color: var(--ink); font-weight: 800; font-size: 14px;
              font-variant-numeric: tabular-nums; white-space: nowrap;
              text-align: right; }

/* 🔴 29/08 (Julien) : « quand je passe sur Ce mois-ci et Tout, les colonnes se
   décalent légèrement ». Mesuré : les 9 colonnes bougeaient, jusqu'à 151px sur
   Client. Une table en `table-layout: auto` calcule ses largeurs d'après le
   contenu AFFICHÉ : changer de filtre change le contenu, donc les largeurs.
   Les largeurs sont désormais posées sur les en-têtes et la table les respecte
   à la lettre ; seule la colonne Client absorbe le reste, avec une ellipse. */
table:has(#facturesTable) { table-layout: fixed; }
#facturesTable td,
table:has(#facturesTable) th { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 🔴 01/09 (Julien) : le compteur en rouge, ici comme sur les clients. Mais
   une étiquette CHOISIE a un fond plein (rouge, vert, ambre, bleu selon la
   famille) : le rouge y serait invisible sur rouge. Elle garde donc le blanc
   de son texte, un cran en dessous pour rester un compteur. */
.chip-f.on b { opacity: 1; }
.kpi .label { color: var(--muted); font-weight: 600; font-size: 17px; line-height: 1.3; }
.kpi .value {
  font-size: 37px;
  font-weight: 800;
  letter-spacing: -1.2px;
  line-height: 1.15;
  margin-top: 2px;
  display: flex; align-items: baseline; gap: 7px;
}
.kpi .value small { font-size: 21px; color: var(--muted); font-weight: 700; letter-spacing: 0; }
.kpi .value .dot {
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--green); align-self: center;
}

/* ---------- Grilles ---------- */

.grid-1 { display: grid; grid-template-columns: 1fr; gap: 18px; }
.grid-2 { display: grid; grid-template-columns: 1fr 1.35fr; gap: 18px; }
.grid-2eq { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
/* 🔴 28/08 (Julien, fiche BEAUTE TOUTOU) : chez un client à l'historique long,
   la carte « Derniers SMS » s'étirait à la hauteur du tableau voisin et
   affichait une bulle perdue dans un grand vide. Chaque carte de cette
   rangée garde la hauteur de SON contenu. */
.grid-2eq > .card { align-self: start; }
/* sauf quand on veut justement que la carte descende jusqu'au bas de la
   colonne voisine, pour que la rangée finisse alignée */
.grid-2eq > .card.etire { align-self: stretch; }
/* 🔴 29/08 (Julien) : « attention à l'espace vide entre les blocs, il faut les
   aligner ». Sur les STATISTIQUES, toutes les cartes d'une rangée portent un
   graphe : leurs hauteurs de contenu diffèrent (un SVG a une hauteur fixe, une
   liste de règlements dépend du nombre de modes), et le `align-self: start`
   ci-dessus laissait la plus courte flotter au-dessus d'un trou. Ici les bords
   priment : les cartes d'une même rangée descendent ensemble, et le contenu
   court se centre au lieu de rester collé en haut.
   ⚠️ On ne touche PAS la fiche client, où le start reste voulu (une bulle de
   SMS perdue dans un grand vide, retour Julien du 28/08). */
[data-page="stats"] .grid-2eq > .card { align-self: stretch; }
[data-page="stats"] .grid-2eq > .card.block {
  display: flex; flex-direction: column;
}
[data-page="stats"] .grid-2eq > .card.block > .block-head { flex: none; }
[data-page="stats"] .grid-2eq > .card.block > *:not(.block-head) {
  flex: 1; display: flex; flex-direction: column; justify-content: center;
}
/* la colonne de droite de la dernière rangée empile ses propres cartes :
   elles gardent chacune leur hauteur, c'est la PILE qui s'aligne */
[data-page="stats"] .grid-2eq > div > .card { align-self: auto; }
.grid-1 > *, .grid-2 > *, .grid-2eq > *, .kpis > *, .robots-grid > * { min-width: 0; }

/* ---------- Blocs communs ---------- */

.block { padding: 24px 26px; }
.block-head {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px; margin-bottom: 16px;
}
.block-head h2 {
  font-size: 19.5px; font-weight: 800; letter-spacing: -.4px;
  display: flex; align-items: center; gap: 10px;
}
.block-head h2 img { width: 22px; height: 22px; }

.link-more {
  color: var(--red); font-weight: 700; font-size: 14.5px;
  display: inline-flex; align-items: center; gap: 5px;
  border: 1px solid var(--border);
  min-height: 40px; padding: 0 14px; border-radius: var(--r);
  background: var(--card);
}
.link-more:hover { background: var(--red-soft); border-color: #F6D5D3; }
.link-more.bare { border: none; padding: 0; background: none; }
/* 07/09 : sous le tableau des meilleurs clients, la note sur les HT estimés */
.tc-note { color: var(--muted); font-size: 13px; font-weight: 600; margin-top: 12px; }
.tc-est { color: var(--orange); font-weight: 700; cursor: help; }

/* 22/09 : les 15 meilleurs produits, quantités facturées sur 3 mois.
   Julien, quatre retouches le même jour : « une couleur en vertical sous les
   mois » (fond de colonne), « juste la largeur du mois, plus petit »
   (pastille), « une colonne unie, pas plein de petites bulles » (bande
   continue), et enfin « pas de fond coloré, juste les chiffres en couleur,
   ce n'est pas très lisible, et une barre fine entre les colonnes de
   chiffres ». État retenu : AUCUN fond, le nom du mois et ses chiffres dans
   la couleur du mois (orange, vert, bleu de la bande des postes), le Total
   en rouge, et un filet vertical fin (--border) devant chaque colonne de
   chiffres. La coquille .tp-q reste (le JS écrit le mois dedans). */
.tp-table td:first-child { font-weight: 700; }
.tp-q { display: inline; font-variant-numeric: tabular-nums; }
.tp-table th.tp-c0, .tp-table th.tp-c1, .tp-table th.tp-c2, .tp-table th.tp-total,
.tp-table td.tp-c0, .tp-table td.tp-c1, .tp-table td.tp-c2, .tp-table td.tp-total { border-left: 1px solid var(--border); }
.tp-c0 .tp-q { color: var(--orange-txt); }
.tp-c1 .tp-q { color: #1F7A43; }
.tp-c2 .tp-q { color: #1A5EA8; }
.tp-total .tp-q { color: var(--red); font-weight: 800; }
.tp-rang {
  display: inline-block; min-width: 26px; margin-right: 4px;
  color: var(--muted); font-weight: 700; font-variant-numeric: tabular-nums;
}
.tp-table td.num { font-variant-numeric: tabular-nums; }
.tp-table td.tp-zero .tp-q { color: var(--muted); font-weight: 500; }
.tp-table td.tp-total { font-weight: 800; }
@media (max-width: 520px) {
  /* cinq colonnes ne tiennent pas sur un téléphone (mesuré à 320 px : le
     tableau débordait de 29 px et un nom prenait 105 px de haut). Chaque
     produit devient une CARTE, comme Clients et Factures : le nom sur sa
     ligne, les quatre chiffres dessous, chacun coiffé de son mois (data-m,
     posé par app.js). L'en-tête disparaît, il est dans les cellules. */
  .tp-table thead { display: none; }
  .tp-table, .tp-table tbody { display: block; width: 100%; }
  .tp-table tr { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0 6px; padding: 11px 0; border-bottom: 1px solid var(--border); }
  .tp-table tr:last-child { border-bottom: none; }
  .tp-table tr.attente-tr { display: block; }
  .tp-table td { display: block; padding: 0; border: none; }
  .tp-table td:first-child { grid-column: 1 / -1; white-space: normal; font-size: 14px; line-height: 1.3; margin-bottom: 7px; overflow-wrap: anywhere; }
  /* en carte, chaque chiffre garde sa couleur sous son mois ; le filet
     vertical sépare les quatre cases, sauf devant la première */
  .tp-table td.num { text-align: left; font-size: 15px; padding-left: 8px; }
  .tp-table td.tp-c0 { border-left: none; padding-left: 0; }
  .tp-table td.num::before {
    content: attr(data-m); display: block;
    font-size: 11.5px; font-weight: 700; color: var(--muted);
  }
  .tp-table td.tp-c0::before { color: var(--orange-txt); }
  .tp-table td.tp-c1::before { color: #1F7A43; }
  .tp-table td.tp-c2::before { color: #1A5EA8; }
  .tp-table td.tp-total::before { color: var(--red);
    text-transform: uppercase; letter-spacing: .3px;
  }
  .tp-rang { min-width: 22px; margin-right: 2px; }
}

/* ---------- Boutons ---------- */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  background: var(--red); color: #fff;
  font-weight: 700; font-size: 16px;
  min-height: var(--ctrl-lg); padding: 0 26px;
  border-radius: var(--r);
  text-align: center;
  transition: background .12s ease;
}
.btn:hover { background: var(--red-dark); }
.btn img { width: 18px; height: 18px; }
.btn.ghost {
  background: var(--card); color: var(--red);
  border: 1.5px solid #F3C9C6;
}
.btn.ghost:hover { background: var(--red-soft); }
.btn.small { min-height: var(--ctrl); padding: 0 18px; font-size: 14.5px; border-radius: var(--r); }
/* Bouton SANS icône : l'encre du texte tombait 1,2 px trop bas (le centrage
   flex compte l'espace des jambages). Avec une icône, l'équilibre est déjà
   bon : elle dépasse des capitales et remplit ce blanc, on n'y touche pas.
   Mesuré par test-centrage-vertical le 28/08. */
.btn.small:not(:has(img)) { padding-bottom: 1.2px; }
/* 🔴 28/08 (Julien) : « le plus devrait être plus gros et plus au milieu ».
   Un « + » est centré sur l'axe mathématique de la police (mesuré à 0,2844 em
   au-dessus de la baseline), là où l'œil attend le centre des CAPITALES
   (0,365 em) : à la taille du texte il tombe 1 px trop bas. Le grossir à
   1,25 em le recentre TOUT SEUL (0,2844 × 1,25 = 0,3555 em), sans décalage
   fragile à maintenir. line-height 0 pour qu'il n'agrandisse pas le bouton. */
.btn .plus { font-size: 1.25em; line-height: 0; font-weight: 800;
  /* Dans un bouton flex, le span du « + » est centré par sa BOÎTE, pas par sa
     baseline : le glyphe retombait 1,45 px trop bas (mesuré, retour Julien
     28/08 x2). On remonte de la valeur mesurée. */
  position: relative; top: -1.45px; }

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

.table-wrap { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; }
th {
  text-align: left; color: var(--muted);
  font-size: 13.5px; font-weight: 700;
  padding: 10px 14px; border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
td {
  padding: 14px; border-bottom: 1px solid var(--border);
  font-weight: 600; font-size: 15px;
  vertical-align: middle;
}
tr:last-child td { border-bottom: none; }
td.num, th.num { text-align: right; }
tr.rowlink { cursor: pointer; }
tr.rowlink:hover td { background: #FCFAF7; }

.avatar-ini {
  width: 40px; height: 40px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-weight: 800; font-size: 14px; color: #fff;
  flex-shrink: 0;
}
.av-1 { background: #D9776B; }
.av-2 { background: #C9A03C; }
.av-3 { background: #7B9E6B; }
.av-4 { background: #6B87A8; }
.av-5 { background: #A87BA0; }
.av-6 { background: #B4846B; }

/* Photos de profil dessinees (homme / femme / boutique), meme style que la
   mascotte chien/chat. Le disque garde la teinte du client, en version pale :
   la ligne reste reperable a la couleur, et l'illustration se detache dessus. */
.avatar-ini.av-img {
  background-size: cover; background-position: center;
  background-repeat: no-repeat;
  font-size: 0; color: transparent;
}
.av-img.av-1 { background-color: #F6DCD7; }
.av-img.av-2 { background-color: #F5E7C6; }
.av-img.av-3 { background-color: #E1EAD9; }
.av-img.av-4 { background-color: #DCE4EE; }
.av-img.av-5 { background-color: #EBDCE8; }
.av-img.av-6 { background-color: #EEDFD6; }
.avp-homme    { background-image: url(img/av-homme.webp); }
.avp-femme    { background-image: url(img/av-femme.webp); }
.avp-boutique { background-image: url(img/av-boutique.webp); }

/* Factures : HT et TVA. Un tiret pour les factures d'avant le système maison,
   qui n'ont pas de ventilation. Sous 900 px, ces deux colonnes s'effacent :
   le tableau tient déjà tout juste, et le TTC reste le chiffre qu'on cherche
   en mobilité (le détail est dans l'État de TVA et dans le PDF). */
.vide { color: var(--muted); opacity: .7; }
/* 🔴 28/08 (Julien) : « il faut préciser HT et TTC ». Une mention discrète
   collée au libellé, pas au chiffre : le chiffre reste le héros de la carte. */
/* 🔴 29/08 (Julien, 2 passages) : « les TTC doivent être à côté du chiffre,
   pas au dessus », puis « le ttc juste après € et même couleur que les
   chiffres ». L'unité n'est plus une pastille beige au bout de la ligne :
   elle est DANS .value, collée au symbole, dans la teinte du nombre. Elle est
   donc réécrite par chaque rendu (helper uni() de app.js), puisque tous
   réécrivent l'innerHTML de .value.
   ⚠️ Deux règles à battre : .kpi .value small la met en gris, et
   .value.c-* small la passe à 50 % d'opacité. */
/* 07/09 (lot B) : le ≈ d'un HT estimé (pièces EBP sans ventilation) */
.kpi .value small.est { font-size: 20px; color: var(--orange); opacity: 1; font-weight: 800; }
.kpi .value small.unite {
  font-size: 15px; font-weight: 800; letter-spacing: .3px;
  color: inherit; opacity: 1;
  /* 🔴 29/08 (Julien) : « l'écart entre € et TTC est trop grand ». Mesuré :
     12px entre les deux boîtes, dont 5 seulement venaient de cette marge, les
     7 autres étant la chasse à droite du glyphe €. Une marge négative annule
     une partie de cette chasse et ramène l'écart réel à 5px. */
  margin-left: -2px;
}
/* Une teinte par NATURE de montant (Julien 28/08 : « mets des couleurs aux
   différents prix ») : HT en bleu ardoise, TVA en ambre, TTC en rouge JBC,
   chacun sur son fond doux. Trois colonnes de chiffres identiques se
   confondaient, la couleur les identifie avant même de lire l'en-tête. */
.mnt { display: inline-block; padding: 3px 9px; border-radius: 8px;
       font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
.mnt-ht  { color: #2F6F8F; background: #EAF2FB; }
.mnt-tva { color: var(--orange-txt); background: #FBF2E2; }
.mnt-ttc { color: var(--red-dark); background: var(--red-soft); font-weight: 800; }
/* 🔴 29/08 (Julien) : « une colonne vendeur aussi ici, avec une couleur ».
   Un poste = une teinte, la même dans le tableau, dans le graphe du CA HT et
   dans le sélecteur Notion. Habit de la pastille de montant, pas de la
   pastille de statut : c'est un QUI, pas un état. */
.vend { display: inline-block; padding: 3px 10px; border-radius: 8px;
        font-weight: 700; font-size: 14px; white-space: nowrap;
        color: var(--muted); background: var(--cream-deep); }
/* 🔴 07/09 (Fabien) : « j'aurais bien voulu avoir aussi le type de règlement,
   si c'est fait par virement, par carte bancaire, par chèque ». Même habit que
   la pastille vendeur : c'est un COMMENT, pas un état (le badge de statut) ni
   un montant (la pastille .mnt). La teinte vient du mode et reprend celle du
   graphe des règlements des Statistiques, posée en style en ligne par
   pastilleReglement().
   Le plus long libellé de la base (« Prélèvement sur salaire », 9 pièces sur
   7 990) dépasse la colonne : la pastille le coupe proprement et le garde en
   entier dans son infobulle. La hauteur est celle des autres pastilles de ce
   tableau (34 px) pour que la ligne reste à 58 px, mais en inline-block :
   l'inline-flex des voisines empêcherait l'ellipse. */
.regl { display: inline-flex; align-items: center; justify-content: center;
        box-sizing: border-box; max-width: 100%; height: 34px; padding: 0 10px;
        border-radius: 8px; font-weight: 700; font-size: 14px; line-height: 1;
        color: var(--muted); background: var(--cream-deep); }
/* l'ellipse est portée par un ENFANT : un texte nu dans un flex est un item
   anonyme, il ne se tronque pas (leçon du 01/09 sur la fiche client). */
.regl > span { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* les deux cellules qui se corrigent au clic le disent au survol */
td.fcell { cursor: pointer; }
td.fcell:hover .vend, td.fcell:hover .badge { outline: 2px solid var(--border); outline-offset: 2px; }
/* 🔴 01/09 (Julien) : « quand je clique pour éditer ça fait bouger la ligne,
   j'aimerais que ça soit fixe ». Le menu d'édition est plus haut que la
   pastille qu'il remplace, donc la ligne grandissait et tout le tableau
   descendait d'un cran sous les yeux. Première réponse : la ligne RÉSERVE
   58 px, la hauteur du plus grand des deux états.
   🔴 01/09 (Julien, 2e retour) : « quand je choisis le statut reçu ou le
   vendeur, le sélecteur fait légèrement bouger la hauteur de la ligne ».
   MESURÉ au banc (maquette/_banc-hauteur-ligne.html, Chrome) : la réserve de
   58 px n'était PAS le plus grand des deux états. Au repos une ligne fait
   60,25 px, parce que l'émoji du badge tire la boîte de ligne vers le haut,
   et pas du même nombre de pixels d'un émoji à l'autre (60,25 avec ✅, 59,75
   avec ⚠️). Le menu d'édition, lui, tient dans 42,5 px : la ligne RETOMBAIT
   donc sur les 58 px réservés, et ce sont ces 2,25 px que Julien voyait, à
   l'ouverture du menu comme au « … » d'attente qui suit le choix.
   On ne réserve plus une hauteur en pariant qu'elle est la plus grande : on
   RETIRE la variable. Tout ce qui peut occuper une cellule (pastille vendeur,
   badge de statut, tiret des cellules vides, menu maison, « … » d'attente)
   tient dans la MÊME boîte de 34 px, centrée, et sort du calcul de la ligne
   de base (« vertical-align: middle ») pour que la métrique de l'émoji n'ait
   plus de prise. Mesuré : les 4 états (repos, statut ouvert, vendeur ouvert,
   attente) donnent 59,00 px sur TOUTES les lignes, au centième près. */
table:has(#facturesTable) tr { height: 58px; }
#facturesTable .badge, #facturesTable .vend, #facturesTable .regl, #facturesTable .vide,
#facturesTable .fi-attente, #facturesTable .sel-jbc, #facturesTable .mnt {
  vertical-align: middle;
}
#facturesTable .badge, #facturesTable .vend, #facturesTable .regl, #facturesTable .vide,
#facturesTable .fi-attente {
  box-sizing: border-box; height: 34px;
  display: inline-flex; align-items: center; justify-content: center;
  line-height: 1; padding-top: 0; padding-bottom: 0;
}
#facturesTable .sel-jbc {
  box-sizing: border-box; height: 34px; line-height: 1;
  padding-top: 0; padding-bottom: 0;
}
#facturesTable td, #facturesTable td.fcell.edit { padding-top: 12px; padding-bottom: 12px; }
td.fcell .sel-jbc { padding-left: 11px; padding-right: 11px; font-size: 15px; }
td.fcell select.fi-in { width: 100%; min-width: 132px; font-size: 16px; }
/* le nom du client ouvre SA fiche, alors que la ligne ouvre la facture */
a.lien-fiche { text-decoration: underline; text-decoration-color: var(--border);
               text-underline-offset: 3px; }
a.lien-fiche:hover { text-decoration-color: var(--red); color: var(--red); }
/* et dans le parcours du client, la ligne d'une facture ouvre son PDF */
a.txt.lien-pdf { cursor: pointer; }
a.txt.lien-pdf:hover { color: var(--red); }

/* légende du graphe « CA HT par poste » */
.pl-leg { display: flex; flex-wrap: wrap; gap: 8px 20px; justify-content: center;
          margin-top: 6px; }
.pl-l { display: inline-flex; align-items: center; gap: 8px;
        font-size: 14px; font-weight: 600; color: var(--muted); }
.pl-l i { width: 12px; height: 12px; border-radius: 4px; flex: none; }
.pl-l b { color: var(--ink); font-weight: 800; }
.chart-tip .pl-tot { margin-top: 4px; padding-top: 4px; border-top: 1px solid rgba(255,255,255,.25); }
.chart-tip i { display: inline-block; width: 9px; height: 9px; border-radius: 3px;
               margin-right: 7px; vertical-align: baseline; }

@media (max-width: 900px) {
  .col-ht, .col-tva { display: none; }
}
/* 🔴 29/08 (Julien) : « l'écart entre la colonne Vendeur et Montant est trop
   proche, du coup Vendeur est trop éloigné du Statut ». Le montant TTC est
   calé à DROITE de sa colonne et le vendeur à GAUCHE de la sienne : les deux
   se touchaient, pendant que le statut, calé à droite, restait seul au bout.
   Le vendeur se cale à droite comme ses voisines : il s'écarte du montant et
   se rapproche du statut. */
.col-vend { text-align: right; }
/* 🔴 07/09 : le règlement se cale à droite comme le vendeur et le statut qui
   l'encadrent. Calé à gauche, il serait venu toucher le vendeur, l'écart que
   Julien avait fait corriger le 29/08 entre Montant et Vendeur. */
.col-regl { text-align: right; }
/* sur petit écran, le vendeur passe derrière les montants : le statut et le
   montant TTC priment quand la largeur manque.
   🔴 07/09 : le règlement se retire au MÊME palier que les montants HT et TVA
   (900 px), pas plus tôt. Mesuré au banc : c'est le palier qui laisse la bande
   761-900 px exactement aussi confortable qu'avant la colonne. En dessous de
   520 px la ligne devient une carte et le règlement y reprend sa place. */
@media (max-width: 900px) {
  .col-regl { display: none; }
}
@media (max-width: 760px) {
  .col-vend { display: none; }
}

/* 🔴 29/08 (Julien) : le mini-menu au clic sur une facture, et l'écran d'avoir.
   Deux modales maison (jamais de pop-up navigateur, règle du 28/08). */
.dlg-fact, .dlg-avoir { max-width: 460px; text-align: left; }
.dlg-fact h3, .dlg-avoir h3 { text-align: center; }
.dlg-sous { text-align: center; color: var(--muted); font-weight: 700;
            font-size: 14.5px; margin-bottom: 18px; }
.dlg-choix { display: flex; flex-direction: column; gap: 10px; margin-bottom: 18px; }
.dlg-c { display: block; width: 100%; text-align: left; padding: 14px 16px;
         border: 1px solid var(--border); border-radius: 12px; background: var(--card);
         font: inherit; cursor: pointer; }
.dlg-c:hover { border-color: var(--red); background: var(--red-softer); }
.dlg-c b { display: flex; align-items: center; gap: 8px;
           font-size: 15.5px; font-weight: 800; margin-bottom: 3px; }
/* l'icône Tabler du bouton, alignée sur la hauteur de capitale du titre */
.dlg-c .dlg-ic { width: 18px; height: 18px; flex-shrink: 0; }
.dlg-c small { display: block; color: var(--muted); font-weight: 600;
               font-size: 13px; line-height: 1.4; }
/* la règle, dite avant d'agir : c'est elle qui évite le « pourquoi je peux pas
   juste modifier ? » */
.av-regle { background: var(--cream-deep); border-radius: 12px; padding: 13px 15px;
            font-size: 13.5px; font-weight: 600; line-height: 1.5; color: var(--ink);
            margin-bottom: 16px; }
.av-choix { display: flex; flex-direction: column; gap: 8px; margin-bottom: 14px; }
.av-r { display: flex; align-items: flex-start; gap: 11px; padding: 12px 14px;
        border: 1px solid var(--border); border-radius: 12px; cursor: pointer; }
.av-r:has(input:checked) { border-color: var(--red); background: var(--red-softer); }
.av-r input { margin-top: 3px; accent-color: var(--red); }
.av-r b { display: block; font-size: 15px; font-weight: 800; }
.av-r small { display: block; color: var(--muted); font-weight: 600; font-size: 12.5px; }
/* 07/09 : rendre PAR PRODUITS, les lignes de la facture avec la quantité rendue */
.av-lignes { margin: 6px 0 10px; }
.av-lignes-t { font-size: 13.5px; font-weight: 700; margin-bottom: 4px; }
.av-attente { color: var(--muted); font-size: 13.5px; font-weight: 600; }
.av-l { display: grid; grid-template-columns: minmax(0, 1fr) 68px 78px; gap: 8px; align-items: center;
        padding: 7px 0; border-top: 1px solid var(--border); font-size: 13.5px; font-weight: 700; }
.av-l small { display: block; color: var(--muted); font-weight: 600; font-size: 12.5px; }
.av-l input { width: 100%; text-align: center; font: inherit; font-size: 16px; padding: 6px 4px;
              border: 1.5px solid var(--border); border-radius: 9px; background: #FFFFFF; }
.av-l-mt { text-align: right; font-variant-numeric: tabular-nums; }
.av-total { display: flex; justify-content: space-between; padding-top: 8px; border-top: 1.5px solid var(--ink);
            font-size: 14px; font-weight: 800; }
.av-libre { margin-top: 6px; }
.av-ap-l { margin: 6px 0 0 18px; font-size: 13px; font-weight: 600; }
.av-mt { margin-bottom: 14px; }
.av-mt label { display: block; font-size: 13px; font-weight: 700; color: var(--muted);
               margin-bottom: 5px; }
.av-mt input, .dlg-avoir .form-field input {
  width: 100%; font: inherit; font-size: 16px; padding: 11px 13px;
  border: 1px solid var(--border); border-radius: 10px; background: #FFFFFF;
}
.av-apercu { margin: 4px 0 16px; padding: 13px 15px; border-radius: 12px;
             background: var(--cream-deep); color: var(--muted);
             font-size: 13.5px; font-weight: 600; line-height: 1.5; }
.av-apercu.pret { background: var(--red-soft); color: var(--red-dark); }
.av-apercu.ok { background: var(--green-soft); color: #187A42; }
.av-apercu.ko { background: var(--orange-soft); color: var(--orange-txt); }

/* porteur de valeur, pas un accent typographique : il ne doit pas ressortir */
#robotsNb { font-weight: inherit; }

.badge {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 13.5px; font-weight: 700;
  padding: 5.5px 12px; border-radius: 20px;
  white-space: nowrap;
}
.badge.ok { background: var(--green-soft); color: #187A42; }
.badge.warn { background: var(--orange-soft); color: var(--orange-txt); }
.badge.off { background: var(--cream-deep); color: var(--muted); }
.badge.red { background: var(--red-soft); color: var(--red); }
/* « N réponses arrivées aujourd'hui » à côté du titre Récap du jour : même
   habit que la pastille de date (fond blanc, cadre, ombre douce), texte rouge
   (Julien 27/08). Le point .pt suit en rouge via currentColor. */
/* 27/08 (2e retour Julien) : fond rouge plein, texte blanc, comme le bouton
   d'action. Le point .pt suit en blanc via currentColor. Contraste 4,9:1 = AA. */
.badge.jour { background: var(--red); color: #fff; }
/* …et il vit juste À CÔTÉ du titre, pas à l'autre bout du bandeau
   (« c'est toujours au même endroit »), avec de l'air avant les capsules
   de comptage du dessous (« c'est un peu trop collé »). */
.block-head.recap-head { justify-content: flex-start; gap: 14px; margin-bottom: 28px; }
.badge .pt { width: 8px; height: 8px; border-radius: 50%; background: currentColor; }
/* 🔴 01/09 (Julien) : « ça doit pas dépasser de la carte ». Une pastille est
   en white-space:nowrap par défaut, ce qui est juste pour « ✓ Livrée » mais
   faux pour une phrase de provenance (« EBP jusqu'à juillet 2026 · Cockpit
   depuis août 2026 ») : elle sortait de la carte au lieu de se replier.
   .source lui rend le droit de passer à la ligne, et la borne à la largeur
   disponible. Le texte, lui, n'offre qu'un seul point de coupure (le « · ») :
   chaque moitié est montée en espaces insécables côté JS, donc la pastille
   coupe à la frontière de sens et jamais au milieu d'une date. */
.badge.source { white-space: normal; max-width: 100%; text-align: center; line-height: 1.35; }

/* ---------- Accueil : tir de demain ---------- */

.tir-card {
  padding: 30px 28px;
  text-align: center;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
}
.tir-card .chip-big {
  width: 84px; height: 84px; border-radius: 50%;
  background: var(--red-soft);
  display: flex; align-items: center; justify-content: center;
  margin-bottom: 16px;
}
.tir-card .chip-big img { width: 40px; height: 40px; }
.tir-card .t1 { font-size: 19px; font-weight: 700; color: var(--ink); }
.tir-card .t2 {
  font-size: 42px; font-weight: 800; color: var(--red);
  letter-spacing: -1.5px; line-height: 1.1; margin: 2px 0 0;
}
.tir-card .t3 { font-size: 21px; font-weight: 700; line-height: 1.4; margin-bottom: 20px; }
.tir-card .btn { width: 100%; max-width: 320px; }
.tir-card .done-note { margin-top: 14px; }

/* ---------- Accueil : la carte du tir logée dans la rangée des chiffres ----------
   Même hiérarchie que ses voisines, mise à l'échelle de la 4e case, et surtout
   AUSSI COURTE QU'ELLES : la grosse pastille a sauté (l'icône est passée en ligne
   devant la date) et le compte à rebours tient sur une pastille d'une ligne.
   Julien 26/08 : « les nombres flottent dans les cartes trop grandes ». */
.tir-kpi {
  padding: 22px 22px 20px;
  /* 29/08 (Julien) : « mets ça au milieu des deux infos » : le titre reste en
     haut, la pastille du compte à rebours en bas, et le bloc date + état se
     centre dans l'espace entre les deux (marges élastiques sur t2/t3). */
  justify-content: flex-start;
  border: 1.5px solid var(--red);
  box-shadow: 0 1px 2px rgba(227, 6, 19, .06), 0 8px 22px rgba(227, 6, 19, .10);
}
.tir-kpi .t1 {
  display: flex; align-items: center; justify-content: center; gap: 7px;
  font-size: 14.5px; color: var(--muted); font-weight: 600; line-height: 1.3;
}
.tir-kpi .t1 img { width: 17px; height: 17px; }
.tir-kpi .t2 { font-size: 34px; letter-spacing: -1.2px; margin-top: auto; line-height: 1.1; }
.tir-kpi .t2 small { font-size: 19px; color: var(--muted); font-weight: 700; letter-spacing: 0; }
/* 29/08 (Julien) : le 9h00 en orange, comme les blocs de Factures */
.tir-kpi .t2.t2-date small { color: var(--orange); }
.tir-kpi .t3 { font-size: 14px; margin-top: 3px; margin-bottom: auto; line-height: 1.45; }
/* quand le tir est parti, la carte annonce une DATE : elle se lit plus petite
   qu'un compteur, et la ligne d'explication a besoin d'air (Julien 28/08) */
.tir-kpi .t2.t2-date { font-size: 25px; letter-spacing: -.5px; margin-bottom: 14px; }
.tir-kpi .t2.t2-date small { font-size: 17px; }
.tir-kpi .t3.doux { color: var(--muted); font-weight: 600; }
/* les quatre lignes de la carte respirent : titre, date, état, explication
   (Julien 28/08 : « aère un peu », « le texte est trop collé ») */
.tir-kpi .t1 { margin-bottom: 8px; }
.tir-kpi .t2.t2-date + .t3 { line-height: 1.55; }
.tir-kpi .t3 .t3-douce {
  display: block; margin-top: 11px;
  color: var(--muted); font-weight: 600; font-size: 13.5px; line-height: 1.6;
}
/* 29/08 : 8px = la marge fixe sous le titre (t1). Les marges élastiques de
   t2/t3 se partagent le reste également : le bloc date+état est donc centré
   au pixel entre le titre et le compte à rebours. */
.tir-kpi .t2.t2-date ~ .hz-pill { margin-top: 8px; }
/* 🔴 01/09 (Julien) : « descends "Prochain tir SMS" et un peu les 2 autres
   lignes aussi, mais Prochain tir SMS doit être plus proche quand même de la
   date de tir ». Depuis le 29/08, le titre restait collé en haut de la carte et
   c'est le bloc date+état qui se centrait : l'espace élastique tombait donc
   ENTRE le titre et la date, les éloignant l'un de l'autre. On déplace
   l'élastique AU-DESSUS du titre : les trois lignes descendent ensemble, et
   l'écart titre → date redevient une marge fixe, serrée. */
.tir-kpi .t1 { margin-top: auto; margin-bottom: 9px; }
.tir-kpi .t2 { margin-top: 0; }
/* 🔴 01/09 (Julien) : « descends un peu le texte, trop collé au compteur (qui
   est bien placé) ». Le compte à rebours est en coin absolu, donc hors flux :
   il ne pousse rien, et le titre démarrait sous lui. La pastille mesure
   14 px de haut + 8 px de padding + 14 px du bord = 36 px : on réserve cette
   hauteur en haut de carte, plus 10 px d'air. */
.kpi.tir-kpi:has(.hz-pill.kpi-coin) { padding-top: 46px; }
.tir-kpi .btn { max-width: none; padding: 11px 14px; font-size: 14.5px; }
.tir-kpi .btn.est-valide, .tir-kpi .btn.est-valide:hover { background: var(--green); }
.tir-kpi .done-note { margin-top: 10px; font-size: 13px; }

/* le compte à rebours du départ des SMS : une pastille, une ligne, pas de trait */
.hz-pill {
  margin-top: 10px;
  display: inline-flex; align-items: center;
  padding: 4px 9px 4px 7px; border-radius: 9px;
  background: var(--red-soft);
}
.hz-pill:empty { display: none; }
.hz-pill .hz { gap: 6px; }
.hz-pill .hz-dial { width: 20px; height: 20px; }
.hz-pill .hz-bg { stroke: #F6D2CF; }
.hz-pill .hz-txt b { font-size: 12.5px; color: var(--red-dark); }
/* 🔴 28/08 (Julien, deux fois) : « toujours trop haut sur le fond rouge clair,
   descends le texte ». align-items:center centre la BOÎTE DE LIGNE, qui compte
   l'espace des jambages sous la baseline : l'encre remonte donc de ~0,94 px, le rattrapage vaut donc la MOITIÉ (0,47)
   (mesuré sur les vraies données, harnais test-pastille-horloge.html). On
   descend le TEXTE seul : le cadran, lui, est déjà centré au quart de pixel,
   et toucher au padding les déplacerait tous les deux. */
.hz-pill .hz-txt { position: relative; top: 0.47px; }
.hz-pill .hz.proche .hz-txt b { color: var(--red); }

/* ---------- Listes de livraisons ---------- */

.deliv-row td:first-child { display: flex; align-items: center; gap: 12px; }
.paw-mini { width: 18px; height: 18px; opacity: .8; }

/* ---------- Recherche ---------- */

.search {
  display: flex; align-items: center; gap: 10px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 13px;
  padding: 0 16px;
  flex: 1;
  max-width: 420px;
}
.search input {
  border: none; outline: none; background: none;
  font-family: inherit; font-size: 16px; font-weight: 600;
  padding: 13px 0; width: 100%;
  color: var(--ink);
}
.search input::placeholder { color: var(--soft-txt); font-weight: 500; }
.search-stock { max-width: 300px; }
/* 11/09 (B5) : le tri compact des listes, visible seulement quand l'en-tête
   du tableau est masqué (sous 520 px, voir la bascule en fin de feuille) */
.tri-mobile { display: none; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 12px; }
.tri-mobile-lib { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 700; color: var(--muted); }
.tri-sel { font: inherit; font-size: 16px; font-weight: 600; color: var(--ink); background: var(--card);
  border: 1px solid var(--border); border-radius: 11px; padding: 0 12px; height: 44px; }
.tri-sens { width: 44px; height: 44px; border: 1px solid var(--border); border-radius: 11px; background: var(--card);
  color: var(--red); font-size: 18px; font-weight: 700; cursor: pointer; }
.sel-tout-m { display: inline-flex; align-items: center; gap: 8px; min-height: 44px; margin-left: auto;
  font-size: 13px; font-weight: 700; color: var(--muted); cursor: pointer; }
.sel-tout-m input { width: 18px; height: 18px; accent-color: var(--red); }

/* 11/09 (B12) : un bouton qui replie un bloc de la fiche, téléphone seulement.
   (Le repli des cumuls de l'accueil est parti le 15/09 : Julien veut l'accueil
   du téléphone dans l'ordre du bureau.) */
.bloc-toggle { display: none; }
.bloc-toggle {
  min-height: 36px; padding: 0 12px; border: 1px solid var(--border); border-radius: var(--r);
  background: var(--card); font: inherit; font-size: 13px; font-weight: 700; color: var(--red); cursor: pointer;
}
/* 11/09 (B12) : les trois accès rapides de la fiche, téléphone seulement */
.fiche-acces { display: none; gap: 8px; flex-wrap: wrap; flex: 1 1 100%; }
.fiche-acces a {
  display: inline-flex; align-items: center; min-height: 40px; padding: 0 12px;
  border: 1px solid var(--border); border-radius: var(--r); background: var(--card);
  font-size: 13.5px; font-weight: 700; color: var(--ink);
}
/* 07/09 (Julien) : « border rouge pour la recherche de produit » (page Stock) */
.search.search-stock { border-color: var(--red); }
/* 07/09 (Julien) : « croix rouge aussi » : la croix d'effacement native du champ
   (bleue sur WebKit) est remplacée par la croix Tabler « x » en rouge maison */
.search.search-stock input::-webkit-search-cancel-button {
  -webkit-appearance: none; appearance: none; width: 15px; height: 15px; margin-left: 6px; cursor: pointer;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23E30613' stroke-width='2.9' stroke-linecap='round'%3E%3Cpath d='M18 6L6 18M6 6l12 12'/%3E%3C/svg%3E") no-repeat center / contain;
}
.search img { width: 18px; height: 18px; opacity: .5; }

.filters { display: flex; gap: 8px; flex-wrap: wrap; }
/* 11/09 (B11) : même silhouette que .chip-f (Factures, Stock) : la pilule de
   20 px de rayon était la seule de sa famille */
.fchip {
  border: 1px solid var(--border);
  background: var(--card);
  border-radius: var(--r);
  min-height: 40px; padding: 0 16px;
  display: inline-flex; align-items: center; gap: 7px;
  font: inherit; font-weight: 700; font-size: 14px;
  color: var(--muted); cursor: pointer;
}
.fchip:hover { border-color: var(--red); color: var(--red); }
/* 🔴 01/09 (Julien) : « pas de fond noir, juste un border rouge, ça suffira.
   Fond blanc, texte noir, et puis rouge pour ce qui est entre parenthèses. »
   L'étiquette choisie se reconnaît à sa BORDURE, pas à un aplat : trois
   repères se cumulent sans rien noircir (bordure rouge, texte à l'encre pleine
   au lieu du gris des autres, compteur rouge). Bonus : le compteur garde la
   MÊME couleur, choisie ou non, il n'a plus à s'éclaircir pour rester lisible
   sur un fond sombre. */
/* 🔴 02/09 (Julien) : « quand un tag est sélectionné, je voudrais que ça soit
   un fond rouge clair, un peu, pour mieux le distinguer ». La bordure rouge
   du 01/09 reste, le fond prend le rouge doux de la maison. */
.fchip.active { background: var(--red-soft); color: var(--ink); border-color: var(--red); }
.fchip.active:hover { color: var(--ink); border-color: var(--red); }
/* 🔴 01/09 (Julien) : « tu peux indiquer entre parenthèses le nombre auquel ça
   correspond aussi, comme les factures ». Le compteur se lit sans voler la
   vedette au libellé : même graisse que sur les étiquettes de factures, un
   ton en dessous, et des chiffres à chasse fixe pour que la largeur de
   l'étiquette ne saute pas quand le nombre change (9 -> 10). */
.fchip b { font-weight: 800; color: var(--red); font-variant-numeric: tabular-nums; }


/* 🔴 28/08 (Julien) : « attention au border bleu ». Le cadre bleu du navigateur
   au clic n'a rien à faire dans la DA. On garde un repère au clavier (règle
   d'accessibilité : on ne supprime jamais l'anneau de focus sans le remplacer),
   mais aux couleurs de la maison. */
:focus { outline: none; }
:focus-visible {
  outline: 2px solid var(--red);
  outline-offset: 2px;
  border-radius: 8px;
}

/* ---------- Fiche client ---------- */

.client-hero {
  display: flex; align-items: center; gap: 22px;
  padding: 26px 28px;
  flex-wrap: wrap;
}
.client-hero .avatar-ini { width: 76px; height: 76px; font-size: 26px; }
.client-hero h1 { font-size: 30px; font-weight: 800; letter-spacing: -.8px; }
.client-tags { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 9px; }
.tag {
  display: inline-flex; align-items: center; gap: 7px;
  border: 1px solid var(--border);
  border-radius: 11px;
  padding: 6.5px 12px;
  font-size: 13.5px; font-weight: 700;
  background: #FDFCFA;
}
.tag img { width: 15px; height: 15px; }
/* Une teinte par catégorie d'info (Julien 27/08 : « je mettrais bien des fonds
   de couleurs différents pour les catégories ») : le lieu, le téléphone,
   l'animal, l'ancienneté se reconnaissent d'un coup d'œil. */
.tag.t-lieu   { background: #EAF2FB; border-color: #D6E5F6; color: #1B4E85; }
.tag.t-tel    { background: var(--green-soft); border-color: #CFE9DA; color: #146B3C; }
.tag.t-animal { background: var(--red-soft); border-color: #F8D9D6; color: var(--red-dark); }
.tag.t-depuis { background: var(--orange-soft); border-color: #F5E0BE; color: var(--orange-txt); }
.tag.t-lieu:hover { background: #DFEBFA; }
.client-side {
  margin-left: auto;
  border-left: 1px solid var(--border);
  padding-left: 24px;
  display: flex; flex-direction: column; gap: 12px;
}
.client-side .line { display: flex; align-items: center; gap: 11px; font-weight: 700; font-size: 15px; }
.client-side .line .chip {
  width: 38px; height: 38px; border-radius: 50%;
  background: var(--red-soft);
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}
.client-side .line .chip img { width: 17px; height: 17px; }

.panier-item td:first-child { display: flex; align-items: center; gap: 14px; }
.prod-thumb {
  width: 44px; height: 44px; object-fit: contain;
  background: var(--cream);
  border-radius: 10px;
  padding: 4px;
}
.total-row {
  display: flex; align-items: center; justify-content: space-between;
  padding: 16px 4px 20px;
  font-size: 20px; font-weight: 800;
}
.cta-duo { display: flex; gap: 14px; flex-wrap: wrap; }
.cta-duo .btn { flex: 1; min-width: 220px; }

/* ---------- Statistiques ---------- */

.seg {
  display: inline-flex;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 13px;
  padding: 4px;
  gap: 2px;
}
.seg button {
  padding: 8.5px 20px;
  font-size: 14.5px; font-weight: 700;
  color: var(--muted);
  border-radius: 10px;
}
.seg button.active { background: var(--red); color: #fff; }

.chart-svg { width: 100%; height: auto; display: block; }

.collab { display: flex; flex-direction: column; gap: 20px; }
.collab-row { display: flex; align-items: center; gap: 14px; }
.collab-row .avatar-ini { width: 44px; height: 44px; font-size: 15px; }
.collab-row .meta { flex: 1; min-width: 0; }
.collab-row .meta b { font-size: 15.5px; }
.collab-row .amount { font-weight: 800; font-size: 17px; white-space: nowrap; }
.bar-track {
  height: 8px; border-radius: 6px;
  background: var(--cream-deep);
  margin-top: 8px;
  overflow: hidden;
}
.bar-fill { height: 100%; border-radius: 6px; background: var(--red); }

/* ---------- Robots ---------- */

.robots-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 18px;
}
.robot-card { padding: 20px 22px; }
.robot-card .top {
  display: flex; align-items: center; gap: 13px; margin-bottom: 12px;
}
.robot-card .emoji {
  width: 46px; height: 46px; border-radius: 13px;
  background: var(--cream);
  display: flex; align-items: center; justify-content: center;
  font-size: 23px;
}
.robot-card h3 { font-size: 17px; font-weight: 800; letter-spacing: -.3px; }
.robot-card .role { color: var(--muted); font-size: 13.5px; font-weight: 600; }
.robot-card .foot {
  display: flex; align-items: center; justify-content: space-between;
  margin-top: 12px; padding-top: 12px;
  border-top: 1px solid var(--border);
  font-size: 13.5px; font-weight: 700; color: var(--muted);
}

/* ---------- Stock ---------- */

.upload-zone {
  border: 2px dashed #F0B9B5;
  border-radius: 14px;
  background: #FEFBFA;
  padding: 22px;
  text-align: center;
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  cursor: pointer;
}
.upload-zone img { width: 26px; height: 26px; }
.upload-zone b { font-size: 15.5px; }
.upload-zone small { color: var(--muted); font-size: 13px; }

.pdf-mini {
  width: 38px; height: 38px; border-radius: 9px;
  background: var(--red-soft);
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--red); font-size: 10.5px; font-weight: 800;
}

.fournisseur {
  display: flex; align-items: center; gap: 18px;
  padding: 8px 0 18px;
}
.fournisseur .chip-big {
  width: 74px; height: 74px; border-radius: 50%;
  background: var(--red-soft);
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}
.fournisseur .chip-big img { width: 36px; height: 36px; }
.fournisseur b { font-size: 19px; font-weight: 800; display: block; }
.fournisseur .meta { color: var(--muted); font-weight: 600; font-size: 14.5px; margin-top: 3px; }

/* ---------- Factures ---------- */

.facture-num { font-weight: 800; }

/* ---------- Modale (pattern JBC) ---------- */

.modal-overlay {
  position: fixed; inset: 0;
  background: rgba(23, 21, 15, .5);
  display: none;
  align-items: center; justify-content: center;
  padding: 20px;
  z-index: 100;
}
.modal-overlay.open { display: flex; }
body.modal-open { overflow: hidden; }

.modal__content {
  position: relative;
  background: var(--card);
  border-radius: 20px;
  width: 100%;
  max-width: 520px;
  /* 🔴 01/09 (Julien) : « ce menu est toujours coupé en bas, agrandis-le ».
     Le plafond de 780 px laissait de la place inutilisée sur un grand écran
     alors que la modale débordait déjà. */
  max-height: min(94dvh, 940px);
  overflow: hidden;
  display: flex; flex-direction: column;
  box-shadow: 0 24px 60px rgba(23, 21, 15, .25);
}
.modal__scroll {
  flex: 1 1 auto; min-height: 0;
  overflow-y: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
  padding: 34px 30px 34px;
  overscroll-behavior: contain;
}
.modal__scroll::-webkit-scrollbar { display: none; width: 0; }
/* Le pied FIXE d'une modale : l'action principale n'entre jamais dans la zone
   qui défile, donc elle ne peut pas être coupée, quelle que soit la hauteur du
   contenu. Même principe que la croix de fermeture (posée le 06/08). */
.modal__pied {
  flex: 0 0 auto;
  padding: 16px 30px calc(20px + env(safe-area-inset-bottom));
  border-top: 1px solid var(--border);
  background: var(--card);
  border-radius: 0 0 20px 20px;
}
.modal__pied-info {
  text-align: center; color: var(--muted); font-weight: 600; font-size: 14px;
  margin: 0 0 12px;
}
.modal__pied-info:empty { display: none; }
/* le récapitulatif chiffré d'une modale (« 2 produits = 39 € ») : ambre et un
   cran plus gros que le gris, c'est ce qu'on relit avant de valider (Julien
   01/09). Le bouton, lui, reste rouge plein : c'est l'action. */
.modal__pied-info.ambre { color: var(--orange); font-weight: 800; font-size: 15.5px; }

/* ---- Panier armé sans mode de règlement (Julien, 01/09/2026) ----
   Le Caissier retombe en silence sur « CB au comptant » : l'oubli ne se voit
   nulle part. Il se voit maintenant à deux endroits, sur la fiche et dans la
   modale de validation, avec l'ambre de ce qui réclame un geste. */
.vl-manque {
  margin: 12px 0 0; padding: 10px 12px; border-radius: 12px;
  background: var(--orange-soft); color: #8A5A00;
  font-size: 14px; font-weight: 600; line-height: 1.45;
}
.vl-manque b { font-weight: 800; }
/* sur la fiche, la valeur manquante ne se lit plus comme un « à compléter »
   ordinaire : elle porte le fond ambre en permanence, pas seulement au survol */
.fi-v.manque { background: var(--orange-soft); }
.fi-v.manque i { color: #8A5A00; font-style: normal; font-weight: 800; }

/* ---- « Ta sélection » : une quantité PAR produit (Julien, 01/09/2026) ----
   Elle se pose au-dessus de la liste du catalogue et ne défile pas avec elle :
   ce qu'on a choisi reste visible pendant qu'on cherche le produit suivant. */
.pp-sel {
  display: flex; flex-direction: column; gap: 6px;
  margin: 0 0 12px; padding: 10px; border-radius: 14px;
  background: var(--cream-deep); border: 1px solid var(--border);
}
.pp-sel-t {
  font-size: 12.5px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase;
  color: var(--red); margin-bottom: 2px;
}
.pp-sel-l {
  display: flex; align-items: center; gap: 10px;
  background: #FFFFFF; border: 1px solid var(--border); border-radius: 11px;
  padding: 7px 9px;
}
.pp-sel-nom { flex: 1 1 auto; min-width: 0; font-weight: 700; font-size: 14.5px;
              overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pp-sel-nom small { display: block; color: var(--muted); font-weight: 600; font-size: 12.5px; }
.pp-sel-tot { flex: 0 0 auto; font-weight: 800; font-size: 14.5px; color: var(--ink);
              font-variant-numeric: tabular-nums; min-width: 62px; text-align: right; }
/* le compteur : deux cibles franches au doigt, jamais sous 36 px */
.pp-pas { display: inline-flex; align-items: center; gap: 2px; flex: 0 0 auto; }
.pp-pas button {
  width: 36px; height: 36px; flex: 0 0 auto;
  font: inherit; font-size: 19px; font-weight: 800; line-height: 1;
  color: var(--red); background: var(--card);
  border: 1.5px solid var(--red); border-radius: 10px; cursor: pointer;
  touch-action: manipulation;
}
.pp-pas button:hover:not(:disabled) { background: var(--red); color: #FFFFFF; }
.pp-pas button:disabled { opacity: .3; cursor: default; }
.pp-pas .pp-nb {
  min-width: 34px; text-align: center;
  font-weight: 800; font-size: 16px; font-variant-numeric: tabular-nums;
}
.pp-sel-x {
  width: 30px; height: 30px; flex: 0 0 auto;
  font: inherit; font-size: 15px; font-weight: 700; line-height: 1;
  color: var(--muted); background: none; border: 0; cursor: pointer; border-radius: 8px;
}
.pp-sel-x:hover { color: var(--red); background: var(--red-soft); }
@media (max-width: 520px) {
  /* sur un téléphone, le nom prend la première ligne et le compteur la seconde :
     personne ne se bat pour 20 px de large */
  .pp-sel-l { flex-wrap: wrap; }
  .pp-sel-nom { flex: 1 1 100%; }
}

.modal-close {
  position: absolute; top: 12px; right: 12px;
  width: 44px; height: 44px;   /* 11/09 (B13) : cible confortable au doigt */
  border-radius: 50%;
  background: var(--cream);
  font-size: 17px; font-weight: 700; color: var(--muted);
  display: flex; align-items: center; justify-content: center;
  z-index: 2;
}
.modal-close:hover { background: var(--cream-deep); }

.modal__scroll h3 {
  text-align: center;
  font-size: 22px; font-weight: 800; letter-spacing: -.5px;
  margin-bottom: 6px;
}
/* Un titre de modale peut porter l'icône du bouton qui l'ouvre (Julien 27/08) :
   text-align ne suffit pas à aligner une image sur le texte, il faut le flex.
   Le wrap protège les titres longs en petite largeur. */
.modal__scroll h3:has(img) {
  display: flex; align-items: center; justify-content: center;
  flex-wrap: wrap; gap: 10px;
}
.modal__scroll h3 img { width: 22px; height: 22px; object-fit: contain; flex: 0 0 auto; }
.modal__scroll .intro { text-align: center; color: var(--muted); font-weight: 600; margin-bottom: 18px; }

.tir-list { display: flex; flex-direction: column; gap: 2px; margin-bottom: 20px; }
.tir-item {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 12px;
  border-radius: 11px;
  font-weight: 600; font-size: 14.5px;
}
.tir-item:nth-child(odd) { background: #FCFAF7; }
.tir-item .who { flex: 1; }
.tir-item small { color: var(--muted); font-weight: 600; }
.tir-item input[type="checkbox"] {
  width: 19px; height: 19px;
  accent-color: var(--red);
  flex-shrink: 0;
}

.success-note {
  display: none;
  background: var(--green-soft);
  color: #187A42;
  font-weight: 700;
  border-radius: 12px;
  padding: 13px 16px;
  text-align: center;
}
.success-note.show { display: block; }

/* ---------- État d'attente ---------- */

/* 09/09/2026 (Julien : « je veux absolument plus que la démo soit présente »).
   Les pages ne portent plus de chiffres d'exemple ni de voile pour les
   cacher : chaque valeur naît en attente (« ··· », « Chargement… ») et le
   rendu la remplace. Ce qui suit est l'habit de cette attente. */
.sk {
  display: inline-block;
  min-width: 64px;
  color: transparent;
  background: var(--cream-deep);
  border-radius: 9px;
  animation: skpulse 1.2s ease-in-out infinite;
}
@keyframes skpulse { 50% { opacity: .55; } }
/* un graphique pas encore dessiné réserve la hauteur de son dessin (même
   rapport que le viewBox 980 × 300, ou 980 × 320 pour les hauts) : la page
   ne saute pas quand les courbes arrivent */
.chart-box:empty {
  aspect-ratio: 980 / 300;
  min-height: 205px;
  background: var(--cream-deep);
  border-radius: 12px;
  animation: skpulse 1.2s ease-in-out infinite;
}
.chart-box.haut:empty { aspect-ratio: 980 / 320; min-height: 225px; }
/* un graphe qui n'a rien à dessiner le dit, à la même hauteur que son
   dessin aurait eue : la carte ne saute pas non plus dans ce cas */
.chart-box .chart-vide {
  min-height: 205px; display: flex; align-items: center; justify-content: center;
  color: var(--muted); font-size: 14px; text-align: center; padding: 22px 10px;
}
.chart-box.haut .chart-vide { min-height: 225px; }

/* ---------- Panneau de notifications (cloche) ---------- */

.notif-panel {
  position: absolute;
  top: 54px; right: 0;
  width: 330px;
  max-width: calc(100vw - 32px);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 16px;
  box-shadow: var(--shadow);
  padding: 8px;
  display: none;
  z-index: 70;
}
.notif-panel.open { display: block; }
.notif-item {
  display: flex; gap: 11px; align-items: flex-start;
  padding: 11px 12px;
  border-radius: 11px;
  font-size: 14px; font-weight: 700;
  line-height: 1.35;
}
.notif-item:hover { background: var(--cream); }
.notif-item small { display: block; color: var(--muted); font-weight: 600; font-size: 12.5px; }

/* ---------- Formulaire d'édition de fiche ---------- */

.form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px 14px;
  text-align: left;
}
.form-grid .full { grid-column: 1 / -1; }
.form-field label {
  display: block;
  font-size: 12.5px; font-weight: 700;
  color: var(--muted);
  margin-bottom: 4px;
}
.form-field input,
.form-field select,
.form-field textarea {
  width: 100%;
  font-family: inherit;
  font-size: 16px;
  font-weight: 600;
  color: var(--ink);
  background: #FFFFFF;
  border: 1px solid var(--border);
  border-radius: 11px;
  padding: 10px 12px;
  outline: none;
}
.form-field input:focus,
.form-field select:focus,
.form-field textarea:focus { border-color: #F0B9B5; }
/* 🔴 30/08 (Julien) : « pas de possibilité de redimensionner ici, c'est pas
   très joli ». La poignée en escalier du coin bas droit disparaît partout ; la
   zone s'agrandit toute seule sous le texte (autoTaille() dans app.js), donc on
   ne perd rien de ce que la poignée servait à faire. */
.form-field textarea { min-height: 74px; resize: none; }
.esp-checks { display: flex; gap: 10px; flex-wrap: wrap; padding: 2px 0; }
/* 🔴 30/08 (Julien) : « ça manque de couleurs et d'icônes là ». Les trois
   cases à cocher grises de l'espèce deviennent des pastilles à icône qui
   s'allument au rouge de marque quand elles sont choisies. La case native
   reste dans le DOM (le JS lit toujours `input:checked`), simplement masquée. */
.esp-check {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 14px; border-radius: 12px;
  background: var(--card); border: 1.5px solid var(--border);
  font-size: 14.5px; font-weight: 700; color: var(--ink);
  cursor: pointer; user-select: none;
}
.esp-check img { width: 19px; height: 19px; object-fit: contain; flex: 0 0 auto; }
.esp-check input { position: absolute; opacity: 0; width: 0; height: 0; }
.esp-check:hover { border-color: #F0B9B5; }
.esp-check:has(input:checked) {
  background: var(--red-soft); border-color: var(--red); color: var(--red-dark);
}
/* le clavier doit voir où il est : la case est masquée, pas le focus */
.esp-check:has(input:focus-visible) { box-shadow: 0 0 0 3px var(--red-soft); }

/* Les trois temps du formulaire, chacun sur le fond de sa famille. Même motif
   que les sections du récap de l'accueil (.recap-sec), donc rien de nouveau à
   apprendre en lisant l'écran. */
.form-sec {
  border: 1px solid var(--border); border-radius: 14px;
  padding: 14px 16px 16px; background: var(--cream-deep);
}
.form-sec + .form-sec { margin-top: 12px; }
.form-sec > h4 {
  display: flex; align-items: center; gap: 8px;
  font-size: 13.5px; font-weight: 800; letter-spacing: .3px; text-transform: uppercase;
  margin-bottom: 12px; color: var(--ink);
}
.form-sec > h4 img { width: 17px; height: 17px; object-fit: contain; flex: 0 0 auto; }
.form-sec.bleu  { background: #EAF2FB; border-color: #D6E5F6; }
.form-sec.bleu  > h4 { color: #1B4E85; }
.form-sec.rouge { background: var(--red-soft); border-color: #F6D5D3; }
.form-sec.rouge > h4 { color: var(--red-dark); }
.form-sec.vert  { background: var(--green-soft); border-color: #CBEBD8; }
.form-sec.vert  > h4 { color: #187A42; }
/* dans une section teintée, un champ blanc pur ressort (doctrine modales JBC) */
.form-sec .form-field input,
.form-sec .form-field select,
.form-sec .form-field textarea { background: #FFFFFF; }
/* chaque libellé porte son icône, calée sur la hauteur de capitale */
.form-field label img {
  width: 14px; height: 14px; object-fit: contain;
  margin-right: 6px; vertical-align: -2px; flex: 0 0 auto;
}
.form-aide { color: var(--muted); font-size: 13px; font-weight: 600; margin-top: 7px; line-height: 1.5; }
/* « Je viens de le livrer aujourd'hui » : une bascule pleine largeur, pas une
   case perdue au milieu du fond vert */
.bascule {
  display: flex; align-items: center; gap: 9px;
  padding: 10px 14px; border-radius: 12px;
  background: var(--card); border: 1.5px solid var(--border);
  font-size: 15px; font-weight: 700; cursor: pointer; user-select: none;
}
.bascule span { display: inline-flex; align-items: center; gap: 8px; }
.bascule img { width: 18px; height: 18px; object-fit: contain; }
.bascule input { width: 18px; height: 18px; accent-color: var(--red); flex: 0 0 auto; }
.bascule:has(input:checked) { background: var(--green-soft); border-color: #7FCFA1; }

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

/* ---------- Nav mobile ---------- */

.tabbar { display: none; }

/* le compte à rebours du Caissier : pastille d'ANGLE en haut à droite de la
   carte Factures, même style et même taille que celle de la carte tir
   (Julien 28/08 : « j'aime l'harmonie ») */
.kpi .hz-pill.kpi-coin { position: absolute; top: 14px; right: 14px; margin-top: 0; }
/* 🔴 28/08 (Julien, mobile) : « dans 21h57 pas au bon endroit ». Sur une carte
   étroite, la pastille d'angle chevauchait le rond de l'icône. Dès que les
   cartes se resserrent, elle redescend dans le flux, sous le chiffre. */
@media (max-width: 1150px) {
  .kpi .hz-pill.kpi-coin { position: static; margin-top: 8px; align-self: flex-start; }
}

/* ---- État de TVA : le récapitulatif d'une période (Fabien, 28/08) ----
   🔴 30/08 (Julien) : « j'aimerais un peu plus de couleurs, au fond des
   chiffres, parce qu'ils sont pas très clairs ; les chiffres en gris comme ça
   c'est pas très joli ». Rien de nouveau n'est inventé : on rebranche les
   pastilles .mnt du tableau des factures, donc la même nature de montant garde
   la même teinte d'un bout à l'autre du cockpit (HT bleu ardoise, TVA ambre,
   TTC rouge de marque). */
.tva-recap { margin-top: 6px; }
/* Le compte de factures ne reste pas un chiffre gris tout seul : c'est la même
   pastille blanche que les sous-titres de page, icône à gauche puis le texte. */
.tva-tete {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 6px 13px 7px; margin-bottom: 14px;
  background: var(--card); border: 1px solid var(--border); border-radius: 14px;
  color: var(--ink); font-size: 14.5px; font-weight: 700;
  box-shadow: var(--shadow-soft);
}
.tva-tete img { width: 17px; height: 17px; object-fit: contain; flex: 0 0 auto; }
.tva-l {
  display: grid; grid-template-columns: 1fr auto auto; align-items: center; gap: 10px;
  padding: 9px 0; border-bottom: 1px solid var(--border); font-size: 15px; font-weight: 600;
}
.tva-l .tva-lib { display: inline-flex; align-items: center; gap: 8px; }
/* le taux en pastille crème : c'est une clé de lecture, pas un montant */
.tva-l .tva-taux {
  font-style: normal; font-weight: 800; font-size: 13.5px;
  padding: 2px 9px; border-radius: 8px;
  background: var(--cream-deep); color: var(--ink);
}
.tva-l .mnt { font-size: 15px; }
.tva-l:last-of-type { border-bottom: 0; }
/* Le total quitte la colonne : trois montants étiquetés côte à côte, sur la
   bande crème, comme les colonnes HT / TVA / TTC du tableau des factures. */
.tva-total {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px;
  margin-top: 12px; padding: 13px 14px;
  background: var(--cream-deep); border-radius: 14px;
}
.tva-b { display: flex; flex-direction: column; align-items: center; gap: 6px; text-align: center; }
.tva-b-lib { font-size: 12.5px; font-weight: 700; color: var(--muted); }
.tva-b .mnt { font-size: 15.5px; padding: 5px 10px; }
.tva-note { margin-top: 12px; font-size: 13px; font-weight: 600; color: var(--orange); line-height: 1.45; }
.tva-recap .doux { color: var(--muted); font-weight: 600; font-size: 14.5px; }
/* 🔴 30/08 (Julien) : « peut-être un border rouge sur les dates ». La période
   est le réglage de cette modale : ses champs se distinguent des saisies
   ordinaires par le cadre rouge, et le focus l'assombrit au lieu de l'effacer. */
.tva-dates .form-field input {
  border: 1.5px solid rgba(227, 6, 19, .45);
  background: var(--red-softer);
}
.tva-dates .form-field input:focus { border-color: var(--red); background: var(--card); }
.tva-dates .form-field label { color: var(--red-dark); }
/* Sous 430 px les trois totaux à 1/3 de largeur cassent les montants en deux
   lignes : ils passent en colonne, chacun sur sa ligne, libellé à gauche.
   🔴 Mesuré au harnais : la ligne de taux en « libellé | base | TVA » sortait
   de 36 px de la modale à 390 px (371 pour 335 utiles). Le libellé prend sa
   propre ligne et les deux montants se partagent la suivante. */
@media (max-width: 430px) {
  .tva-total { grid-template-columns: 1fr; gap: 8px; }
  .tva-b { flex-direction: row; justify-content: space-between; text-align: left; }
  .tva-l { grid-template-columns: 1fr 1fr; gap: 6px 8px; }
  .tva-l .tva-lib { grid-column: 1 / -1; }
  .tva-l .mnt { font-size: 14px; padding: 3px 8px; text-align: center; }
  .tva-l .mnt-tva { justify-self: end; }
}

/* le CA hors taxes, sous le CA du mois */
/* 29/08 (Julien) : le mois rappelé en pastille après « CA du mois » */
/* 🔴 01/09 soir (Julien) : « fond rouge plutôt pour ces pastilles (2026
   aussi), harmonise sur la page Factures aussi ». Une seule règle pour
   l'accueil, Factures et Stats : le rouge doux de la maison, texte rouge
   sombre, le même habit que la pastille « CA = … » au coin des cartes. */
.kpi .label .label-mois {
  display: inline-block; padding: 2px 9px; border-radius: 8px;
  background: var(--red-soft); color: var(--red-dark);
  font-weight: 800; font-size: 13.5px; vertical-align: 1px;
}
/* 29/08 (Julien) : la date du jour en orange dans le libellé (orange maison) */
.kpi .label .label-date { color: var(--orange); font-weight: 700; }

.kpi .kpi-ht {
  /* 29/08 (Julien) : « mets ça en orange », puis « un autre orange plus
     saturé » : l'orange maison (--orange), celui des deltas et alertes */
  display: block; margin-top: 6px; font-size: 14px; font-weight: 700; color: var(--orange);
  font-variant-numeric: tabular-nums;
}
.kpi .kpi-ht small { font-weight: 600; font-size: 12.5px; }

/* ---- 🤖 Rapport d'analyse IA (Julien 30/08) ----
   Le rapport se lit comme une lettre, pas comme un tableau de bord : des
   titres qui portent la teinte de la marque, des paragraphes aérés, et les
   chiffres en gras que l'oeil accroche. */
/* 🔴 31/08 (Julien) : « un peu plus aéré, avec des mots en gras, un peu plus
   clair. Là, c'est un peu tassé. » Le rapport fait 7 000 caractères : sans
   respiration ni repère, il se lit comme un mur. Chaque section devient un
   bloc, avec son icône du pack Tabler, et les deux sections qui appellent une
   ACTION sont encadrées pour se détacher du simple constat. */
.an-rapport { text-align: left; }
.an-s { margin: 0 0 26px; }
.an-s:last-child { margin-bottom: 0; }
.an-rapport .an-h {
  display: flex; align-items: center; gap: 10px;
  font-size: 15.5px; font-weight: 800; color: var(--red-dark);
  letter-spacing: .2px; margin: 0 0 13px; padding-bottom: 9px;
  border-bottom: 1px solid var(--border);
}
.an-rapport .an-h img {
  width: 20px; height: 20px; flex: 0 0 20px; object-fit: contain;
}
.an-rapport .an-p {
  font-size: 15px; font-weight: 500; line-height: 1.75; margin-bottom: 13px;
}
.an-rapport .an-p:last-child { margin-bottom: 0; }
.an-rapport .an-l {
  list-style: none; display: flex; flex-direction: column; gap: 13px; margin-bottom: 0;
}
.an-rapport .an-l li {
  position: relative; padding-left: 22px;
  font-size: 15px; font-weight: 500; line-height: 1.7;
}
.an-rapport .an-l li::before {
  content: ''; position: absolute; left: 4px; top: .7em;
  width: 7px; height: 7px; border-radius: 50%; background: var(--red);
}
/* Le gras du modèle porte le chiffre : il doit se voir sans crier. */
.an-rapport b { font-weight: 800; color: var(--ink); }
/* Les deux sections qui appellent un geste de Fabien (« Ce que je ferais cette
   semaine », « Ce qui manque pour aller plus loin ») : encadré crème à filet
   rouge, pour qu'on les retrouve d'un coup d'œil dans un long rapport. */
.an-s--agir {
  background: var(--cream-deep); border-left: 3px solid var(--red);
  border-radius: 12px; padding: 16px 18px 18px;
}
.an-s--agir .an-h { border-bottom-color: var(--border); }
@media (max-width: 600px) {
  .an-s { margin-bottom: 22px; }
  .an-s--agir { padding: 14px 15px 16px; }
  .an-rapport .an-p, .an-rapport .an-l li { font-size: 14.5px; }
}
.an-avert {
  margin-top: 18px; padding: 11px 14px;
  background: var(--cream-deep); border-radius: 12px;
  color: var(--muted); font-size: 13px; font-weight: 600; line-height: 1.5;
  text-align: left;
}
/* L'avertissement sur les chiffres calculés par le modèle : ambre, pas rouge.
   Ce n'est pas une panne, c'est une invitation à recouper. */
.an-avert--doute {
  background: var(--orange-soft); color: var(--ink);
  border-left: 3px solid var(--orange); margin-bottom: 10px;
}
.an-avert--doute b { font-weight: 800; }
.an-pied {
  display: flex; flex-wrap: wrap; gap: 4px 8px; justify-content: center;
  margin-top: 20px; padding-top: 14px; border-top: 1px solid var(--border);
  color: var(--muted); font-size: 13px; font-weight: 600;
}
.an-actions { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; margin-top: 14px; }
.an-actions .btn.ghost {
  background: var(--card); color: var(--red-dark); border: 1.5px solid var(--red);
}
/* L'attente : le modèle raisonne une bonne minute, l'écran doit VIVRE, sinon
   Fabien croit que ça a planté et ferme la fenêtre. */
.an-attente {
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  padding: 40px 10px 34px; text-align: center;
}
.an-attente b { font-size: 15.5px; font-weight: 800; }
.an-attente small { color: var(--muted); font-size: 13.5px; font-weight: 600; line-height: 1.5; }
.an-spin {
  width: 34px; height: 34px; border-radius: 50%;
  border: 3px solid var(--red-soft); border-top-color: var(--red);
  animation: an-tourne .9s linear infinite;
}
@keyframes an-tourne { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .an-spin { animation-duration: 3s; } }

/* ---- 🚚 ce que coûte une tournée (02/09/2026) ----------------------------
   Les cinq chiffres que Fabien est seul à connaître, sous le rapport, là où
   ils servent. Libellé à gauche, valeur à droite, et la valeur ne se coupe
   jamais de son unité. */
.couts-s { margin-top: 26px; }
.couts-l {
  list-style: none; margin: 0 0 14px; padding: 0;
  display: flex; flex-direction: column; gap: 0;
}
.couts-l li {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  align-items: center; gap: 10px;
  padding: 10px 0; border-bottom: 1px solid var(--border);
  font-size: 14.5px; font-weight: 600; line-height: 1.4;
}
.couts-l li:last-child { border-bottom: 0; }
.couts-l .k { color: var(--muted); }
.couts-l .v {
  font-weight: 800; color: var(--ink); text-align: right;
  white-space: nowrap; font-variant-numeric: tabular-nums;
}
.couts-calc {
  margin: 0; padding: 12px 14px; border-radius: 12px;
  background: var(--cream-deep); color: var(--muted);
  font-size: 14px; font-weight: 600; line-height: 1.6; text-align: left;
}
.couts-calc b { font-weight: 800; color: var(--ink); }
/* En édition, la valeur devient un champ. 🔴 16 px minimum : sous 16 px,
   Safari iOS zoome au tap et la page devient scrollable en largeur. */
.couts-l--edit li { align-items: center; }
.couts-l--edit label { display: block; cursor: pointer; }
.couts-l--edit .v { display: flex; align-items: center; gap: 7px; }
.couts-l--edit .v input {
  width: 5.5em; padding: 9px 10px; text-align: right;
  font-family: inherit; font-weight: 700; line-height: 1.2;
  /* 🔴 16 px pile : en dessous, Safari iOS zoome au tap et la page part
     en scroll horizontal. Écrit APRÈS font-family, jamais dans un
     raccourci « font: » qui le reperdrait. */
  font-size: 16px; color: var(--ink);
  background: #FFFFFF; border: 1.5px solid var(--border); border-radius: 10px;
  font-variant-numeric: tabular-nums;
}
.couts-l--edit .v input:focus { outline: 2px solid var(--red); outline-offset: 1px; }
.couts-l--edit .v .u { color: var(--muted); font-weight: 700; font-size: 14px; }
.couts-err { background: var(--red-soft); color: var(--red-dark); }
@media (max-width: 380px) {
  .couts-l li { font-size: 14px; }
  .couts-l--edit .v input { width: 4.6em; }
}

/* ---- serveur injoignable : bandeau discret posé par app.js ---- */
.hors-ligne {
  background: #FCEBEC; border: 1px solid rgba(227, 6, 19, .25); color: #C00511;
  border-radius: 14px; padding: 12px 16px; font-size: 14.5px; font-weight: 700;
  margin-bottom: 18px;
}
.hors-ligne .hl-retry {
  background: #C00511; color: #fff; border: none; border-radius: 9px;
  padding: 6px 12px; margin-left: 10px; font: inherit; font-size: 13.5px;
  cursor: pointer;
}
/* 31/08 (audit) : la puce de fraîcheur des chiffres, discrète, sous le titre */
.fraicheur {
  display: inline-block; margin-left: 10px; padding: 2px 10px;
  background: transparent; border: 1px solid #E4DCD0; border-radius: 20px;
  color: var(--muted); font: inherit; font-size: 12px; cursor: pointer;
  vertical-align: middle; white-space: nowrap;
}
.fraicheur:hover { color: #17150F; border-color: #C9BFB0; }
/* 11/09 (lot 1) : le serveur n'a pas répondu, les chiffres viennent du cache :
   la pastille le dit en orange, avec l'heure de ces chiffres */
.fraicheur.vieux { color: var(--orange); border-color: var(--orange); background: var(--orange-soft); }
/* 31/08 (audit) : l'état « Chargement… » des blocs hors tableaux */
.attente {
  color: var(--muted); font-size: 14px; padding: 22px 10px; text-align: center;
}

/* ---------- Footer ---------- */

.app-footer {
  text-align: center;
  color: var(--muted);
  font-size: 13.5px; font-weight: 600;
  padding: 34px 0 6px;
  display: flex; align-items: center; justify-content: center; gap: 8px;
}
.app-footer img { width: 15px; height: 15px; opacity: .85; }

/* ============================================================
   Responsive
   ============================================================ */

@media (max-width: 1120px) {
  .kpis, .kpis.k3, .kpis.k2, .kpis.k5 { grid-template-columns: repeat(2, 1fr); }
  .kpis > .tir-kpi { grid-column: 1 / -1; }
  /* 01/09 : en deux colonnes, une rangée à nombre IMPAIR de cartes étire la
     dernière sur toute la largeur plutôt que de la laisser seule à moitié.
     07/09 : la rangée CA est passée à 4 cartes (l'exercice), Factures à 5. */
  .kpis-ca > .kpi:last-child:nth-child(odd), .kpis.k5 > .kpi:last-child { grid-column: 1 / -1; }
  .robots-grid { grid-template-columns: repeat(2, 1fr); }
  .grid-1, .grid-2, .grid-2eq { grid-template-columns: 1fr; }
}

@media (max-width: 860px) {
  .sidebar { display: none; }
  /* 🔴 01/09 (Julien, capture iPhone) : « le bas de la page est pas visible,
     le scroll s'arrête avant ». La barre d'onglets est en position:fixed :
     elle ne pousse rien, c'est à la page de lui réserver sa place. Sa hauteur
     réelle vaut 80 px (8 haut + 5 + icône 28 + 5 + libellé 18 + 5 + 10 bas +
     1 de filet) PLUS le liseré du bas de l'iPhone, que seul env() connaît.
     Ce réglage était figé à 96 px, sans env() : sur un iPhone à encoche
     (34 px de liseré), la barre mesure 114 px et mangeait les 18 derniers
     pixels de la page, pied de page compris. On réserve la hauteur exacte,
     plus 24 px d'air pour que la dernière carte respire. */
  .main { margin-left: 0; padding: 20px 16px calc(104px + env(safe-area-inset-bottom)); }

  .page-head h1 { font-size: 27px; letter-spacing: -.7px; }

  .tabbar {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    background: var(--card);
    border-top: 1px solid var(--border);
    display: flex;
    justify-content: flex-start;
    padding: 8px 6px calc(10px + env(safe-area-inset-bottom));
    z-index: 50;
    /* 8 entrées : la barre glisse au doigt, sans barre de défilement visible ;
       un onglet à moitié coupé au bord montre qu'il y a une suite (Julien 28/08).
       🔴 15/09 : RÉTABLI tel quel. Le 11/09 elle était passée à 5 positions
       fixes + « Plus » ; Julien préfère la barre qui glisse, dans l'ordre du
       bureau. Ne pas y retoucher sans son accord. */
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    -ms-overflow-style: none;
  }
  .tabbar::-webkit-scrollbar { display: none; width: 0; height: 0; }
  .tabbar a, .tabbar button {
    display: flex; flex-direction: column; align-items: center; gap: 5px;
    font-size: 12px; font-weight: 700; color: var(--muted);
    /* 28/08 (Julien) : « des icônes plus grosses, 4/5 max » : chaque onglet
       prend ~1/4,7 de l'écran, le demi-onglet coupé au bord dit qu'on peut
       glisser pour voir la suite. Plafonné pour les tablettes. */
    width: 21vw; min-width: 74px; max-width: 104px;
    padding: 5px 2px;
    flex: 1 0 auto;
    border: 0; background: none; font-family: inherit; cursor: pointer;
  }
  /* 11/09 (B11) : sur téléphone, filtres et petites actions prennent la cible
     commune de 44 px ; la fiche se resserre */
  .fchip, .chip-f, .chips-cadre .chip-f, .link-more { min-height: var(--ctrl); font-size: 14px; border-radius: var(--r); }
  .fiche-acces { display: flex; }
  .bloc-toggle { display: inline-flex; align-items: center; }
  #infosTable.replie { display: none; }
  .client-hero { padding: 16px 18px; gap: 14px; }
  .client-hero .avatar-ini { width: 56px; height: 56px; font-size: 20px; }
  .client-hero h1 { font-size: 24px; letter-spacing: -.6px; }
  .tabbar a img, .tabbar button img { width: 28px; height: 28px; }
  .tabbar a .ic-on, .tabbar button .ic-on { display: none; }
  .tabbar a.active { color: var(--red); }
  .tabbar a.active .ic-off { display: none; }
  .tabbar a.active .ic-on { display: block; }

  /* 🔴 01/09 (Julien, consigne du 31/08 18h29) : « une latence importante au
     toucher du menu du bas ». Le doigt était bien pris tout de suite, mais
     RIEN ne le montrait : le surlignage de tap est coupé sur tout le site
     (-webkit-tap-highlight-color: transparent), et la page suivante met le
     temps qu'elle met. Un écran qui ne répond pas se lit comme un écran lent.
     On accuse donc réception du doigt à la milliseconde, et on dit au
     navigateur qu'un onglet ne sert jamais à zoomer : il n'a plus à attendre
     un éventuel second appui avant de suivre le lien. La barre, elle, garde
     son défilement horizontal, d'où pan-x et pas none. */
  .tabbar { touch-action: pan-x; }
  .tabbar a, .tabbar button { touch-action: manipulation; }
  .tabbar a img, .tabbar button img { transition: transform .08s ease; }
  .tabbar a.tap, .tabbar button.tap { color: var(--red); }
  .tabbar a.tap img, .tabbar button.tap img { transform: scale(.88); }

  .client-side {
    border-left: none; padding-left: 0;
    border-top: 1px solid var(--border);
    padding-top: 16px;
    margin-left: 0;
    width: 100%;
  }

  .kpi .value { font-size: 31px; }
  .block { padding: 20px 18px; }
  .robots-grid { grid-template-columns: 1fr; }
  .cta-duo .btn { min-width: 100%; }

  /* La légende « ● = réponse arrivée aujourd'hui » se serrait à droite du
     titre et se repliait sur deux lignes : elle prend sa propre ligne. */
  .recap-sec h4 { flex-wrap: wrap; row-gap: 2px; }
  .recap-sec h4 small { flex: 0 0 100%; }
  .recap-sec h4 .h4-etat { flex: 0 0 100%; }

  /* 🔴 28/08 (Julien) : « la première ligne juste l'heure et le nom prénom,
     les lieux sur la deuxième ligne ». Le groupe .li2 (lieu, jour de
     livraison, créneau, montant) descend, et son « · » de liaison disparaît. */
  .recap-sec li .li2 { display: block; margin-top: 1px; }
  .recap-sec li .li2 > .sep:first-child { display: none; }
  .tournee-liste li .li2 { display: block; margin-top: 1px; }
  .tournee-liste li { align-items: flex-start; }
  .tournee-liste .etape { margin-top: 1px; }
}

/* 🔴 01/09/2026 (Julien, capture iPhone de la page Factures) : « sur version
   mobile les textes ne sont pas très bien harmonisés sur les cartes : ça
   déborde, ou ça passe en dessous ». MESURÉ au banc (_banc-cartes-mobile.html,
   iframes à 320 / 360 / 390 / 430 px, vraies valeurs du cockpit) : sur les
   QUATRE largeurs, « fact. » tombait seul sur sa ligne dans deux cartes,
   « €TTC » seul sous « 220 724 » dans la troisième, et la pastille du mois
   (« Septembre », « 2026 ») seule sous « Facturé en ». Sept textes coupés en
   orphelins par carte-rangée : c'est exactement ce que la règle maison
   interdit.
   La cause n'est pas un réglage à ajuster, c'est la LARGEUR : deux colonnes
   sur un téléphone ne laissent que ~106 px utiles dans une carte, et
   « 220 724 € TTC » en demande 150 au corps où le chiffre reste lisible.
   Aucun réglage de police ne rattrape ça sans rendre le nombre minuscule.
   Les cartes passent donc en UNE colonne, mais COUCHÉES : la pastille à
   gauche, le libellé et le nombre à droite. Chaque texte retrouve la largeur
   entière de la carte, donc une seule ligne, et la rangée est PLUS COURTE
   qu'avant (mesuré : 4 cartes couchées contre 2 rangées de cartes debout).
   La carte du tir (.tir-kpi) garde sa forme : elle porte un bouton et trois
   lignes de texte, pas un chiffre. */
@media (max-width: 520px) {
  .kpis, .kpis.k3, .kpis.k2, .kpis.k5 { grid-template-columns: 1fr; gap: 10px; }
  .kpis > .tir-kpi { grid-column: 1 / -1; }
  /* 🔴 07/09 : la règle de base (.achat-l .achat-top, 2 classes) est écrite PLUS
     BAS dans la feuille : à spécificité égale elle gagnait et la grille restait
     à 4 colonnes sur téléphone (le lien PDF sortait de l'écran, mesuré à 360 px).
     Trois classes ici pour que le mobile l'emporte quel que soit l'ordre. */
  .achats-liste .achat-l .achat-pdf { margin-left: 0; }
  .pf-actions .btn { width: 100%; }
  .achats-liste .achat-l .achat-prods { margin-top: 6px; }
  .kpis > .kpi:not(.tir-kpi) {
    display: grid; grid-template-columns: auto minmax(0, 1fr);
    align-items: center; column-gap: 14px;
    padding: 13px 16px;
  }
  .kpis > .kpi:not(.tir-kpi) .chip { grid-column: 1; grid-row: 1 / span 2;
                                     width: 44px; height: 44px; margin-bottom: 0; }
  .kpis > .kpi:not(.tir-kpi) > :not(.chip) { grid-column: 2; min-width: 0; }
  .kpis > .kpi:not(.tir-kpi) .label { margin-top: 0; font-size: 14px; }
  .kpis > .kpi:not(.tir-kpi) .value { font-size: 27px; letter-spacing: -.8px; margin-top: 0; }
  .kpis > .kpi:not(.tir-kpi) .value small { font-size: 17px; }
  .kpis > .kpi:not(.tir-kpi) .value small.unite { font-size: 13px; }
  /* Toutes les cartes de la pile font la MÊME hauteur, qu'elles portent un
     compte de factures ou non : empilées, des cartes inégales se lisent comme
     un alignement raté (mesuré : 98, 101, 81 et 77 px avant ce plancher). */
  .kpis > .kpi:not(.tir-kpi) { min-height: 101px; }
}
/* En dernier recours seulement : si le nombre est trop long même sur toute la
   largeur, c'est le COMPTE de factures qui descend, en entier et sans son
   point de liaison, jamais un « fact. » orphelin. */
.kpi .value { flex-wrap: wrap; row-gap: 0; }
.kpi .value > small { white-space: nowrap; }
.kpi .value .kpi-cpt { white-space: nowrap; }
@media (max-width: 520px) {
  .kpi .value .kpi-cpt { flex: 0 0 100%; margin-top: 1px; font-size: 13.5px; }
  .kpi .value .kpi-cpt .sep { display: none; }
}

/* ---- Liste des clients : chaque ligne tient sur UNE ligne ; sur ordinateur
   TOUT rentre sans scroll (colonnes fixées + ellipse), sur écran étroit le
   tableau glisse dans sa carte (Julien 28/08) */
#clientsTable td { white-space: nowrap; }
@media (min-width: 900px) {
  table:has(#clientsTable) { table-layout: fixed; width: 100%; }
  table:has(#clientsTable) th:nth-child(1) { width: 22%; }
  table:has(#clientsTable) th:nth-child(2) { width: 15%; }
  table:has(#clientsTable) th:nth-child(3) { width: 18%; }
  table:has(#clientsTable) th:nth-child(4) { width: 12%; }
  table:has(#clientsTable) th:nth-child(5) { width: 12%; }
  table:has(#clientsTable) th:nth-child(6) { width: 8%; }
  table:has(#clientsTable) th:nth-child(7) { width: 13%; }
  #clientsTable td { overflow: hidden; text-overflow: ellipsis; }
}

/* les especes en pastilles colorees, a l'echelle du texte de la ligne */
.esp {
  display: inline-block; padding: 3px 10px; border-radius: 999px;
  font-size: 12.5px; font-weight: 700; margin-left: 5px; vertical-align: 1px;
}
.esp-chien { background: #EFE3D2; color: #7B5E3B; }
.esp-chat { background: #EADFF3; color: #6D4E93; }
.esp-cheval { background: #DFF0E2; color: #1F7A43; }
.esp-autre { background: var(--cream-deep); color: var(--muted); }

/* ---- Retour fiche client : aéré + hover souligné ---- */
/* 🔴 28/08 (Julien) : « Retour aux clients devrait être plus haut ». Ce
   margin-top venait du temps où le lien vivait SOUS le sous-titre ; dans la
   pastille il poussait le groupe icône+texte 7,2 px trop bas (mesuré).
   Il ne reste que pour un back-link hors pastille. */
.sub .back-link { display: inline-block; margin-top: 8px; color: var(--red); font-weight: 700; }
.page-head .sub .back-link { margin-top: 0; }
.sub .back-link:hover { text-decoration: underline; }

/* ---- Colonnes triables ---- */
th.sortable { cursor: pointer; user-select: none; white-space: nowrap; }
th.sortable::after {
  content: ''; display: inline-block; width: 8px; height: 13px; margin-left: 7px;
  vertical-align: -2px; opacity: .55;
  background: no-repeat center/8px 13px
    url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='13' fill='none' stroke='%23A79E92' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1.5 4.6 4 2.1l2.5 2.5M1.5 8.4 4 10.9l2.5-2.5'/%3E%3C/svg%3E");
}
th.sortable.asc::after {
  opacity: 1;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='13' fill='none' stroke='%23E30613' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 11V2.4M1.4 5 4 2.4 6.6 5'/%3E%3C/svg%3E");
}
th.sortable.desc::after {
  opacity: 1;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='13' fill='none' stroke='%23E30613' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 2v8.6M1.4 8 4 10.6 6.6 8'/%3E%3C/svg%3E");
}
th.sortable:hover { color: var(--red); }
th.sortable:hover::after { opacity: 1; }

/* ---- Sélection de factures ---- */
/* 11/09 (B9) : la cellule entière coche (44 px de cible), la case dessinée fait 18 px */
th.selcell, td.selcell { width: 44px; padding-right: 4px; cursor: pointer; }
.selcell input { width: 18px; height: 18px; accent-color: var(--red); cursor: pointer; display: block; margin: 0 auto; }

/* ---- Ville cliquable (Google Maps) ---- */
.ville-link { color: inherit; font-weight: 600; text-decoration: none; }
.ville-link:hover { color: var(--red); }
/* le trait ne court que sous le TEXTE, jamais sous l'émoji (Julien 28/08) */
.ville-link:hover .lb { text-decoration: underline; }

/* ---- Parcours du client (timeline à points rouges) ---- */
.timeline { position: relative; padding: 4px 0 0 26px; max-height: 460px; overflow-y: auto; }
.tl-item { position: relative; padding-bottom: 16px; }
/* la pastille, centrée sur la LIGNE DE DATE (line-height 18px -> milieu 9px) */
.tl-item::before { content: ''; position: absolute; left: -24px; top: 3px; width: 12px; height: 12px;
  border-radius: 50%; background: var(--red); box-shadow: 0 0 0 3px var(--card); z-index: 1; }
/* le trait, qui relie cette pastille à la suivante, sur le même axe */
.tl-item::after { content: ''; position: absolute; left: -19px; top: 15px; bottom: -6px;
  width: 2px; background: var(--border); }
.tl-item:last-child::after { display: none; }
.tl-item small { display: block; color: var(--muted); font-size: 12.5px; font-weight: 600;
  line-height: 18px; margin-bottom: 2px; }
.tl-item .txt { font-size: 14px; font-weight: 600; line-height: 1.45; }

/* « Client depuis janvier 2025 » à droite du titre du parcours : même pilule
   que le sous-titre de page, mais en rouge plein (Julien 27/08). */
.badge.depuis { background: var(--red); color: #fff; padding: 7px 15px; border-radius: 999px;
  font-size: 13.5px; font-weight: 700; box-shadow: var(--shadow-soft); }

/* ---- Ligne de panier retirable + ajout produit ---- */
.line-x { border: 0; background: none; color: var(--muted); cursor: pointer; font-size: 13px;
  padding: 3px 7px; border-radius: 8px; font-weight: 700; }
.line-x:hover { color: #fff; background: var(--red); }

/* ---- Récap du jour (accueil) ---- */
.recap-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
.recap-cols h4 { font-size: 13px; text-transform: uppercase; letter-spacing: .4px;
  color: var(--muted); margin-bottom: 10px; }
.recap-cols ul { list-style: none; display: flex; flex-direction: column; gap: 8px; }
.recap-cols li { font-size: 14px; font-weight: 600; display: flex; justify-content: space-between; gap: 10px; }
.recap-cols li a { color: inherit; }
.recap-cols li a:hover { color: var(--red); text-decoration: underline; }
.recap-cols li .num { color: var(--muted); }
.recap-note { color: var(--muted); font-size: 13px; font-weight: 600; margin-top: 14px; }
@media (max-width: 700px) { .recap-cols { grid-template-columns: 1fr; } }

/* ---- Récap du jour v2 : sections colorées ---- */
.badge.blue { background: #E8F0FE; color: #1a73e8; }
.badge.blue .pt { background: #1a73e8; }
.recap-badges { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 18px; }
.recap-badges .badge { font-size: 13.5px; padding: 8px 13px; }
.recap-sec { border: 1px solid var(--border); border-radius: 14px; padding: 16px 18px; position: relative; }
/* 🔴 02/09 (Julien) : raccourci « Préparer les commandes » en pastille au coin
   du cadre vert, vers la page Clients filtrée « À préparer ». Sur ordinateur
   le titre garde sa place à gauche de la pastille ; sous 600 px la pastille
   redescend dans le flux, sous le titre, comme les badges d'angle des KPI. */
.recap-sec .sec-coin {
  position: absolute; top: 12px; right: 12px;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 11px; border-radius: 10px;
  /* 02/09 (Julien) : « fond rouge texte blanc pour ce bouton » : c'est une
     action, elle prend l'habit des boutons d'action (rouge plein dès le repos). */
  background: var(--red); border: 1px solid var(--red); color: #FFFFFF;
  font-size: 13px; font-weight: 800; text-decoration: none; white-space: nowrap;
}
.recap-sec .sec-coin img { width: 15px; height: 15px; }
.recap-sec .sec-coin:hover { background: var(--red-dark); border-color: var(--red-dark); color: #FFFFFF; }
.recap-sec:has(> .sec-coin) h4 { padding-right: 210px; }
@media (max-width: 600px) {
  .recap-sec .sec-coin { position: static; margin: -2px 0 12px; }
  .recap-sec:has(> .sec-coin) h4 { padding-right: 0; }
}
.recap-sec + .recap-sec { margin-top: 14px; }
.recap-sec h4 { font-size: 14px; font-weight: 800; margin-bottom: 10px; display: flex; align-items: center; gap: 8px; }
.recap-sec.vert { background: var(--green-soft); border-color: #CBEBD8; }
/* état week-end du Livreur : neutre, il explique une absence, il n'alerte pas */
.recap-sec.gris { background: var(--cream-deep); border-color: var(--border); }
.recap-sec.vert h4 { color: #187A42; }
.recap-sec.orange { background: var(--orange-soft); border-color: #F6E2C2; }
.recap-sec.orange h4 { color: var(--orange-txt); }
.recap-sec.bleu { background: #E8F0FE; border-color: #CFE0FB; }
.recap-sec.bleu h4 { color: #1a73e8; }
.recap-sec.rouge { background: var(--red-soft); border-color: #F6D5D3; }
.recap-sec.rouge h4 { color: var(--red-dark); }
.recap-sec ul { list-style: none; display: flex; flex-direction: column; gap: 7px; }
.recap-sec li { font-size: 14px; font-weight: 600; line-height: 1.4; }
.recap-sec li a { color: inherit; }
/* 07/09 (Julien) : le nom prend la couleur de sa pastille, orange = arrivée
   aujourd'hui, vert = déjà reçue (mêmes teintes que .pt-new / .pt-hier) */
/* 07/09 (Julien) : « orange et vert plus foncé pour les noms » : un texte fin
   paraît plus clair qu'un point plein, on prend les foncés de la charte
   (le vert des chiffres .c-green, l'orange des chiffres .n-orange). */
.recap-sec li a.nom-jour { color: #C2650B; }
.recap-sec li a.nom-recu { color: #146B3C; }
.recap-sec li a:hover { text-decoration: underline; }
.recap-sec li small { color: var(--muted); font-weight: 600; }
/* 07/09 (lot B) : la coche « pris en compte » d'une réponse. 🔴 Julien (capture
   iPhone) : « fond rouge et coche blanche, sans cercle en plus autour » : le
   rond rouge de la maison porte la coche nue ; la ligne s'estompe dès le clic. */
/* puis « en petit à côté de vendredi, ou du lieu » : un petit rond rouge DANS la
   2e ligne, après le jour de livraison ou la ville. */
.rep-ok { width: 18px; height: 18px; padding: 0; margin-left: 6px;
          /* même aplomb que la pastille du jour (.liv, vertical-align: 1px,
             ~20 px de haut) : 18 px de haut, on remonte de 2 px pour que les
             deux centres tombent sur la même ligne */
          vertical-align: 2px;
          border: none; border-radius: 50%; background: var(--red);
          color: #FFFFFF; cursor: pointer; display: inline-flex; align-items: center;
          justify-content: center; transition: var(--transition); }
/* 🔴 07/09 (Julien : « la coche est pas blanche ») : la règle générique
   .fi-ic { color: var(--soft) } battait le blanc hérité du bouton : on le
   pose SUR l'icône, avec deux classes pour gagner. */
.rep-ok .fi-ic { width: 11px; height: 11px; color: #FFFFFF; }
.rep-ok:hover { background: var(--red-dark); }
.rep-ok:disabled { cursor: default; }
li.rep-fait { opacity: .35; }
/* 07/09 (lot C) : l'heure impérative, la précision et le crayon d'un arrêt de tournée */
.tr-heure { display: inline-flex; align-items: center; gap: 3px; color: var(--red); font-weight: 800; }
.tr-heure svg { width: 13px; height: 13px; }
.tr-note { display: flex; align-items: flex-start; gap: 5px; margin-top: 2px; color: var(--ink);
           font-size: 13px; font-weight: 600; font-style: italic; }
.tr-note svg { width: 13px; height: 13px; flex-shrink: 0; margin-top: 2px; color: var(--muted); }
.tr-edit { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px;
           margin-left: 6px; padding: 0; border: 1px solid var(--border); border-radius: 50%;
           background: #FFFFFF; color: var(--muted); cursor: pointer; vertical-align: middle; }
.tr-edit svg { width: 12px; height: 12px; }
.tr-edit:hover { border-color: var(--red); color: var(--red); }

/* Lisibilité des lignes du récap (demande Julien 26/08) : une couleur par
   information, et la réponse du client assez foncée pour se lire d'un coup
   d'œil sur les 4 fonds de section (vert, orange, bleu, rouge). */
.recap-sec li .hr {
  display: inline-flex; align-items: center; gap: 4px;
  margin-left: 4px;                     /* qu'elle ne colle pas au nom */
  color: #2F6F8F;                       /* bleu ardoise : l'heure de réception */
  font-weight: 700; font-variant-numeric: tabular-nums;
  vertical-align: -2px;
}
.recap-sec li .hr .fi-ic { width: 14px; height: 14px; color: #2F6F8F; opacity: .75; }
/* réponse d'un jour passé : même pastille, en gris, pour que le bleu ardoise
   reste le signal « c'est arrivé aujourd'hui » (Julien, 27/08). */
/* « hier » reste discret, mais l'HEURE garde son bleu ardoise partout
   (Julien 27/08 : « tu laisses les heures en bleu »). */
.recap-sec li .hr .jr { color: var(--muted); font-weight: 600; }
.recap-sec li .hr .hh { color: #2F6F8F; font-weight: 700; }
.recap-sec li a.nom-lien { cursor: pointer; }
.recap-sec li .sep { color: var(--soft); font-weight: 700; margin: 0 1px; }
.recap-sec li .ville { color: #7B5E3B; font-weight: 600; }   /* brun doux : le lieu */
.recap-sec li .eur { color: var(--red); font-weight: 800; }   /* le montant d'une commande */
.recap-sec li .liv {
  display: inline-flex; align-items: center; gap: 4px;
  background: #FFFFFF; border: 1px solid var(--border);
  border-radius: 999px; padding: 1px 9px 1px 6px;
  font-size: 12.5px; font-weight: 700; color: var(--ink);
  vertical-align: 1px; white-space: nowrap;
}
.recap-sec li .liv img { width: 13px; height: 13px; display: block; flex-shrink: 0; }
/* Le SMS du client : fond RETIRÉ le 27/08 (Julien : « ça prend trop de place »).
   Sur 55 lignes, le cadre de la bulle ajoutait ~16px de hauteur chacune. On garde
   le gain de lisibilité (texte plus foncé, interligne) sans le cadre. La bulle
   reste dans la FICHE EXPRESS, où il n'y a qu'une réponse et donc pas d'enjeu. */
.recap-sec li .rep {
  display: block; color: var(--sms-txt); /* la réponse du client, en rouge doux */
  font-size: 13.5px; font-weight: 500; font-style: italic;
  margin-top: 2px; line-height: 1.45; overflow-wrap: anywhere;
}
.pt-new { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--orange); margin-right: 5px; vertical-align: 1px; }
/* Code couleur des réponses (Julien 31/08, remplace la règle du 27/08 où le
   vert ne marquait que la VEILLE) : orange = la réponse est arrivée
   aujourd'hui, vert = elle est déjà reçue, quel que soit le jour. Deux états,
   et toute ligne qui a une réponse porte sa pastille. */
.pt-hier { display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  background: var(--green); margin-right: 5px; vertical-align: 1px; }
/* Le compte des réponses du jour et la légende du vert, collés au titre de la
   carte (Julien 31/08 : « ça reconfirme le nombre »). Un seul bloc, pour qu'ils
   descendent ensemble sur une seule ligne quand l'en-tête se replie sur mobile.
   Le compte porte la couleur des pastilles qu'il annonce. */
.recap-sec h4 .h4-etat {
  font-size: 13px; font-weight: 600; color: var(--muted);
  display: inline-flex; align-items: center; flex-wrap: wrap; gap: 4px 10px;
}
.recap-sec h4 .h4-etat .nb-jour { font-weight: 700; color: var(--orange); white-space: nowrap; }
/* 🔴 01/09 (Julien) : le rond de la légende se cale sur l'ŒIL du texte, pas
   sur sa boîte de ligne. L'axe des capitales est à 0,365 em au-dessus de la
   ligne de base, soit 4,75 px à 13 px de corps ; le rond fait 8 px, son centre
   est donc à 4 px de son bas : il lui manque 0,75 px pour tomber pile dessus.
   Et la légende du vert vit désormais dans .lg-recu, un seul morceau, pour que
   le gap de 10 px de la rangée ne s'insère plus entre le rond et le « = ». */
.recap-sec h4 .h4-etat .nb-jour .pt-new { margin-right: 4px; vertical-align: .75px; }
.recap-sec h4 .h4-etat .lg-recu { white-space: nowrap; }
.recap-sec h4 .h4-etat .lg-recu .pt-hier { margin-right: 4px; vertical-align: .75px; }
.recap-grid { display: grid; grid-template-columns: 1.25fr 1fr; gap: 14px; align-items: start; }
@media (max-width: 900px) { .recap-grid { grid-template-columns: 1fr; } }

/* ---- Stats v2 ---- */
.regl-row { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
.regl-row .mode { width: 130px; font-size: 14px; font-weight: 700; flex-shrink: 0; }
.regl-row .bar-track { flex: 1; height: 12px; border-radius: 7px; background: var(--cream-deep); overflow: hidden; }
.regl-row .bar-fill { height: 100%; border-radius: 7px; background: var(--red); }
.regl-row .amount { font-size: 13.5px; font-weight: 700; white-space: nowrap; color: var(--muted); }
.kpi .delta { font-size: 13.5px; font-weight: 700; margin-top: 3px; display: block; }
.kpi .delta.up { color: var(--green); }
.kpi .delta.down { color: var(--red); }
.sync-note { text-align: center; color: var(--soft-txt); font-size: 12.5px; font-weight: 600; margin-top: 10px; }

/* règlements : sur petit écran, la barre passe dessous */
@media (max-width: 520px) {
  .regl-row { flex-wrap: wrap; gap: 6px 10px; }
  .regl-row .mode { width: auto; }
  .regl-row .bar-track { flex-basis: 100%; order: 3; }
}

/* ---- Badge d'angle sur une carte KPI (crédits SMS sur l'accueil) ---- */
.kpi { position: relative; }
.kpi-corner {
  position: absolute; top: 14px; right: 14px;
  font-size: 15px; font-weight: 800; letter-spacing: -.2px;
  padding: 7px 13px; border-radius: 11px;
  background: var(--cream-deep); color: var(--muted);
  white-space: nowrap;
}
.kpi-corner.ok { background: var(--green-soft); color: #187A42; }
.kpi-corner.warn { background: var(--orange-soft); color: var(--orange-txt); }
.kpi-corner.red { background: var(--red-soft); color: var(--red-dark); }
/* le CA facturé du jour, au coin de la carte Factures (Julien 27/08) */

@media (max-width: 600px) {
  .kpi-corner { font-size: 13px; padding: 6px 10px; top: 11px; right: 11px; }
  .kpi .label { font-size: 15.5px; }
}

/* ---- Montant au survol d'une barre du graphique (Julien 27/08) ---- */
.chart-tip {
  position: absolute; z-index: 3; pointer-events: none;
  display: flex; flex-direction: column; gap: 1px;
  padding: 8px 12px; border-radius: 12px;
  background: var(--ink); color: #fff;
  font-size: 13px; font-weight: 700; line-height: 1.25;
  box-shadow: 0 8px 20px rgba(23, 21, 15, .22);
  white-space: nowrap;
}
/* 🔴 28/08 : « l'infobulle reste affichée ». LA cause : le « display: flex »
   ci-dessus écrase la règle du navigateur « [hidden] { display: none } » (même
   spécificité, mais la feuille de l'auteur passe après). L'attribut hidden
   était donc bien posé par le JS, et la bulle restait peinte quand même. */
.chart-tip[hidden] { display: none; }
.chart-tip b { font-size: 12px; font-weight: 700; opacity: .72; }
/* 🔴 31/08 (Fabien, CA par mois passé en HT) : le même mois en TTC, sur une
   2e ligne discrète sous le montant HT. Le TTC ne disparaît pas du cockpit,
   il cède juste la vedette au chiffre sur lequel Fabien pilote. */
.chart-tip .tip-2 { font-size: 12px; font-weight: 600; opacity: .66; }
.chart-tip span { font-size: 16px; font-weight: 800; font-variant-numeric: tabular-nums; }
.chart-svg .bar { transition: opacity .12s ease; }
.chart-svg .bar-hit { cursor: default; }

/* ---- Bouton orange : action en attente de validation ---- */
.btn.orange { background: var(--orange); }
.btn.orange:hover { background: #D97D10; }
.btn.vert { background: var(--green); }
.btn.vert:hover { background: #1C8F4C; }

/* ---- KPI cochés : couleur selon l'état de validation ---- */
.kpi .value.attente { color: var(--orange); }
.kpi .value.valide { color: var(--green); }

/* ---- Nom cliquable dans la liste d'un tir ---- */
.tir-item .who .nom-lien {
  color: var(--red); font-weight: 800; cursor: pointer;
  border-bottom: 1px dotted currentColor;
}
.tir-item .who .nom-lien:hover { background: var(--red-soft); border-radius: 4px; }

/* ---- Tournées à venir (27/08) : un sous-bloc par jour, 5 lignes visibles,
        itinéraire Google Maps du jour en un clic ---- */
.tournee + .tournee { margin-top: 18px; border-top: 1px solid var(--border); padding-top: 16px; }
.tournee-head { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 14px; margin-bottom: 10px; }
.tournee-head h3 { font-size: 16.5px; font-weight: 800; letter-spacing: -.3px; }
.tournee-head .tournee-infos { color: var(--muted); font-weight: 700; font-size: 14px; }
.tournee-head .tournee-itins { margin-left: auto; display: inline-flex; gap: 8px; flex-wrap: wrap; }
.tournee-liste { list-style: none; display: flex; flex-direction: column; gap: 7px; }
.tournee-liste li { font-size: 15px; font-weight: 600; display: flex; align-items: center; gap: 9px; }
.tournee-liste .tr-txt { flex: 1 1 auto; min-width: 0; }
/* le numéro d'arrêt : la tournée se lit comme elle se roule, 1, 2, 3... */
.tournee-liste .etape {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; flex: 0 0 auto;
  border-radius: 50%; background: var(--red-soft); color: var(--red);
  font-size: 12.5px; font-weight: 800; font-variant-numeric: tabular-nums;
}
.tournee-liste .ville { color: #7B5E3B; font-weight: 600; }
.tournee-liste .jr { color: var(--muted); font-weight: 600; }
.tournee-liste .jr.cr {
  padding: 2px 9px; border-radius: 8px; font-size: 13.5px; font-weight: 700;
}
.tournee-liste .jr.cr-matin { background: var(--orange-soft); color: var(--orange-txt); }
.tournee-liste .jr.cr-aprem { background: #EAF2FB; color: #1B4E85; }
.tournee-liste .jr.cr-soir { background: #EFEAF7; color: #4B3A82; }
.tournee-liste .eur { color: var(--red); font-weight: 800; }
.tournee-liste .sep { color: var(--soft); font-weight: 700; margin: 0 1px; }
.tournee-liste .nom-lien:hover { background: var(--red-soft); border-radius: 4px; cursor: pointer; }
@media (max-width: 600px) { .tournee-head .tournee-itins { margin-left: 0; } }

/* ---- Fiche express (modale depuis un tir ou le récap) ----
   Refonte du 27/08 (Julien) : chaque sujet dans sa carte, avec du fond et des
   bords arrondis, parce que tout enchaîné ça se lisait trop tassé. */
.qk-head { display: flex; flex-direction: column; align-items: center; gap: 6px; margin-bottom: 18px; }
.qk-head .avatar-ini { width: 60px; height: 60px; font-size: 21px; margin-bottom: 2px; }
.qk-head h3 { margin: 0; }
.qk-head .intro { margin: 0; }
.qk-head #qkStatut { margin-top: 4px; }
/* 02/09 (Fabien) : la pastille de statut + « Changer le statut », puis le menu
   à la place des deux le temps du choix. Le menu prend une largeur de mot, pas
   toute la modale (règle Julien 30/08 : un sélecteur fait la taille du texte). */
#qkStatut { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 8px 10px; min-height: 36px; }
#qkStatut .sel-jbc { width: auto; min-width: 190px; }
.qk-statut-btn {
  font-family: inherit; font-size: 13.5px; font-weight: 700; color: var(--red);
  background: var(--card); border: 1.5px solid var(--red); border-radius: 20px;
  padding: 5px 13px; min-height: 32px; cursor: pointer; white-space: nowrap;
}
.qk-statut-btn:hover { background: var(--red-soft); }
.qk-statut-etat { font-size: 13.5px; font-weight: 700; color: #187A42; }
/* la pastille de statut de l'en-tête de la fiche se clique (même éditeur que
   la ligne Statut du bloc Infos) */
.badge.badge-mod { cursor: pointer; }
.badge.badge-mod:hover, .badge.badge-mod:focus-visible { box-shadow: 0 0 0 2px var(--card), 0 0 0 3.5px currentColor; outline: none; }

.qk-card { background: var(--cream); border: 1px solid var(--border); border-radius: 16px; padding: 15px 17px; }
.qk-card + .qk-card { margin-top: 12px; }
.qk-card > h4 { font-size: 13px; font-weight: 800; text-transform: uppercase; letter-spacing: .4px;
  color: var(--muted); margin-bottom: 11px; }

/* le contact : une ligne par info, donc jamais de pastille orpheline */
.qk-contact { display: grid; gap: 8px; padding: 12px; }
.qk-ct { display: flex; align-items: center; gap: 10px; background: #fff; border: 1px solid var(--border);
  border-radius: 11px; padding: 9px 12px; font-size: 14.5px; font-weight: 700; color: var(--ink);
  text-decoration: none; min-width: 0; }
.qk-ct > span:last-child { min-width: 0; overflow-wrap: anywhere; }
a.qk-ct:hover { border-color: var(--red); color: var(--red); }
.qk-ci { display: inline-flex; flex: 0 0 auto; color: var(--red); }
.qk-ci svg { width: 18px; height: 18px; }

/* son suivi : des tuiles régulières, la dernière s'étale si elle est seule */
.qk-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.qk-info { background: #fff; border: 1px solid var(--border); border-radius: 11px; padding: 9px 11px; min-width: 0; }
.qk-info:last-child:nth-child(odd) { grid-column: 1 / -1; }
.qk-info dt { color: var(--muted); font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .3px; }
.qk-info dd { font-size: 15px; font-weight: 700; margin-top: 3px; overflow-wrap: anywhere; }

.qk-card .rep { background: var(--sms-bg); border: 1px solid var(--sms-bd);
  border-radius: 12px 12px 12px 4px;
  padding: 12px 14px; font-size: 14.5px; font-weight: 600; line-height: 1.5; overflow-wrap: anywhere; }
.qk-quand { color: var(--muted); font-size: 12.5px; font-weight: 700; margin-top: 8px; text-align: right; }

.qk-liste { list-style: none; display: flex; flex-direction: column; gap: 7px; }
.qk-liste li { display: flex; align-items: center; gap: 10px; background: #fff; border: 1px solid var(--border);
  border-radius: 11px; padding: 9px 12px; font-size: 14.5px; font-weight: 700; flex-wrap: wrap; }
.qk-liste .qk-eur { color: var(--red); }
.qk-liste small { color: var(--muted); font-weight: 600; margin-left: auto; }

/* derniers échanges : un résumé lisible avant d'ouvrir la fiche complète */
.qk-fil { list-style: none; display: flex; flex-direction: column; gap: 8px; }
.qk-fil li { display: flex; gap: 10px; background: #fff; border: 1px solid var(--border);
  border-radius: 11px; padding: 10px 12px; }
.qk-fil .qk-em { flex: 0 0 auto; font-size: 15px; line-height: 1.4; }
.qk-fil > li > div { min-width: 0; }
.qk-fil .qk-date { display: block; color: var(--muted); font-size: 12px; font-weight: 800;
  text-transform: uppercase; letter-spacing: .3px; margin-bottom: 2px; }
.qk-fil .qk-txt { font-size: 14px; font-weight: 600; line-height: 1.5; overflow-wrap: anywhere; }

@media (max-width: 520px) {
  .qk-grid { grid-template-columns: 1fr; }
  .qk-card { padding: 13px 14px; }
  .qk-liste small { margin-left: 0; width: 100%; }
}

/* ---- Récap : texte à la taille du reste du cockpit (Julien, 26/08) ---- */
.recap-sec h4 { font-size: 16px; }
.recap-sec li { font-size: 15.5px; line-height: 1.5; }
.recap-sec li small { font-size: 14px; }
.recap-sec li .rep { font-size: 14.5px; margin-top: 2px; }
.recap-badges .badge { font-size: 14px; padding: 9px 14px; }
.recap-note { font-size: 14px; }

/* ---- Anniversaires : teinte à part, pour ne pas la confondre
        avec « réponses à vérifier » qui est orange ---- */
.recap-sec.violet { background: #F3EFFB; border-color: #E1D6F6; }
.recap-sec.violet h4 { color: #6B4EA8; }
.badge.violet { background: #F3EFFB; color: #6B4EA8; }

/* la fiche express peut s'ouvrir PAR-DESSUS la modale de validation du tir */
#quickModal { z-index: 120; }

/* ---- Encarts de réponse dans une modale (création de fiche) ---- */
.alerte-box { border-radius: 13px; padding: 14px 16px; font-size: 14.5px; font-weight: 600; margin-top: 16px; }
.alerte-box.red { background: var(--red-soft); color: var(--red-dark); border: 1px solid #F6D5D3; }
.alerte-box.vert { background: var(--green-soft); color: #187A42; border: 1px solid #CBEBD8; }
/* 31/08 : une limite voulue (quota du jour) n'est pas une panne : ambre, pas
   rouge, et on laisse respirer l'explication qui suit le titre. */
.alerte-box.orange { background: var(--orange-soft); color: var(--ink); border: 1px solid #F3DDBA; }
.alerte-box.orange b { color: var(--orange); font-weight: 800; }
.alerte-box.orange small { display: block; margin-top: 7px; color: var(--muted); font-size: 13px; line-height: 1.5; }
.alerte-box ul { list-style: none; display: flex; flex-direction: column; gap: 6px; font-weight: 600; font-size: 13.5px; }
.alerte-box li::before { content: '⚠️ '; }
.alerte-box .link-more { margin-top: 4px; }


/* Dépliage « Voir les N autres » (récap ET tournées, d'où les sélecteurs sans
   préfixe). Julien 27/08 : rouge, plus gros, chevron en trait arrondi à DROITE
   du libellé, ALIGNÉ sur le texte (svg en display:block dans un conteneur
   flex : plus de décalage de baseline), et jamais souligné : seul le libellé
   prend le soulignement au survol. */
.voirplus { list-style: none; margin-top: 6px; }
.voirplus .lnk { background: none; border: 0; cursor: pointer;
  color: var(--red); font: inherit; font-size: 15px; font-weight: 800; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  padding: 7px 10px; margin-left: -10px; border-radius: 9px; }
.voirplus .lnk .fl { display: inline-flex; align-items: center; color: var(--red); }
.voirplus .lnk .fl .fi-ic { display: block; width: 15px; height: 15px; }
.voirplus .lnk:hover { color: var(--red-dark); }
.voirplus .lnk:hover .fl { color: var(--red-dark); }
.voirplus .lnk:hover .lb { text-decoration: underline; }


/* 💬 Fil SMS complet de la fiche client (26/08/2026) : scroll interne,
   bulles envoyé (crème, gauche) / reçu (vert, droite), dernier en bas. */
.sms-fil { max-height: 400px; overflow-y: auto; padding-right: 6px;
  scrollbar-width: thin; overscroll-behavior: contain; }
.sms-b { border-radius: 14px 14px 14px 4px; padding: 14px 16px;
  font-size: 14.5px; font-weight: 600; background: var(--cream); max-width: 88%; }
.sms-out { align-self: flex-start; }
.sms-in { background: var(--green-soft); border-radius: 14px 14px 4px 14px;
  align-self: flex-end; }
.sms-meta { color: var(--muted); font-size: 12.5px; font-weight: 600; margin-top: 6px; }
.sms-meta.in { color: #187A42; }

/* ---- Bloc « Infos de la fiche » : deux colonnes par sujet, tout affiché,
   tout corrigeable au clic (26/08/2026) ----
   L'animal et le contact à gauche, la livraison à droite, les notes en pleine
   largeur. Les catégories VIDES restent visibles (« à compléter ») pour qu'on
   voie ce qui manque. Icônes : tracés Tabler inline, teintés par currentColor. */
.fiche-infos { display: grid; grid-template-columns: 1fr 1fr; gap: 0 34px; align-items: start; }
.fi-grp + .fi-grp { margin-top: 20px; }
.fi-grp h3 {
  display: flex; align-items: center; gap: 8px;
  font-size: 13px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase;
  color: var(--red); margin-bottom: 6px;
}
.fi-grp h3 .fi-ic { color: var(--red); width: 18px; height: 18px; }
.fi-l {
  display: grid; grid-template-columns: 20px auto minmax(0, 1fr);
  align-items: center; gap: 12px;
  padding: 10px 0; border-bottom: 1px solid var(--border);
}
.fi-grp .fi-l:last-child { border-bottom: 0; }
.fi-i { display: flex; align-items: center; justify-content: center; }
.fi-ic { width: 19px; height: 19px; flex-shrink: 0; color: var(--soft); }
.fi-k {
  color: var(--muted); font-weight: 600; font-size: 15px;
  white-space: nowrap; min-width: 0; overflow: hidden; text-overflow: ellipsis;
}
.fi-v {
  font-weight: 700; font-size: 15.5px; text-align: right;
  border-radius: 8px; padding: 3px 7px; margin: -3px -7px;
  transition: background .15s;
  /* 🔴 01/09 (Julien) : « la zone de texte est trop large, réduis-la vers la
     gauche ». La valeur occupait toute la colonne restante, du libellé jusqu'au
     bord de la carte : le fond de survol s'étalait sur 40 cm de vide. Elle se
     serre maintenant sur son texte et reste calée à droite. Le plafond de
     largeur reste la colonne, donc les valeurs longues se coupent toujours en
     « … » avec le texte entier en info-bulle. */
  justify-self: end; max-width: 100%;
  /* une valeur trop longue (produit habituel, adresse) est coupée proprement :
     le texte entier vient en info-bulle, et le clic l'ouvre en entier. */
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.fi-v.edit { overflow: visible; white-space: normal; }
.fi-v a { color: inherit; text-decoration: none; }
.fi-v a:hover { text-decoration: underline; }
/* 🔴 30/08 (Julien) : « j'aimerais une infobulle ou une sélection qui indique
   qu'on peut modifier ». L'indice devient PERMANENT (soulignement pointillé au
   repos, pas seulement au survol), et le survol ajoute la pastille rouge douce,
   un crayon et l'infobulle maison : on voit ce qui se corrige sans avoir à
   promener la souris sur toute la fiche. */
/* 🔴 01/09 (Julien) : « j'aime pas les zones de sélection, mets juste un fond
   rouge au survol, pas de pointillés, garde le petit stylo c'est bien ». Le
   soulignement pointillé permanent (posé le 30/08) quadrillait la fiche de
   traits ; il disparaît. Ce qui reste : rien au repos, un fond rouge doux et
   le crayon au survol. */
.fi-v.mod { cursor: text; position: relative; }
/* 🔴 01/09 (Julien) : « les cases qu'on peut modifier ne sont pas alignées avec
   celles que l'on ne peut pas, je veux bien tout aligner ». Le crayon du survol
   est un pseudo-élément EN LIGNE : même invisible au repos, il prenait ses
   12 px + 6 px de marge DANS la boîte de la valeur. Les valeurs modifiables
   finissaient donc 18 px plus à gauche que les valeurs calculées (l'âge, lu de
   la date d'anniversaire), et la colonne de droite était en dents de scie.
   La gouttière du crayon est maintenant réservée sur TOUTES les valeurs, la
   vignette n'étant peinte que sur celles qui se modifient : un seul bord droit
   pour toute la fiche, et le crayon reste exactement où il était. */
.fi-v::after {
  content: ''; display: inline-block; width: 12px; height: 12px;
  margin-left: 6px; vertical-align: -1px;
  opacity: 0;
}
.fi-v.mod::after {
  background: url('img/ic-pencil-red.png?v=2') center/contain no-repeat;
}
.fi-v.mod:hover, .fi-v.mod:focus-visible {
  background: var(--red-soft);
  color: var(--red-dark); outline: none;
}
.fi-v.mod:hover::after, .fi-v.mod:focus-visible::after { opacity: .75; }
.fi-v.mod::before {
  content: 'Cliquer pour modifier';
  position: absolute; bottom: calc(100% + 7px); right: 0;
  padding: 5px 10px; border-radius: 9px;
  background: var(--ink); color: #FFFFFF;
  font-size: 12px; font-weight: 700; white-space: nowrap; letter-spacing: .2px;
  opacity: 0; pointer-events: none; z-index: 5;
}
.fi-v.mod:hover::before { opacity: 1; }
/* pendant l'édition, ni crayon ni infobulle : le champ parle de lui-même */
.fi-v:has(.fi-in)::after, .fi-v.mod:has(.fi-in)::before { display: none; }
/* 🔴 01/09 (Julien) : « quand on clique sur Chat ou sur Persan, une sélection
   apparaît sur toute la largeur de la ligne, rapidement, puis disparaît ».
   MESURÉ : au clic, la cellule passait de 104,9 px à 395,8 px (elle s'étirait
   pour accueillir le champ) pendant que le fond rouge du SURVOL était encore
   peint dessus, et la transition de 0,15 s le faisait fondre tranquillement sur
   cette boîte élargie : une barre rouge qui traverse la ligne, puis s'efface.
   Deux corrections, chacune suffirait, les deux valent mieux :
   1. le fond et la transition sont coupés NET dès que la cellule passe en
      édition, survol compris (`.edit` arrive avant le champ : pendant qu'un
      menu charge ses options, la cellule était rouge et large sans rien
      dedans) ;
   2. la cellule ne s'étire plus au moment du CLIC mais au moment où le champ
      est réellement là, et un MENU ne s'étire plus du tout (voir plus bas). */
/* 🔴 01/09 (Julien, 2e passe) : « attention, des sélections orange qui traînent
   sur toute la longueur ». Le fond ROUGE du survol était bien coupé en édition,
   mais pas l'AMBRE des valeurs vides (« à compléter ») : sa règle
   `.fi-v.vide.mod:hover` pèse autant que la mienne et se déclare plus bas, donc
   elle gagnait. Un champ vide s'ouvre justement sur une valeur vide : c'était le
   cas le plus fréquent. On ajoute le sélecteur qui la bat, avec sa classe
   `.vide`. 📌 Une collision CSS se règle en comparant l'ORDRE autant que la
   spécificité : à égalité, c'est la dernière déclarée qui l'emporte. */
.fi-v.mod:has(.fi-in), .fi-v.edit, .fi-v.mod.edit:hover, .fi-v.mod.edit:focus-visible,
.fi-v.vide.mod.edit:hover, .fi-v.vide.mod:has(.fi-in) {
  background: none; transition: none;
}
/* en édition, le champ reprend TOUTE la largeur disponible : on écrit dedans */
.fi-v.mod:has(.fi-in) { justify-self: stretch; }
/* 🔴 01/09 (Julien) : « c'est mieux pour le menu dans la page client, mais c'est
   trop large et pas aussi bien présenté que dans factures ». Un CHAMP a besoin
   de toute la place (on écrit dedans, la valeur peut être longue) ; un MENU,
   non : il n'affichera jamais que le libellé d'une option. Étiré sur la colonne
   entière, le bouton laissait 400 px de vide entre le mot et son chevron, et le
   panneau, dont la largeur PART DE CELLE DU BOUTON, s'étalait d'autant. Le menu
   se serre donc sur son contenu et reste calé à droite, exactement comme la
   cellule Statut du tableau des factures. */
.fi-v.mod:has(select.fi-in), .fi-v.edit:has(select.fi-in) { justify-self: end; }
.fi-v .sel-jbc { width: auto; min-width: 132px; max-width: 100%; }
.fi-v.ro { cursor: default; }
.fi-v.ro i { color: var(--soft-txt); font-style: italic; font-weight: 600; }
/* ce qui manque saute aux yeux (Julien 26/08) : par la COULEUR ambre seule,
   le soulignement pointillé étant retiré partout le 01/09 */
.fi-v.vide.mod i {
  color: var(--orange); font-style: normal; font-weight: 700;
}
.fi-v.vide.mod:hover { background: var(--orange-soft); }
.fi-v.ok { background: var(--green-soft); }
/* 🔴 01/09 (Julien) : « quand je clique, ça fait changer la hauteur de la ligne
   "Vendu par" ou le mode de règlement ; je voudrais que ça ne bouge pas, comme
   pour les clients et les factures ». MESURÉ au banc (maquette/_banc-fi-hauteur.html,
   Chrome) : au repos une ligne fait 44,25 px, elle passe à 61,00 px avec un
   champ de saisie et à 63,50 px avec un menu, soit un saut de 17 à 19 px, et
   toute la fiche descendait d'un cran sous les yeux.
   Même remède que le tableau des factures le matin même : on ne PARIE pas sur
   « la hauteur du plus grand des deux états », on RETIRE la variable. Tout ce
   qui peut occuper la valeur d'une ligne (texte, « à compléter » en italique,
   champ de saisie, menu maison, « … » d'attente) tient dans la MÊME boîte de
   38 px. Détail que ça corrige au passage : la ligne vide mesurait déjà 1 px de
   moins que la ligne pleine, la métrique de l'italique suffisant à la bouger.
   Exception assumée : le CATALOGUE de produits, qui est un panneau de recherche
   et pas une valeur ; lui a le droit de pousser sa ligne. */
.fi-v {
  box-sizing: border-box; height: 38px; line-height: 32px;
  /* 🔴 01/09 soir (passe mobile) : flex-START, plus flex-end. Sur téléphone,
     « Produit habituel », « Secteur » ou « Dernière commande » s'affichaient
     coupés AU DÉBUT (« ère Pine 6L », « BIN DES ORMEAUX ») : un texte posé
     directement dans la boîte est un item flex ANONYME, que text-overflow ne
     sait pas tronquer, et flex-end le calait par la fin, donc le début sortait
     de la boîte. La boîte étant déjà serrée sur son texte et calée à droite
     par justify-self:end, flex-start ne change rien quand ça tient, et garde
     le début lisible quand ça ne tient pas. Le « … » lui-même vient du span
     .fi-t que le front pose maintenant autour de chaque valeur texte. */
  display: flex; align-items: center; justify-content: flex-start;
}
/* le texte garde sa coupe en « … » : c'est LUI qui porte l'ellipse, la boîte
   n'étant plus qu'un cadre de hauteur fixe */
.fi-v > * { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fi-v .fi-ed { display: block; width: 100%; height: 32px; }
.fi-v .fi-in, .fi-v .sel-jbc {
  box-sizing: border-box; height: 32px; line-height: 1;
  padding-top: 0; padding-bottom: 0;
}
.fi-v .fi-attente { display: inline-flex; align-items: center; height: 32px; }
/* le catalogue de produits : un panneau, pas une valeur */
.fi-v:has(.fi-prod) { height: auto; display: block; }
.fi-v:has(.fi-prod) .fi-ed { height: auto; }
.fi-v:has(.fi-prod) .fi-in { height: auto; padding-top: 7px; padding-bottom: 7px; }
.fi-attente { color: var(--muted); font-weight: 600; font-size: 14px; font-style: italic; }
/* l'éditeur de ligne */
.fi-ed { display: block; }
/* 🔴 30/08 (Julien) : « les sélecteurs pour ajouter des infos ne devraient pas
   être si longs, juste la taille moyenne pour le texte prévu ». Le champ ne
   prend plus toute la ligne : sa largeur est posée en JS d'après ce qu'on
   attend dedans (un poids tient en 8 signes, une adresse en 34). Le min-width
   de 150 px est retiré : c'est lui qui écrasait les 90 px du champ de stock et
   poussait « Stock réel » sur deux lignes. */
.fi-in {
  width: 100%; min-width: 0; max-width: 100%; margin-left: auto;
  font: inherit; font-size: 16px; font-weight: 600;   /* 16px : pas de zoom au tap sur iOS */
  text-align: right;
  padding: 7px 10px; border: 1.5px solid var(--red); border-radius: 10px;
  background: #FFFFFF; color: var(--ink);
}
textarea.fi-in { text-align: left; resize: none; line-height: 1.45; }
.fi-in:focus { outline: none; box-shadow: 0 0 0 3px var(--red-soft); }
/* 🔴 28/08 (Julien) : « les petites flèches ne sont pas très jolies ». Les
   compteurs natifs des champs nombre cassent l'harmonie : retirés partout. */
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
input[type="number"] { -moz-appearance: textfield; appearance: textfield; }
/* (les pastilles d'espèce et leur bouton vert ✓ sont retirés le 01/09 : l'espèce
   passe par le menu déroulant maison, comme tous les autres choix du cockpit) */
/* les notes : pleine largeur, titre au-dessus, cadre d'écriture toujours là */
.fi-notes {
  grid-column: 1 / -1; display: block;
  margin-top: 20px; padding: 14px 0 0; border-top: 1px solid var(--border); border-bottom: 0;
}
/* 28/08 (Julien) : « c'est trop collé à l'icône » ; et le titre suit la
   même livrée que L'ANIMAL / LE CONTACT / LA LIVRAISON (rouge, majuscules) */
.fi-notes h3 {
  display: flex; align-items: center; gap: 9px; margin-bottom: 9px;
  font-size: 13px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase;
  color: var(--red);
}
.fi-notes h3 .fi-ic { width: 18px; height: 18px; flex: 0 0 auto; }
/* …et le cadre est posé d'emblée, sans « à compléter » ni poignée de
   redimensionnement (Julien : « laisse un cadre comme ça direct »). */
.fi-notes-in {
  text-align: left; resize: none; min-height: 92px;
  border: 1px solid transparent; background: #FAF6EE;
}
.fi-notes-in:focus { background: #FFFFFF; border-color: var(--border); }
.fi-notes-etat { display: block; margin-top: 7px; font-size: 13.5px; font-weight: 700; color: var(--muted); }
.fi-notes-etat.ok { color: #146B3C; }
.fi-notes .fi-v {
  display: block; text-align: left; margin: 0; padding: 4px 0;
  font-weight: 600; font-size: 15px; line-height: 1.5;
  white-space: normal; overflow: visible; overflow-wrap: anywhere;
}
@media (max-width: 900px) {
  .fiche-infos { grid-template-columns: 1fr; }
  .fi-col + .fi-col .fi-grp:first-child { margin-top: 20px; }
}

/* ---- Horloges : compte à rebours avant le prochain passage (26/08/2026) ----
   L'anneau se remplit entre le passage précédent et le suivant, l'aiguille
   tourne en continu pour montrer que c'est vivant. Sur l'accueil : le départ
   des SMS et les confirmations de livraison. Sur la page Robots : chacun. */
.hz-bloc { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border); width: 100%; }
.hz-l { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 7px 0; }
.hz-l + .hz-l { border-top: 1px dashed var(--border); }
.hz-nom { font-size: 13.5px; font-weight: 700; color: var(--muted); text-align: left; }
.hz { display: inline-flex; align-items: center; gap: 10px; }
.hz-dial { width: 34px; height: 34px; flex-shrink: 0; overflow: visible; }
.hz-bg { fill: none; stroke: var(--border); stroke-width: 4; }
.hz-arc {
  fill: none; stroke: var(--red); stroke-width: 4; stroke-linecap: round;
  /* 🔴 29/08 (Julien : « le trait rouge à gauche ») : l'origine « 22px 22px »
     était calibrée pour le cadran de 34px ; dans la pastille, le cadran est
     RÉDUIT à 20px et cette origine tombait HORS du cadran : l'arc tourné de
     -90° orbitait vers la gauche et un fragment sortait du cercle.
     `view-box` + 50% = le centre du viewBox, à N'IMPORTE QUELLE taille. */
  transform: rotate(-90deg); transform-box: view-box; transform-origin: 50% 50%;
  transition: stroke-dashoffset .9s linear;
}
.hz-hand {
  stroke: var(--ink); stroke-width: 2; stroke-linecap: round;
  transform-box: view-box; transform-origin: 50% 50%;
  /* l'angle vient du JS (hzTic) : l'aiguille pointe où la bande rouge
     s'arrête, plus d'animation décorative qui racontait autre chose */
  transition: transform .6s ease;
}
.hz-txt { display: flex; flex-direction: column; line-height: 1.25; text-align: right; }
.hz-txt b { font-size: 15px; font-weight: 800; font-variant-numeric: tabular-nums; color: var(--ink); }
.hz-txt small { font-size: 12px; font-weight: 600; color: var(--muted); }
/* moins d'un quart d'heure : ça vire au rouge */
.hz.proche .hz-txt b { color: var(--red); }
.hz.proche .hz-hand { stroke: var(--red); }
/* dans l'en-tête d'un bloc : pas de trait, juste le cadran et son texte */
.head-right { display: inline-flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.hz-inline:empty { display: none; }
.hz-inline .hz-l { padding: 0; border: 0; gap: 10px; }
.hz-inline .hz-nom { font-size: 13px; }
/* sur la page Robots, l'horloge est posée au bas de la carte */
.robot-hz { margin-top: 10px; padding-top: 10px; border-top: 1px dashed var(--border); display: flex; justify-content: flex-end; }
@media (max-width: 420px) {
  .hz-l { flex-direction: column; align-items: flex-start; gap: 6px; }
  .hz-txt { text-align: left; }
}


/* ================================================================
   ALIGNEMENT ICÔNE / TEXTE : règle générale (Julien 28/08 : « icônes et
   textes pas alignés, vérifie partout »).

   Un `align-items: center` aligne les BOÎTES : l'icône se centre sur la boîte
   de ligne, qui compte le vide au-dessus des capitales et sous la baseline.
   L'œil, lui, voit le texte de la CAPITALE à la BASELINE. Mesuré en headless :
   les icônes tombaient 3,7 à 7,3 px trop bas selon les blocs.

   On aligne donc sur la baseline, puis on redescend l'icône d'exactement
   (hauteur d'icône − hauteur de capitale) / 2. La hauteur de capitale vaut
   ~0,73 em dans Plus Jakarta Sans, d'où le 0.365em : la formule s'adapte
   toute seule à la taille du texte.
   ================================================================ */
.page-head .sub,
.block-head h2,
.fi-grp h3,
.fi-notes h3,
.nav-souci,
.tag { align-items: baseline; }

.page-head .sub img { position: relative; top: calc(10px - 0.365em); }   /* icône 20 px */
.block-head h2 img  { position: relative; top: calc(11px - 0.365em); }   /* icône 22 px */
.fi-grp h3 .fi-ic,
.fi-notes h3 .fi-ic { position: relative; top: calc(9px - 0.365em); }    /* icône 18 px */
.nav-souci img      { position: relative; top: calc(9px - 0.365em); }    /* icône 18 px */
.tag img            { position: relative; top: calc(7.5px - 0.365em); }  /* icône 15 px */

/* ================================================================
   🔴 30/08/2026 : AVOIRS, FACTURES ANNULÉES ET NOTES (retec Julien)
   « Tu barres la facture qui a été annulée, et statut : pas reçu, mais annulé.
     On voit que l'avoir au-dessus lui est associé directement. »
   ================================================================ */

/* Le numéro d'une pièce morte : barré, en gris, partout où il s'écrit. */
.num-annule { text-decoration: line-through;
              text-decoration-color: var(--red); text-decoration-thickness: 2px;
              color: var(--muted); }
/* La ligne entière recule d'un ton : elle est là pour l'historique, pas pour
   l'action. On garde le contraste du texte lisible (pas d'opacité globale). */
.ligne-annulee td { background: #FBF8F4; }
.ligne-annulee .mnt { opacity: .62; }
/* Le tag de statut : ni « Reçu » (rien n'a été encaissé), ni « En attente »
   (personne n'attend rien). Un mot à lui, dans le rouge doux de la maison. */
.badge.annule { background: var(--red-soft); color: var(--red-dark); }
/* Dans le parcours du client, la même pièce se barre aussi. */
.tl-item .txt.piece-annulee { text-decoration: line-through;
                              text-decoration-color: var(--red); color: var(--muted); }

/* Le post-it : petit, discret, à côté du numéro, et seulement s'il y a
   quelque chose à lire. Le clic ouvre la note sans ouvrir la facture.
   🔴 30/08 (Julien) : « je le veux jaune ». Un vrai carré jaune dessiné en
   CSS, coin bas droit corné (clip-path + pli plus foncé), identique partout,
   là où l'émoji 🗒️ rendait gris et changeait selon la plateforme. */
.postit { border: 0; padding: 0; cursor: pointer; position: relative;
          display: inline-block; width: 15px; height: 15px;
          margin-left: 3px; vertical-align: -2px;
          background: linear-gradient(160deg, #FFE996 0%, #FFD84D 100%);
          border-radius: 2px;
          clip-path: polygon(0 0, 100% 0, 100% 62%, 62% 100%, 0 100%);
          box-shadow: inset 0 0 0 1px rgba(190, 155, 45, .35); }
.postit::after { content: ''; position: absolute; right: 0; bottom: 0;
                 width: 0; height: 0; border-style: solid;
                 border-width: 6.5px 0 0 6.5px;
                 border-color: #E8B93B transparent transparent transparent;
                 transform: rotate(-45deg); transform-origin: 100% 0; }
.postit:hover { transform: scale(1.2); }
/* la même pastille, posée dans un titre ou une carte (non cliquable) */
.pi-inline { cursor: default; margin: 0 7px 0 0; }
.pi-inline:hover { transform: none; }
.pi-titre { width: 18px; height: 18px; vertical-align: -2px; }

/* Le lien entre les deux pièces, en tête du mini-menu : une facture annulée
   dit par quel avoir, un avoir dit quelle facture. Un clic ouvre l'autre. */
/* 🔴 01/09 (Fabien) : noter comment une facture a été réglée, en une fois ou
   en plusieurs. Une ligne = un mode et son montant. Le montant n'apparaît
   qu'à partir de deux règlements : avec un seul, c'est la facture entière et
   il n'y a rien à saisir. */
.dlg-regl { max-width: 460px; text-align: left; }
.dlg-regl h3 { text-align: center; }
.regl-lignes { display: flex; flex-direction: column; gap: 9px; margin-bottom: 12px; }
.regl-l { display: flex; align-items: center; gap: 8px; }
.regl-l .regl-mode { flex: 1 1 auto; min-width: 0; }
/* 16 px minimum : sous ce seuil, Safari iOS zoome au tap et la page part en
   défilement horizontal (règle maison, skill ios-responsive-hardening). */
.regl-l .regl-m { flex: 0 0 106px; text-align: right; font-variant-numeric: tabular-nums;
                  font-size: 16px; }
.regl-l .regl-x {
  flex: 0 0 auto; width: 34px; height: 34px; border-radius: 10px;
  border: 1px solid var(--border); background: none; color: var(--muted);
  font-size: 19px; line-height: 1; cursor: pointer;
}
.regl-l .regl-x:hover { border-color: var(--red); color: var(--red); }
.regl-plus {
  background: none; border: 0; padding: 0; cursor: pointer;
  color: var(--red); font-family: inherit; font-weight: 700; font-size: 14.5px;
}
.regl-plus:hover { text-decoration: underline; }
.regl-etat { margin: 14px 0 18px !important; font-size: 14.5px; font-weight: 700; }
.regl-etat.juste { color: var(--green); }
.regl-etat.manque { color: var(--orange); }
.dlg-btns button[data-a="ok"]:disabled { opacity: .45; cursor: not-allowed; }

.dlg-lie { display: flex; align-items: center; justify-content: space-between;
           gap: 12px; width: 100%; text-align: left; margin-bottom: 14px;
           padding: 11px 14px; border: 1px solid #F6D5D3; border-radius: 12px;
           background: var(--red-softer); color: var(--ink);
           font: inherit; font-size: 14px; font-weight: 600; cursor: pointer; }
.dlg-lie:hover { border-color: var(--red); background: var(--red-soft); }
.dlg-lie b { font-weight: 800; }
.dlg-lie .dlg-fl { color: var(--red); font-weight: 800; white-space: nowrap; }

/* La note déjà écrite, rappelée dans le mini-menu. */
.dlg-note { background: var(--sms-bg); border: 1px solid var(--sms-bd);
            border-radius: 12px; padding: 11px 14px; margin-bottom: 14px; }
.dlg-note-t { display: block; font-size: 12px; font-weight: 800; letter-spacing: .3px;
              text-transform: uppercase; color: #8A6B14; margin-bottom: 4px; }
.dlg-note p { font-size: 14px; font-weight: 600; line-height: 1.5;
              white-space: pre-wrap; overflow-wrap: anywhere; }

/* 🔴 « Voir la facture » porte le cadre rouge : c'est le geste de tous les
   jours, il doit se distinguer des deux autres choix au premier regard. */
.dlg-c-vedette { border: 2px solid var(--red); background: var(--red-softer); }
.dlg-c-vedette b { color: var(--red-dark); }
.dlg-c-vedette:hover { background: var(--red-soft); border-color: var(--red-dark); }

/* La fenêtre de note : lecture (post-it) ou écriture (mini-menu). */
.dlg-note-m { max-width: 460px; text-align: left; }
.dlg-note-m h3 { text-align: center; }
.note-lue { background: var(--sms-bg); border: 1px solid var(--sms-bd);
            border-radius: 12px; padding: 14px 16px; margin-bottom: 18px;
            font-size: 15px; font-weight: 600; line-height: 1.55;
            white-space: pre-wrap; overflow-wrap: anywhere; }
/* 🔴 16px mini : sous cette taille, Safari iOS zoome au tap et la page
   zoomée devient scrollable horizontalement (règle JBC). */
.note-in { width: 100%; font: inherit; font-size: 16px; font-weight: 600;
           padding: 12px 14px; border: 1px solid var(--border); border-radius: 12px;
           background: #FFFFFF; color: var(--ink); resize: none; line-height: 1.5; }
.note-in:focus { outline: none; border-color: var(--red); }
.note-aide { color: var(--muted); font-size: 12.5px; font-weight: 600;
             line-height: 1.45; margin: 8px 0 16px; }
/* (l'habit des boutons de boîte de dialogue est posé une seule fois, plus haut) */

/* 🔴 « Un cadre de couleur comme pour le CA par poste, sous statut, rassemblés
   sous vendeur. » Ce groupe ne parle pas du paiement mais de la NATURE de la
   pièce : il a son propre cadre, pour qu'on ne le lise pas comme un statut. */
/* 🔴 30/08 (Julien, « là ça bug ») : deux fautes corrigées ici. (1) le fond
   blanc posé sur TOUS les .chip-f du cadre écrasait aussi celui du tag CHOISI
   (même spécificité, règle plus bas dans le fichier) : « Toutes » se rendait
   blanc sur blanc, comme un tag vide. Le :not(.on) règle ça. (2) sa demande :
   UN fond commun pour tout ce qui décrit l'état de la pièce (Statut + Avoir /
   Annulée), un AUTRE fond pour les vendeurs. Deux cadres, deux teintes. */
/* 🔴 30/08 (Julien, 3e passe : « réduis la taille pour les mettre sur la même
   ligne ») : chips compactées pour que les deux cadres tiennent côte à côte
   sur ordi. Et le « Toutes » caché du groupe Pièce s'affichait quand même :
   le display:inline-flex de .chip-f BAT l'attribut hidden (style UA), il faut
   le dire en CSS. */
.chip-f[hidden] { display: none !important; }
.chips-cadre { padding: 6px 11px; border-radius: 12px; }
.chips-cadre .chip-f:not(.on) { background: #FFFFFF; }
/* variante compacte de bureau (groupes période / statut / vendeur) ; sur
   téléphone elle reprend la cible commune de 44 px (bascule 860) */
.chips-cadre .chip-f { font-size: 13px; min-height: 34px; padding: 0 10px; border-radius: var(--r-sm); gap: 5px; }
.chips-cadre .chips-lib { font-size: 11.5px; }
.chips-cadre .chips-groupe { gap: 6px; }
/* 🔴 30/08 (Julien) : « pas besoin du mot PIÈCE, mets-les à la suite des
   autres ». Les deux groupes restent des filtres distincts, mais se lisent
   comme une seule rangée : un seul libellé, un écart régulier. */
.chips-cadre-etat { border: 1px solid #F6D5D3; background: var(--red-softer);
                    display: flex; flex-wrap: wrap; gap: 8px 6px; }
.chips-cadre-vendeur { border: 1px solid var(--border); background: var(--cream-deep); }
/* 🔴 30/08 (Julien) : la barre de tags de l'inventaire. Chaque étiquette prend
   une fois choisie la couleur EXACTE de la pastille de statut de la même ligne
   du tableau : on relie l'étiquette à ce qu'elle filtre sans lire le mot. */
.chips-cadre-stock { border: 1px solid var(--border); background: var(--cream-deep); }
.chip-f.c-epuise.on { background: var(--red-soft); border-color: var(--red); }
.chip-f.c-epuise.on b { color: var(--red); }
.chip-f.c-arecom.on { background: #FAECDC; border-color: var(--orange-txt); }
.chip-f.c-arecom.on b { color: var(--orange-txt); }
.chip-f.c-bientot.on { background: #F4E2DD; border-color: #B3320B; }
.chip-f.c-bientot.on b { color: #B3320B; }
.chip-f.c-stockok.on { background: #DFECE5; border-color: #187A42; }
.chip-f.c-stockok.on b { color: #187A42; }
/* 02/09 : les trois étiquettes des produits autrefois cachés. Le négatif est
   une anomalie : même rouge que l'épuisé. Zéro et sans chiffre : neutres. */
.chip-f.c-negatif.on { background: var(--red-soft); border-color: var(--red); }
.chip-f.c-negatif.on b { color: var(--red); }
.chip-f.c-zero.on, .chip-f.c-inconnu.on, .chip-f.c-retire.on { background: #ECE8E1; border-color: var(--muted); }
.chip-f.c-zero.on b, .chip-f.c-inconnu.on b, .chip-f.c-retire.on b { color: var(--muted); }
.chip-f.c-negatif:not(.on):hover { border-color: var(--red); color: var(--red); }
.chip-f.c-zero:not(.on):hover, .chip-f.c-inconnu:not(.on):hover, .chip-f.c-retire:not(.on):hover { border-color: var(--muted); }
.chip-f.c-epuise:not(.on):hover  { border-color: var(--red); color: var(--red); }
.chip-f.c-arecom:not(.on):hover  { border-color: var(--orange-txt); color: var(--orange-txt); }
.chip-f.c-bientot:not(.on):hover { border-color: #B3320B; color: #B3320B; }
.chip-f.c-stockok:not(.on):hover { border-color: #187A42; color: #187A42; }
/* 🔴 30/08 (Julien) : « au hover le texte disparaît ». Ces deux règles-ci
   arrivent APRÈS le « .chip-f.on:hover { color: #fff } » du 29/08, à
   spécificité égale : elles gagnaient, et repeignaient le texte du tag ACTIF
   dans sa propre couleur de fond (violet sur violet). Le survol ne colore
   donc plus que les tags NON choisis. */
.chip-f.c-avoir.on { background: #EAE6F3; border-color: #6B4EA8; }
.chip-f.c-avoir.on b { color: #6B4EA8; }
.chip-f.c-avoir:not(.on):hover { border-color: #6B4EA8; color: #6B4EA8; }
.chip-f.c-annulee.on { background: var(--red-soft); border-color: var(--red-dark); }
.chip-f.c-annulee.on b { color: var(--red-dark); }
.chip-f.c-annulee:not(.on):hover { border-color: var(--red-dark); color: var(--red-dark); }

/* Mobile : le cadre respire un peu moins, et les groupes s'empilent. */
@media (max-width: 720px) {
  .chips-filtres { gap: 10px 14px; }
  .chips-cadre { width: 100%; }
}

/* 🔴 30/08 (Julien) : une ligne de « Commandes à préparer » sans extrait SMS
   n'est plus muette. Elle dit qu'il n'y a pas eu de SMS, et quand ça part.
   Ton neutre, jamais le rouge des vraies réponses de clients. */
.recap-sec li .rep.rep-sanssms { color: var(--muted); font-style: normal;
                                 font-weight: 600; font-size: 13.5px; }
.recap-sec li .rep.rep-sanssms .sep { color: var(--soft); }

/* 🔴 30/08 (Julien) : « ici c'est tout tassé. Le 18h54, tu peux le mettre en
   haut à droite comme pour les factures du jour à préparer, et rééquilibrer.
   Je ne veux pas que tout soit tassé, c'est moins joli. »
   Le compte à rebours passe en badge d'angle (même motif .kpi-coin que la
   carte Factures) et libère le bas de la carte. Le haut réserve sa bande pour
   que le titre centré ne coure jamais sous le badge, et les quatre éléments
   (titre, chiffre, état, bouton) reçoivent chacun leur respiration.
   Ces règles arrivent APRÈS celles du 26-29/08 et les remplacent volontairement :
   la pastille n'est plus dans le flux, ses anciennes marges n'ont plus de sens. */
.tir-card.tir-kpi { position: relative; padding: 46px 22px 24px; }
.tir-card.tir-kpi .hz-pill.kpi-coin {
  position: absolute; top: 14px; right: 14px; margin-top: 0;
}
.tir-card.tir-kpi .t1 { margin-bottom: 16px; }
/* 🔴 02/09 (Julien, mobile) : « prochain tir SMS est trop collé au compteur en
   haut à droite ». Le badge d'angle mesure en vrai 27 px de haut (cadran de
   20 px + rembourrage), posé à 14 px du bord : il finit à 41 px, et la bande
   réservée de 46 px ne laissait que 5 px d'air au titre sur une carte serrée
   (sur ordinateur l'espace élastique masquait le défaut). On réserve 62 px. */
.tir-card.tir-kpi:has(.hz-pill.kpi-coin) { padding-top: 62px; }
.tir-card.tir-kpi .t2 { margin-top: auto; }
.tir-card.tir-kpi .t3 { margin-top: 9px; margin-bottom: auto; }
.tir-card.tir-kpi .t2.t2-date { margin-bottom: 0; }
.tir-card.tir-kpi .btn { margin-top: 20px; }
.tir-card.tir-kpi .done-note { margin-top: 12px; }

/* 🔴 01/09 (Julien) : « Modifier le tir » se pose à GAUCHE de l'action
   principale. Il reste discret (fantôme, petit) : le geste courant est de
   valider, revenir dessus est l'exception. La rangée porte la marge haute et
   la largeur de l'ancien bouton seul, sinon les deux se cumulaient ; et sur
   une carte étroite les boutons passent l'un sous l'autre plutôt que de se
   comprimer jusqu'à couper leur texte.
   ⚠️ Ces règles doivent rester APRÈS « .tir-card.tir-kpi .btn » : même poids
   de sélecteur, c'est l'ordre qui tranche. */
.tir-card .tir-actions {
  display: flex; align-items: stretch; flex-wrap: wrap; gap: 10px;
  width: 100%; max-width: 320px; margin-top: 20px;
}
.tir-card .tir-actions .btn { width: auto; max-width: none; margin-top: 0; flex: 1 1 auto; }
.tir-card .tir-actions #btnModifierTir { flex: 0 1 auto; white-space: nowrap; }
.tir-card .tir-actions #btnModifierTir[hidden] { display: none; }

@media (max-width: 600px) {
  /* carte pleine largeur : le badge garde son coin, la bande du haut peut
     se resserrer puisque le titre a toute la place. */
  .tir-card.tir-kpi { padding: 42px 20px 22px; }
  .tir-card.tir-kpi .hz-pill.kpi-coin { top: 11px; right: 11px; }
}


/* ================================================================
   📥 30/08/2026 : ACHATS DE STOCK (retec Julien)
   « Ajouter/recharger un produit, avec la facture de l'achat associée. »
   ================================================================ */
.dlg-achat { max-width: 520px; text-align: left; }
.dlg-achat h3 { text-align: center; }
.ac-prod { margin-bottom: 14px; }
.ac-resultats { display: flex; flex-direction: column; gap: 6px; margin: 8px 0; }
/* 🔴 30/08 (Julien) : « mets l'aperçu des produits ». La ligne de résultat
   porte la photo du catalogue Husse (ou une pastille colis), le nom, le stock. */
.ac-r { display: flex; align-items: center; gap: 11px;
        width: 100%; text-align: left; padding: 7px 11px; font: inherit; font-size: 14.5px;
        font-weight: 700; border: 1px solid var(--border); border-radius: 11px;
        background: var(--card); cursor: pointer; }
.ac-r-nom { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ac-r small { color: var(--muted); font-weight: 600; white-space: nowrap; }
.ac-r:hover { border-color: var(--red); background: var(--red-softer); }
.ac-vign { width: 40px; height: 40px; flex-shrink: 0; border-radius: 9px;
           object-fit: contain; background: var(--cream-deep); }
.ac-vign.vide { display: flex; align-items: center; justify-content: center; color: var(--soft); }
.ac-vign.vide svg, .ac-vign.vide .fi-ic { width: 20px; height: 20px; }
/* 🔴 30/08 (Julien) : « qu'on puisse scroller les produits de la base ».
   TOUTE la base est rendue et défile ici. Scroll invisible (règle des modales
   JBC), et de l'air en bas pour que la dernière ligne ne colle pas au bord. */
.ac-resultats { max-height: 52vh; overflow-y: auto; padding-bottom: 2px;
                display: flex; flex-direction: column; gap: 6px;
                overscroll-behavior: contain;
                scrollbar-width: none; -ms-overflow-style: none; }
.ac-resultats::-webkit-scrollbar { display: none; width: 0; }
/* le compteur : dit combien il y en a, et sur quel ordre on est */
.ac-compte { color: var(--muted); font-size: 12.5px; font-weight: 700;
             margin: 8px 2px 6px; }
/* le stock prend la couleur de son statut : ce qui manque saute aux yeux */
.ac-r small.st-rouge { color: var(--red); font-weight: 800; }
.ac-r small.st-orange { color: var(--orange-txt); font-weight: 800; }
.ac-nouveau { background: none; border: 0; cursor: pointer; font: inherit;
              font-size: 13.5px; font-weight: 700; color: var(--red); padding: 2px 0; }
.ac-nouveau:hover { text-decoration: underline; }
.ac-neuf { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.ac-2col { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 10px; }
.ac-l { display: flex; flex-direction: column; gap: 5px;
        font-size: 13px; font-weight: 700; color: var(--muted); margin-bottom: 10px; }
.ac-l .note-in { margin: 0; }
/* l'input fichier : natif mais habille, 16px mini (regle iOS anti-zoom) */
.ac-fic input[type="file"] { font: inherit; font-size: 16px; font-weight: 600;
  padding: 10px 12px; border: 1px dashed var(--border); border-radius: 12px;
  background: #FFFFFF; color: var(--ink); width: 100%; }
/* (l'habit des boutons de boîte de dialogue est posé une seule fois, plus haut) */
@media (max-width: 520px) { .ac-2col { grid-template-columns: 1fr; gap: 0; } }

/* le bouton et l'historique d'achats de la fiche produit */
/* 🔴 01/09 (Julien) : « mets ça au milieu ». Le bouton d'action de la fiche
   produit était collé au bord gauche, seul sur sa ligne. En flex avec des
   marges latérales automatiques, il se centre sans toucher à sa largeur. */
.pf-recharge { display: flex; align-items: center; justify-content: center; gap: 8px;
               width: fit-content; margin: 0; }
/* 07/09 (lot B) : « Recharger le stock » et « Retirer du catalogue » côte à côte */
.pf-actions { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 10px; margin-top: 16px; }
.pf-recharge img { width: 17px; height: 17px; }
.pf-achats { margin-top: 14px; }
.pf-achats h4 { font-size: 14.5px; margin-bottom: 8px; }
.pf-achat-l { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
              padding: 7px 0; border-top: 1px solid var(--border);
              font-size: 13.5px; font-weight: 600; color: var(--muted); }
.pf-achat-l b { color: var(--ink); font-variant-numeric: tabular-nums; }

/* 07/09 (RDV tel Fabien) : les factures d'achat scannées, listées dans Stock.
   Une rangée par scan : date, numéro, lignes et total HT, le PDF, puis les
   produits en dessous, en gris. Sous 520 px les colonnes s'empilent. */
.achats-intro { color: var(--muted); font-size: 14px; font-weight: 600; margin: -6px 0 12px; }
.achats-liste { margin-top: 4px; }
.achat-l { padding: 12px 0; border-top: 1px solid var(--border);
           font-size: 14px; font-weight: 700; color: var(--ink); }
/* la grille ne porte QUE la 1re ligne ; les puces des produits, longues, sont
   dessous, en flux libre, et ne peuvent plus élargir les colonnes */
/* 07/09 : en FLEX, pas en grille : les quatre morceaux se suivent, le lien PDF
   se cale à droite par sa marge, et sur téléphone tout se replie sans règle
   à part (la grille à colonnes « auto » avait déjà trahi deux fois). */
.achat-l .achat-top { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 14px; }
.achat-l .achat-date { min-width: 104px; }
/* 07/09 (Julien) : « dates en bleu » : le bleu ardoise des horaires (.hh) */
.achat-l .achat-date { color: #2F6F8F; font-weight: 700; }
/* 07/09 (Julien : « il manque des couleurs ») : le numéro de facture en pastille
   rouge douce (l'habit d'un TTC), un avoir fournisseur en vert doux */
.achat-l .achat-fact { justify-self: start; display: inline-block; padding: 3px 10px; border-radius: 8px;
                       background: var(--red-soft); color: var(--red-dark); font-weight: 800; }
.achat-l .achat-fact.avoir { background: var(--green-soft); color: #146B3C; }
.achat-l .achat-det { display: inline-flex; align-items: center; gap: 8px; white-space: nowrap; }
.achat-l .achat-det .mnt { font-weight: 800; font-variant-numeric: tabular-nums; }
.achat-l .achat-det .mnt-gris { background: var(--cream-deep); color: var(--muted); }
.achat-l .achat-sans { color: var(--muted); font-size: 13px; font-weight: 600; }
.achat-l .achat-prods { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; min-width: 0; }
/* 07/09 (Julien) : « fond produit : jaune clair » */
.achat-l .achat-prods i { font-style: normal; font-size: 12.5px; font-weight: 600; color: var(--ink);
                          background: #FFF4CC; border-radius: 7px; padding: 3px 8px; max-width: 100%;
                          overflow-wrap: anywhere; }
.achat-l .achat-prods i.plus { background: none; color: var(--muted); padding-left: 0; }
/* 07/09 (Julien) : « border rouge pour les boutons » : le lien PDF prend le cadre rouge */
.achat-l .achat-pdf { display: inline-flex; align-items: center; gap: 8px; padding: 4px 12px 4px 4px; margin-left: auto;
                      border-color: var(--red); }
.achat-l .achat-pdf .pdf-mini { width: 30px; height: 30px; border-radius: 7px; font-size: 10px; }

/* 🤖 30/08 : le scan de facture fournisseur. Un grand bouton en tête de la
   modale d'achat, puis l'écran de validation ligne par ligne. */
.ac-scan { display: flex; align-items: center; gap: 12px; width: 100%;
           text-align: left; padding: 13px 15px; margin-bottom: 14px;
           border: 2px dashed var(--red); border-radius: 13px;
           background: var(--red-softer); font: inherit; cursor: pointer; }
.ac-scan:hover { background: var(--red-soft); }
.ac-scan img { width: 26px; height: 26px; flex-shrink: 0; }
.ac-scan b { display: block; font-size: 15px; font-weight: 800; color: var(--red-dark); }
.ac-scan small { display: block; color: var(--muted); font-weight: 600;
                 font-size: 12.5px; line-height: 1.4; }
.ac-scan.scan-encours { pointer-events: none; opacity: .7; }
.sc-lignes { display: flex; flex-direction: column; gap: 8px;
             max-height: 44vh; overflow-y: auto; margin-bottom: 10px;
             scrollbar-width: none; -ms-overflow-style: none; }
.sc-lignes::-webkit-scrollbar { display: none; width: 0; }
.sc-l { display: flex; gap: 10px; align-items: flex-start;
        border: 1px solid var(--border); border-radius: 12px; padding: 10px 12px; }
.sc-l.sc-inconnu { border-color: #F0C36D; background: #FDF7EA; }
.sc-coche { padding-top: 4px; }
.sc-coche input { width: 18px; height: 18px; accent-color: var(--red); }
.sc-corps { flex: 1; display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.sc-lu { color: var(--muted); font-weight: 600; font-size: 12.5px;
         overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sc-nb { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }

/* 🔴 30/08 (Julien) : la modale d'achat en DEUX ÉTAPES, elle était trop
   grande d'un seul bloc. Étape 1 : le produit (scan / recherche / nouveau).
   Étape 2 : l'achat. Le produit choisi se rappelle en bandeau, avec
   « changer » pour revenir. */
.ac-etape[hidden] { display: none; }
.ac-choisi { display: flex; align-items: center; justify-content: space-between;
             gap: 10px; padding: 10px 14px; margin-bottom: 14px;
             border: 1px solid var(--border); border-radius: 12px;
             background: var(--cream-deep); }
.ac-choisi[hidden] { display: none; }
.ac-choisi-nom { font-weight: 800; font-size: 14.5px; min-width: 0;
                 overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ac-choisi .lnk { background: none; border: 0; cursor: pointer; font: inherit;
                  font-size: 13px; font-weight: 700; color: var(--red);
                  white-space: nowrap; }
.ac-choisi .lnk:hover { text-decoration: underline; }


/* 🔴 30/08 (Julien, 3e passe) : le panneau d'achat en écrans séparés.
   Un bouton « Retour » discret à gauche, et l'attente pendant la lecture
   des factures (une ou plusieurs). */
.btn-fant { background: none; border: 1px solid var(--border); border-radius: 12px;
            padding: 11px 16px; font: inherit; font-size: 14.5px; font-weight: 700;
            color: var(--muted); cursor: pointer; margin-right: auto; }
.btn-fant:hover { border-color: var(--ink); color: var(--ink); }
.ac-lecture { display: flex; align-items: center; justify-content: center; gap: 11px;
              padding: 30px 10px; font-size: 15px; font-weight: 700; color: var(--ink); }
.ac-spin { width: 20px; height: 20px; border-radius: 50%; flex-shrink: 0;
           border: 2.5px solid var(--red-soft); border-top-color: var(--red);
           animation: ac-tourne .8s linear infinite; }
@keyframes ac-tourne { to { transform: rotate(360deg); } }
/* un groupe par facture dans l'écran de validation */
.sc-groupe { border: 1px solid var(--border); border-radius: 13px; padding: 11px;
             display: flex; flex-direction: column; gap: 8px; background: var(--cream); }
.sc-tete b { font-size: 14.5px; font-weight: 800; display: block; margin-bottom: 6px; }
.sc-tete .ac-2col { margin-bottom: 0; }
.sc-tete .ac-l { margin-bottom: 0; }
.sc-groupe .sc-l { background: #FFFFFF; }

/* 🔴 02/09 (Julien, facture de Fabien) : « certains n'avaient pas tout à fait
   le même nom », « être sûr qu'on a le bon truc », « pouvoir en rajouter ».
   Chaque ligne porte son état : Reconnu (vert), À vérifier (orange : un
   produit renommé ou deux voisins de gamme, avec les candidats en boutons),
   Pas trouvé (ambre, décochée). Et un « + Ajouter une ligne » par facture. */
.sc-haut { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-width: 0; }
.sc-haut .sc-lu { flex: 1; min-width: 0; }
.sc-lu em { font-style: normal; color: var(--orange-txt); }
.sc-etat { flex-shrink: 0; font-size: 11px; font-weight: 800; letter-spacing: .02em;
           text-transform: uppercase; padding: 3px 8px; border-radius: 999px; white-space: nowrap; }
.sc-etat.ok { background: var(--green-soft); color: #187A42; }
.sc-etat.doute { background: var(--orange-soft); color: var(--orange-txt); }
.sc-etat.inconnu { background: #F1ECE4; color: var(--muted); }
/* 07/09 (lot B) : une ligne qui CRÉE un produit depuis le scan */
.sc-etat.neuf { background: #E8F1FB; color: #1B4E85; }
.sc-l.sc-neuf-on { border-color: #9DBCE0; background: #F3F8FD; }
.sc-neuf { margin: 4px 0 8px; padding: 10px 12px; border: 1px dashed #9DBCE0; border-radius: 12px; background: #FFFFFF; }
.sc-neuf .ac-2col { margin-bottom: 0; }
.sc-creer { border-style: dashed; }
.sc-l.sc-doute { border-color: #F5C48A; background: #FFF8EE; }
.sc-groupe .sc-l.sc-doute { background: #FFF8EE; }
.sc-groupe .sc-l.sc-inconnu { background: #FDF7EA; }
.sc-cands { display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
            font-size: 12px; font-weight: 700; color: var(--muted); }
.sc-cand { background: #FFFFFF; border: 1px solid #F0C36D; color: var(--ink); border-radius: 999px;
           padding: 4px 10px; font: inherit; font-size: 12px; font-weight: 700; cursor: pointer;
           max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sc-cand:hover { border-color: var(--red); color: var(--red); }
.sc-lu-in { flex: 1; min-width: 0; margin: 0; }
.sc-plus { align-self: flex-start; background: none; border: 1px dashed var(--soft); border-radius: 12px;
           padding: 9px 14px; font: inherit; font-size: 13.5px; font-weight: 700; color: var(--red);
           cursor: pointer; }
.sc-plus:hover { border-color: var(--red); background: var(--red-softer); }
.dlg-sous .c-doute { color: var(--orange-txt); }
.sc-avert { margin-top: 8px; padding: 9px 12px; border-radius: 10px; background: var(--orange-soft);
            color: var(--orange-txt); font-size: 12.5px; font-weight: 700; line-height: 1.45; }
.dlg-sous .c-inconnu { color: var(--muted); }

/* 🔴 30/08 (Julien) : le graphe de marge. « Des couleurs différentes pour
   vendu, marchandise, marge, avec un fond de couleur », « l'infobulle vraiment
   au-dessus de la barre, connectée », « la barre survolée d'une autre couleur
   ou un contour ». Trois lignes lisibles séparément, la bulle collée au
   sommet de la barre, et la barre du mois lu qui s'allume. */
/* 🔴 30/08 (Julien, 2e passe) : « fond blanc plutôt, et pas de border noir ».
   La bulle passe en carte claire, comme le reste du cockpit ; les trois
   chiffres gardent chacun son fond de couleur, en teintes douces cette fois. */
/* 🔴 30/08 : la bulle était en « display: flex; flex-direction: column »
   (hérité de .chart-tip). Ses lignes, elles-mêmes en flex, ne remontaient pas
   leur largeur au parent : boîte de 175 px pour 281 px de texte, et le
   contenu sortait du fond (mesuré au banc, puis reproduit au harnais).
   En display: block, l'absolu se dimensionne enfin sur son contenu. */
.mg-tip { display: block; padding: 10px 11px;
          background: #FFFFFF; color: var(--ink);
          border: 1px solid var(--border);
          box-shadow: 0 10px 26px rgba(23, 21, 15, .16); }
/* 🔴 et on redit le hidden : le display ci-dessus le battrait (piège déjà
   rencontré le 30/08 sur les étiquettes de filtre). */
.mg-tip[hidden] { display: none; }
/* 🔴 14/09 SOIR (Julien, capture de l'infobulle) : « grossis un tout petit peu
   l'ensemble, que 130 fact. · panier 93 € soit plus gros, c'est petit même sur
   écran ». Toute la bulle monte d'un cran, et le détail de deux : il était à
   11 px contre 14,5 px pour le montant, l'écart était trop grand pour qu'on le
   lise sans s'approcher. */
.mg-tip b { display: block; font-size: 13px; color: var(--muted); opacity: 1; margin-bottom: 7px; }
.mg-tip .mg-l + .mg-l { margin-top: 5px; }
/* 🔴 14/09 SOIR, PIÈGE DE SPÉCIFICITÉ MESURÉ : « .chart-tip span » (plus haut
   dans cette feuille, 2 niveaux) BAT « .mg-l » (1 niveau). La taille et la
   graisse écrites ici n'ont JAMAIS rien peint depuis le 30/08 : le nom du poste
   s'affiche en 16px/800, pas en 12px/700 comme cette règle le croyait. On ne
   redéclare donc pas ici une taille qu'elle ne peut pas tenir : ce qui doit
   gagner est écrit plus bas, en « .mg-tip … », et ce qui est écrit ici est ce
   qu'elle peut réellement porter. */
.mg-l { display: flex; align-items: baseline; justify-content: space-between; gap: 16px;
        padding: 5px 10px; border-radius: 8px; }
/* 🔴 30/08, LA cause du débordement : « .chart-tip i » (la pastille de couleur
   des légendes de graphe) impose width/height 9px à TOUT <i> d'une infobulle.
   Mes montants sont dans des <i> : ils étaient donc enfermés dans une boîte de
   9 px et débordaient par-dessus le fond. On rend leur largeur au texte. */
.mg-l i { font-style: normal; font-size: 16px; font-weight: 800;
          font-variant-numeric: tabular-nums;
          display: inline; width: auto; height: auto; border-radius: 0;
          background: none; }
/* 🔴 14/09 (Fabien) : l'infobulle du CA par poste porte aussi le nombre de
   factures et le panier moyen du mois. Le nom du poste et ce détail voyagent
   ensemble à gauche, le montant reste calé à droite. Le détail est en encre
   plus légère : le montant garde la vedette. */
.mg-g { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.mg-l em { font-style: normal; font-size: 14.5px; font-weight: 700; opacity: .92; }
/* 🔴 sur un téléphone, la bulle ne doit pas dépasser le graphe : elle est en
   `white-space: nowrap` et déborderait la carte, donc la page. Le détail
   s'efface, le montant reste. Les quatorze mois se lisent sur un ordinateur ;
   sur le téléphone, Fabien a la bande juste au-dessus. */
@media (max-width: 640px) { .mg-l em { display: none; } }

/* un fond par nature de chiffre : ce qui est entré, ce qui est sorti, ce qui reste */
.mg-vendu { background: var(--red-soft); color: var(--red-dark); }
.mg-vendu i { color: var(--red-dark); }
.mg-cout  { background: var(--cream-deep); color: #6E675C; }
.mg-cout i { color: #4A443B; }
.mg-marge { background: var(--green-soft); color: #187A42; }
.mg-marge i { color: #146838; }
/* La pointe qui relie la bulle à la barre. Elle suit le CENTRE DE LA BARRE
   (variable --fl posée par le JS) : quand la bulle est recalée près d'un bord,
   son propre centre n'est plus en face de la barre. Deux triangles superposés
   pour dessiner le liseré clair de la carte. */
.mg-tip::after, .mg-tip::before {
  content: ''; position: absolute; left: var(--fl, 50%); width: 0; height: 0;
  border-left: 7px solid transparent; border-right: 7px solid transparent;
  margin-left: -7px;
}
.mg-tip::before { bottom: -8px; border-top: 8px solid var(--border); }
.mg-tip::after  { bottom: -6px; border-top: 7px solid #FFFFFF; }
.mg-tip.mg-sous::before { bottom: auto; top: -8px; border-top: 0; border-bottom: 8px solid var(--border); }
.mg-tip.mg-sous::after  { bottom: auto; top: -6px; border-top: 0; border-bottom: 7px solid #FFFFFF; }


/* 🔴 30/08 : le bouton d'achat monté dans le page-head est LONG. À 320 px il
   poussait la page en scroll horizontal (attrapé par le harnais : body 344
   pour 305 de large). Libellé court sur petit écran, et head-tools qui accepte
   de rétrécir et de passer à la ligne au lieu de déborder. */
.lbl-court { display: none; }
@media (max-width: 620px) {
  .head-tools { flex-shrink: 1; flex-wrap: wrap; min-width: 0; }
  #btnAchat .lbl-long { display: none; }
  #btnAchat .lbl-court { display: inline; }
  /* 02/09 : même repli pour « Exporter pour mon comptable » (page Factures) */
  #btnExport .lbl-long { display: none; }
  #btnExport .lbl-court { display: inline; }
}


/* 🔴 30/08 (Julien) : « même style d'infobulle ici que celle du graphe de
   marge brute ». Le graphe CA par poste réutilise .mg-tip / .mg-l ; il lui
   faut juste la ligne de total, et la neutralisation des vieux styles du
   .chart-tip sombre (pastille <i>, séparateur clair sur fond noir). */
.mg-tip .mg-total { background: var(--cream-deep); color: var(--ink);
                    font-weight: 800; margin-top: 6px; }
.mg-tip .mg-total i { color: var(--ink); }
.mg-tip .mg-l i { display: inline; width: auto; height: auto;
                  border-radius: 0; background: none; margin-right: 0; }
/* 🔴 14/09 soir (Julien) : « grossis un tout petit peu l'ensemble, que
   130 fact. · panier 93 € soit plus gros, c'est petit même sur écran ».
   Ces deux règles-là (2 niveaux) battent « .chart-tip span » : elles écrivent
   ce que la bulle doit VRAIMENT peindre, au lieu de le subir. Le nom du poste
   garde la taille qu'il a toujours eue à l'écran (16px/800) ; c'est le détail
   qui monte, de 11 à 14,5 px, et le montant qui s'aligne sur le nom. */
.mg-tip .mg-g { font-size: 16px; font-weight: 800; }
.mg-tip .mg-l em { font-size: 14.5px; font-weight: 700; }
.mg-tip .pl-tot { border-top: 0; margin-top: 0; padding-top: 0; }
/* Les barres empilées du CA par poste s'estompent comme celles de la marge.
   🔴 PAS de transition : l'estompage doit être instantané quand on balaie les
   barres, et surtout une transition rend toute lecture de computed style
   FAUSSE au harnais (l'inline disait 0.55, le computed répondait encore 1).
   Même piège que la transition sur le fill des barres de marge, ce matin. */


/* 🔴 30/08 (Julien) : l'icône du titre « Tir de demain » dans sa modale.
   Titre centré comme les autres modales, icône alignée sur la hauteur de
   capitale du texte (règle d'alignement icône/texte du 28/08). */
.h3-ic { display: flex; align-items: center; justify-content: center; gap: 10px; }
.h3-ic img { width: 22px; height: 22px; object-fit: contain; flex-shrink: 0; }


/* ============================================================
   Menus déroulants maison (Julien, 01/09/2026)
   ============================================================
   « J'aimerais que les menus soient dans le même style que le site, pas de
   Chrome ou Mac, comme des modales normales. » Le <select> natif reste dans la
   page (il porte la valeur et émet « change »), mais il devient invisible :
   c'est .sel-jbc qu'on voit, et .menu-jbc qui s'ouvre. */
.sel-jbc-vrai {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  opacity: 0; pointer-events: none;
}
.sel-jbc {
  display: inline-flex; align-items: center; justify-content: space-between; gap: 10px;
  width: 100%; min-width: 0;
  font-family: inherit; font-size: 15px; font-weight: 700; color: var(--ink);
  text-align: left; cursor: pointer;
  background: #FFFFFF;                       /* blanc pur : le champ se distingue du crème */
  border: 1.5px solid var(--border); border-radius: 11px;
  padding: 9px 13px;
  transition: border-color .15s, box-shadow .15s;
}
.sel-jbc:hover { border-color: var(--red); }
.sel-jbc:focus-visible, .sel-jbc.ouvert {
  outline: none; border-color: var(--red); box-shadow: 0 0 0 3px var(--red-soft);
}
.sel-jbc.vide .sel-jbc-t { color: var(--soft-txt); font-weight: 600; }
.sel-jbc-t { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* le chevron est dessiné en CSS : pas d'image à charger, il suit la couleur */
.sel-jbc-fl {
  width: 9px; height: 9px; flex: 0 0 auto; margin-right: 1px;
  border-right: 2px solid var(--muted); border-bottom: 2px solid var(--muted);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform .15s, border-color .15s;
}
.sel-jbc:hover .sel-jbc-fl, .sel-jbc.ouvert .sel-jbc-fl { border-color: var(--red); }
.sel-jbc.ouvert .sel-jbc-fl { transform: rotate(-135deg) translate(-3px, -3px); }

/* Le panneau : une petite modale JBC, posée en repère écran pour n'être
   rognée par aucun « overflow:hidden » de cellule ou de carte. */
.menu-jbc {
  /* 🔴 01/09 (Julien) : « attention le menu passe derrière ». Le panneau était
     au plan 200, mais il s'ouvre AUSSI depuis une boîte de dialogue (le
     règlement d'une facture), qui est au plan 1000, et depuis les modales, au
     plan 100/120. Il doit donc passer au-dessus de TOUT ce qui peut le
     contenir. Plans de la feuille, du fond vers la surface : barre latérale 40,
     barre d'onglets 50, modales 100 et 120, voile de dialogue 999/1000. Le
     menu se pose à 1200 : au-dessus du plus haut, avec de la marge. */
  position: fixed; z-index: 1200;
  background: var(--card);
  border: 1px solid var(--border); border-radius: 14px;
  box-shadow: 0 18px 44px rgba(24, 20, 14, .18), 0 2px 6px rgba(24, 20, 14, .06);
  padding: 6px;
  /* 🔴 01/09 (Julien) : « je peux pas scroller dans le menu ». Deux causes : un
     écouteur de défilement en capture qui refermait le menu (corrigé côté JS),
     et un plafond de 320 px qui coupait la dernière option des listes de 9
     entrées. Le plafond suit maintenant la hauteur de l'écran : sur un
     téléphone la liste tient d'un coup, et quand elle ne tient pas, elle
     défile pour de vrai. */
  max-height: min(420px, 68vh); overflow-y: auto;
  width: max-content; max-width: calc(100vw - 16px);
  /* pas de barre de défilement visible : règle des modales JBC.
     overscroll-behavior: contain empêche le défilement de « traverser » vers la
     page quand on arrive au bout de la liste. */
  scrollbar-width: none; -ms-overflow-style: none;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
.menu-jbc::-webkit-scrollbar { display: none; width: 0; }
.menu-jbc-o {
  display: flex; align-items: center; gap: 9px; width: 100%;
  border: 0; background: none; font-family: inherit; cursor: pointer;
  font-size: 15px; font-weight: 700; color: var(--ink); text-align: left;
  padding: 9px 11px; border-radius: 10px;
}
.menu-jbc-o:hover, .menu-jbc-o:focus-visible { background: var(--red-soft); color: var(--red-dark); outline: none; }
.menu-jbc-o.on { color: var(--red); }
/* « Non renseigné » : une vraie ligne, mais qui se lit comme un retrait */
.menu-jbc-o.rien .menu-jbc-t { color: var(--soft-txt); font-weight: 600; font-style: italic; }
.menu-jbc-o.rien.on .menu-jbc-t { color: var(--red); font-style: normal; }
/* la coche : un gabarit fixe pour que TOUS les libellés s'alignent, coché ou non */
.menu-jbc-c { width: 14px; height: 14px; flex: 0 0 auto; position: relative; }
.menu-jbc-o.on .menu-jbc-c::after {
  content: ''; position: absolute; left: 2px; top: 0;
  width: 6px; height: 11px; border-right: 2.5px solid currentColor;
  border-bottom: 2.5px solid currentColor; transform: rotate(45deg);
}
.menu-jbc-t { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 🔴 01/09 (Julien) : l'espèce d'un client passe du trio de pastilles au MÊME
   menu que les statuts de facture. Comme un client peut avoir un chien ET un
   chat, ses lignes portent une vraie case à cocher (le simple ✓ des menus à
   choix unique ne dit pas qu'on peut en prendre plusieurs), et le panneau reste
   ouvert le temps de cocher. */
.menu-jbc.multi .menu-jbc-c {
  width: 19px; height: 19px; border-radius: 6px;
  border: 1.5px solid var(--border); background: #FFFFFF;
}
.menu-jbc.multi .menu-jbc-o.on { background: var(--red-soft); }
.menu-jbc.multi .menu-jbc-o.on .menu-jbc-c { border-color: var(--red); background: var(--red); }
.menu-jbc.multi .menu-jbc-o.on .menu-jbc-c::after {
  left: 6px; top: 2px; width: 5px; height: 10px;
  border-right-color: #FFFFFF; border-bottom-color: #FFFFFF;
}


/* ============================================================
   Pagination du tableau des factures (Julien, 01/09/2026)
   ============================================================
   « Il faut pas toutes les afficher, 10 par 10 sinon trop lourd. » */
.pager {
  display: flex; align-items: center; justify-content: center; gap: 14px;
  flex-wrap: wrap;
  padding: 16px 0 4px; margin-top: 6px;
  border-top: 1px solid var(--border);
}
/* 🔴 01/09 (Julien) : « je veux ça au milieu, avec du rouge en border du bouton
   et rouge pour le texte précédentes, page 1/50, pareil sur la liste des
   clients ». La barre est centrée (justify-content: center, déjà posé), les
   deux boutons portent le cadre rouge de la marque, et leur libellé comme le
   numéro de page sont en rouge : ce sont les seules commandes de la barre. */
.pager-b {
  font-family: inherit; font-size: 14.5px; font-weight: 700; color: var(--red);
  background: var(--card); border: 1.5px solid var(--red); border-radius: 11px;
  padding: 9px 15px; cursor: pointer;
  transition: background .15s, color .15s;
}
.pager-b:hover:not(:disabled) { background: var(--red); color: #FFFFFF; }
/* un bouton hors service garde son cadre, mais s'efface : il ne se lit plus
   comme une action possible */
.pager-b:disabled { opacity: .32; cursor: default; }
.pager-e { color: var(--muted); font-weight: 600; font-size: 14.5px; white-space: nowrap; }
.pager-e b { color: var(--ink); font-weight: 800; font-variant-numeric: tabular-nums; }
.pager-p { color: var(--red); font-weight: 700; }
/* 02/09 soir : la ligne sous le tableau des factures qui dit ce que la page
   ne montre pas (fenêtre de 500 sur toute la base, période en cours de
   chargement, période trop large) */
.liste-note {
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  margin: 12px 0 0; text-align: center;
  font-size: 14px; line-height: 1.5; color: var(--muted);
}
/* 02/09 soir : le bouton « Voir les N autres » sous la ligne, au repos */
.liste-note .btn.ghost { font-size: 14px; padding: 8px 16px; min-height: 36px; }
@media (max-width: 480px) {
  /* sur un téléphone, l'état passe sous les deux boutons plutôt que de les
     serrer à trois par ligne */
  .pager { gap: 10px; }
  .pager-e { flex: 0 0 100%; text-align: center; order: 3; }
}


/* ============================================================
   Période libre sur les factures (Julien, 01/09/2026)
   ============================================================
   « Ajoute un tag ici pour qu'on puisse saisir une période donnée. » */
.perso-dates {
  display: inline-flex; align-items: center; flex-wrap: wrap; gap: 8px 12px;
}
/* 🔴 01/09 (3e retour Julien) : « Période choisie reste à sa place, et les
   périodes de dates s'affichent après, à droite, quand on sélectionne ce tag ».
   Les quatre raccourcis restent donc visibles : les deux champs de date
   s'ajoutent simplement à la suite de l'étiquette, dans la même rangée. Faire
   disparaître les autres étiquettes coupait le repère (« sur quoi je filtrais
   déjà ? ») pour un gain de place dont on n'a pas besoin. */
.perso-raz {
  font-family: inherit; font-size: 14px; font-weight: 700; color: var(--muted);
  background: none; border: 0; padding: 6px 4px; cursor: pointer;
  text-decoration: underline; text-underline-offset: 3px;
}
.perso-raz:hover { color: var(--red); }
.perso-l {
  display: inline-flex; align-items: center; gap: 8px;
  color: var(--muted); font-weight: 700; font-size: 14.5px; white-space: nowrap;
}
/* 16px imposé : sous 16px, Safari iOS zoome au tap et la page zoomée devient
   défilable en largeur (règle JBC du scroll horizontal). */
/* 🔴 01/09 (Julien) : « quand je clique sur Période choisie les lignes
   s'agrandissent aussi, corrige stp ». Les champs de date étaient plus HAUTS
   que les étiquettes de la rangée (16 px de corps et 7 px de padding contre
   14 px et 7 px), donc la rangée grandissait et tout le tableau descendait à
   l'ouverture. Ils prennent la hauteur EXACTE d'une étiquette : même corps,
   même padding, même bordure. Le 16 px reste imposé sur le CHAMP lui-même,
   sinon Safari iOS zoome au tap. */
.perso-dates .fi-in {
  width: auto; min-width: 148px; font-size: 16px; text-align: left;
  border: 1px solid var(--border); background: #FFFFFF;
  padding: 6px 12px; border-radius: 11px; line-height: 1.2;
}
.perso-dates .fi-in:focus { box-shadow: 0 0 0 3px var(--red-soft); }
/* la rangée réserve la hauteur du plus grand de ses deux états : ouvrir la
   période libre ne déplace alors plus rien en dessous */
.chips-periode { min-height: 36px; align-items: center; }
.perso-dates .fi-in:focus { border-color: var(--red); }
.chip-perso { border-style: dashed; }
.chip-perso.on { border-style: solid; }
@media (max-width: 480px) {
  .perso-l { flex: 1 1 100%; }
  .perso-dates .fi-in { flex: 1 1 auto; min-width: 0; }
}


/* Les boutons d'en-tête d'un bloc (Panier du jour : « Remettre à zéro » et
   « Ajouter un produit »), Julien 01/09/2026. Ils restent groupés à droite et
   passent à la ligne ensemble sur téléphone plutôt qu'un par ligne. */
.head-actions { display: inline-flex; align-items: center; gap: 10px; flex-wrap: wrap; justify-content: flex-end; }
@media (max-width: 480px) {
  .head-actions { width: 100%; justify-content: stretch; }
  .head-actions .btn { flex: 1 1 auto; justify-content: center; }
}


/* Page Tirs, état « aucun tir en préparation » (Julien, 01/09/2026) :
   l'horloge du Préparateur, au même endroit que la liste qu'elle annonce.
   🔴 « fond rouge sous le compteur » : elle reprend la pastille rouge doux de
   l'accueil, pour que le même objet se reconnaisse d'une page à l'autre. */
.tir-attente {
  display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
  padding: 6px 0 2px;
}
.tir-attente .hz-pill { margin-top: 0; padding: 9px 14px 9px 11px; border-radius: 13px; }
.tir-attente .hz-pill .hz-dial { width: 26px; height: 26px; }
.tir-attente .hz-pill .hz-txt b { font-size: 15px; }
.tir-attente .hz-pill .hz-txt small { font-size: 12.5px; }
.tir-attente p {
  margin: 0; color: var(--muted); font-weight: 600; font-size: 15px; line-height: 1.5;
}
@media (max-width: 560px) {
  .tir-attente { gap: 14px; }
  .tir-attente p { flex: 1 1 100%; }
}


/* SMS dont l'opérateur n'a pas confirmé la remise (Julien, 01/09/2026) :
   « quand le SMS est non remis tu peux l'indiquer dans le fil de message ».
   La bulle garde sa place et son texte : seule sa mention change, en ambre.
   ⚠️ Ambre et pas rouge : un accusé en échec n'est pas une preuve de
   non-remise (mesuré le 08/07/2026, un SMS marqué en échec avait bien été
   reçu). C'est un doute à lever, pas une erreur constatée. */
.sms-b.sms-rate { border-left: 3px solid var(--orange); padding-left: 11px; }
.sms-meta.rate { color: var(--orange); font-weight: 700; cursor: help; }


/* Plusieurs animaux sur une fiche (Julien, 01/09/2026) : le repli « +1 » a été
   ABANDONNÉ le jour même (« oublie le +1, mets des virgules c'est mieux ») ;
   la normalisation en virgules se fait côté JS, sans style dédié.
   ⚠️ La leçon de l'essai raté est conservée ici : la vue repliée ET la liste
   complète s'affichaient ENSEMBLE, parce que « .multi-tout { display: flex } »
   l'emporte sur l'attribut « hidden » (qui ne vaut qu'un display:none de la
   feuille du navigateur). Masquer par attribut demande toujours une règle
   « [hidden] { display: none !important } » quand une classe pose un display. */

/* 🔴 02/09 (Julien) : « on teste la version cartes, sur mobile seulement ».
   Sous 520 px, les tableaux Clients et Factures glissaient dans leur carte
   sans aucun indice (première colonne visible, la deuxième coupée en plein
   mot). Ici le HTML ne change PAS : chaque <tr> devient une carte en flex,
   les <td> se replacent par ordre. Clics, filtres, tri par défaut et
   pagination restent ceux du tableau. L'en-tête (tri au clic, « tout
   cocher ») s'efface : sur téléphone on lit, on ne trie pas. */
@media (max-width: 520px) {
  /* 11/09 (B5) : les <th> sont maintenant dans un vrai <thead> (build.py),
     cette règle le trouve enfin ; et les règles de carte visent les lignes
     de DONNÉES (#facturesTable tr), plus « toutes les tr du tableau ». */
  table:has(#clientsTable) thead, table:has(#facturesTable) thead, table:has(#stockTable) thead { display: none; }
  table:has(#clientsTable), table:has(#facturesTable), table:has(#stockTable),
  #clientsTable, #facturesTable, #stockTable { display: block; width: 100%; }
  .tri-mobile { display: flex; }
  #clientsTable tr, #facturesTable tr, #stockTable tr {
    display: flex; flex-wrap: wrap; align-items: center;
    padding: 13px 2px; height: auto; border-bottom: 1px solid var(--border);
  }
  #clientsTable tr:last-child, #facturesTable tr:last-child, #stockTable tr:last-child { border-bottom: 0; }
  #clientsTable td, #facturesTable td, #stockTable td {
    display: block; padding: 0; border: 0; min-width: 0;
    font-size: 14px; white-space: normal; overflow: visible; text-align: left;
  }
  tr.rowlink:hover td { background: transparent; }

  /* Clients : nom + statut / ville / espèce · fréquence · relance · panier */
  #clientsTable td:nth-child(1) { order: 1; flex: 1 1 0; font-size: 15px; }
  #clientsTable td:nth-child(1) > span { gap: 10px; }
  #clientsTable td:nth-child(7) { order: 2; margin-left: 10px; flex: 0 0 auto; }
  #clientsTable td:nth-child(2) {
    order: 3; flex: 1 1 100%; padding-left: 50px; margin-top: 3px;
    font-size: 13px; color: var(--muted);
  }
  /* un trait, puis la rangée espèce, puis fréquence · relance · panier.
     Le trait est un pseudo-élément du <tr> (un flex accepte ses ::before
     comme items) : il tient même quand la rangée espèce est vide. */
  #clientsTable tr.rowlink::before {
    content: ''; order: 4; flex: 1 1 100%; height: 0; margin-top: 10px;
    border-top: 1px solid var(--border);
  }
  #clientsTable td:nth-child(3) {
    order: 5; flex: 1 1 100%; margin-top: 8px;
    font-size: 12.5px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  #clientsTable td:nth-child(3) .esp { font-size: 11px; padding: 1px 6px; }
  #clientsTable td:nth-child(4), #clientsTable td:nth-child(5), #clientsTable td:nth-child(6) {
    order: 6; margin-top: 7px; font-size: 12.5px; color: var(--muted); white-space: nowrap; flex: 0 0 auto;
  }
  #clientsTable td:nth-child(4) .fq-l { display: none; }
  #clientsTable td:nth-child(4):has(.fq-l)::before { content: '🔁 '; }
  #clientsTable td:nth-child(5) { padding-left: 14px; }
  #clientsTable td:nth-child(5):has(.rl-d)::before { content: '📅 '; }
  #clientsTable td:nth-child(6) { margin-left: auto; padding-left: 14px; color: var(--ink); font-size: 14px; }
  /* une valeur absente (« · ») ne prend pas de place sur la carte */
  #clientsTable td:has(> .vide:only-child) { display: none; }
  /* les lignes d'attente (« Aucune fiche trouvée. ») n'ont qu'une cellule */
  #clientsTable tr > td:only-child, #facturesTable tr > td:only-child, #stockTable tr > td:only-child { flex: 1 1 100%; }

  /* 11/09 (B8) : Stock en cartes : produit + statut sur la première ligne,
     stock réel et théorique nommés en dessous. Avant, le statut commençait
     à x = 483 px sur un écran de 390 : il fallait glisser pour savoir
     qu'un produit était épuisé. */
  #stockTable td.s-prod { order: 1; flex: 1 1 0; font-size: 15px; }
  #stockTable td.s-stat { order: 2; flex: 0 0 auto; margin-left: 10px; }
  #stockTable td.s-reel, #stockTable td.s-theo {
    order: 3; flex: 0 0 auto; margin-top: 8px; padding-left: 54px; font-size: 14px; white-space: nowrap;
  }
  #stockTable td.s-theo { padding-left: 16px; color: var(--muted); font-size: 12.5px; }
  #stockTable td.s-reel::before { content: 'Réel '; color: var(--muted); font-size: 12.5px; }
  #stockTable td.s-theo::before { content: 'Théorique '; }
  .block-head .search-stock { flex: 1 1 100%; max-width: none; }

  /* 🔴 11/09 (brief Astra B9) : le CLIENT et le MONTANT d'abord (le numéro
     était en 15 px gras et le client en petit gris tronqué) ; numéro et date
     en second ; vendeur et statut ; puis le règlement sur sa rangée (07/09 :
     posé à la suite du vendeur, le plus long libellé repliait la rangée et
     les cartes changeaient de hauteur). Les cellules se visent par CLASSE. */
  #facturesTable td.selcell { order: 1; width: 44px; flex: 0 0 44px; min-height: 44px; margin-right: 0;
    display: flex; align-items: center; justify-content: center; }
  #facturesTable td.f-cli { order: 2; flex: 1 1 0; font-size: 15px; font-weight: 700; color: var(--ink);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  #facturesTable td.f-cli .lien-fiche { color: inherit; }
  #facturesTable td.f-ttc { order: 3; margin-left: auto; padding-left: 8px; flex: 0 0 auto; font-size: 15px; font-weight: 800; }
  #facturesTable td.facture-num { order: 4; flex: 0 0 auto; padding-left: 44px; margin-top: 4px;
    font-size: 12.5px; font-weight: 700; color: var(--muted); white-space: nowrap; }
  #facturesTable td.f-date { order: 5; flex: 0 0 auto; margin-left: 8px; margin-top: 4px;
    font-size: 12.5px; color: var(--muted); white-space: nowrap; }
  #facturesTable td.col-vend { order: 6; display: block; flex: 0 0 auto; margin-top: 9px; padding-left: 44px; }
  #facturesTable td.col-stat { order: 7; margin-left: auto; margin-top: 9px; flex: 0 0 auto; }
  #facturesTable td.col-regl { order: 8; display: block; flex: 1 1 100%; margin-top: 8px; padding-left: 44px; min-width: 0; }
  #facturesTable td.col-regl .regl { font-size: 12.5px; height: 26px; padding: 0 8px; }
  #facturesTable td.col-ht, #facturesTable td.col-tva { display: none; }
  #facturesTable td.col-vend:has(> .vide:only-child) { display: none; }
  #facturesTable td.col-regl:has(> .vide:only-child) { display: none; }
  #facturesTable td.fcell select.fi-in { min-width: 120px; }

  /* 11/09 (B12) : le panier et l'historique de la fiche parlent le même
     vocabulaire de cartes que les autres listes ; un panier vide se lit en
     entier, sans en-tête ni glissement */
  table:has(#panierTable) thead, table:has(#histoFactures) thead { display: none; }
  table:has(#panierTable), table:has(#histoFactures), #panierTable, #histoFactures { display: block; width: 100%; }
  #panierTable tr, #histoFactures tr { display: flex; flex-wrap: wrap; align-items: center;
    padding: 12px 2px; height: auto; border-bottom: 1px solid var(--border); }
  #panierTable tr:last-child, #histoFactures tr:last-child { border-bottom: 0; }
  #panierTable td, #histoFactures td { display: block; padding: 0; border: 0; min-width: 0;
    font-size: 14px; white-space: normal; overflow: visible; text-align: left; }
  #panierTable td.p-prod { order: 1; flex: 1 1 0; font-size: 15px; }
  #panierTable td.p-x { order: 2; flex: 0 0 auto; margin-left: 8px; }
  #panierTable td.p-qte { order: 3; flex: 0 0 auto; margin-top: 8px; padding-left: 50px; }
  #panierTable td.p-prix, #panierTable td.p-rem { order: 4; flex: 0 0 auto; margin-top: 8px; margin-left: 12px;
    font-size: 12.5px; color: var(--muted); white-space: nowrap; }
  #panierTable td.p-prix::before { content: 'PU '; }
  #panierTable td.p-rem::before { content: 'Remise '; }
  #panierTable td.p-rem:has(> .vide:only-child) { display: none; }
  #panierTable td.p-tot { order: 6; flex: 0 0 auto; margin-left: auto; margin-top: 8px; font-size: 15px; font-weight: 800; }
  #panierTable tr.panier-vide td { flex: 1 1 100%; }
  #histoFactures td.h-num { order: 1; flex: 1 1 0; font-size: 15px; }
  #histoFactures td.h-ttc { order: 2; flex: 0 0 auto; margin-left: auto; font-size: 15px; font-weight: 800; }
  #histoFactures td.h-date { order: 3; flex: 1 1 100%; margin-top: 4px; font-size: 12.5px; color: var(--muted); }
  #histoFactures td.h-ht { order: 4; flex: 0 0 auto; margin-top: 6px; font-size: 12.5px; color: var(--muted); }
  #histoFactures td.h-ht::before { content: 'HT '; }
  #histoFactures td.h-stat { order: 5; flex: 0 0 auto; margin-left: auto; margin-top: 6px; }
}

/* 🔴 02/09 (Julien) : le nom et le dessin de la fiche se modifient depuis
   l'en-tête (crayon à côté du nom, clic sur le nom ou la vignette). */
.client-hero h1 + .ch-edit {
  border: 1px solid var(--border); background: var(--card); border-radius: 9px;
  width: 30px; height: 30px; padding: 0; cursor: pointer; margin-left: 10px;
  display: inline-flex; align-items: center; justify-content: center; vertical-align: middle;
}
.client-hero h1 + .ch-edit img { width: 15px; height: 15px; }
.client-hero h1 + .ch-edit:hover { border-color: var(--red); background: var(--red-soft); }
.client-hero h1 { display: inline; }
.dlg-ident { max-width: 440px; text-align: left; }
.dlg-ident h3 { text-align: center; }
.dlg-ident .dlg-lab { display: block; font-size: 13px; font-weight: 700; color: var(--muted); margin-bottom: 12px; }
.dlg-ident .dlg-lab .fi-in { display: block; width: 100%; margin-top: 5px; font-size: 16px; background: #FFFFFF; }
.dlg-ident .dlg-err { color: var(--red); font-weight: 700; margin: 0 0 12px !important; }
