/* ---------------------------------------------------------------------------
   PALETTE (30/07) — chaque couleur porte le nom de son RÔLE, pas de sa teinte.
   « --clair » restera vrai le jour où ce gris-bleu deviendra un gris-vert ;
   « --bleu-pale » aurait menti. Les cinq valeurs marquées (*) étaient écrites
   en dur dans la feuille : elles remontent ici pour qu'on ne puisse plus en
   inventer une seizième variante.
   Contrastes vérifiés (WCAG 2.1 — texte normal 15 px, seuil AA = 4,5:1). Les
   trois valeurs qui NE PASSENT PAS sont signalées à leur ligne : elles restent
   utilisables comme repère graphique, jamais sous un libellé blanc.
   --------------------------------------------------------------------------- */
:root {
  /* Identité */
  --bleu:      #1f3864;  /* geste principal, titres         · blanc dessus 11,6:1 */
  --bleu2:     #2e5a88;  /* survol du principal, liens      · blanc dessus  7,2:1 */

  /* Surfaces */
  --fond:      #f4f6f9;  /* fond de page */
  --carte:     #ffffff;  /* fond d'un cadre */
  --clair:     #e8edf4;  /* (*) fond d'une action SECONDAIRE — .btn.light, .act-close */
  --clair-h:   #d7e0ed;  /* son survol                      · --bleu dessus  8,7:1 */
  --selection: #eaf0f8;  /* (*) élément RETENU — .subtile.on, button.tile.on */
  --survol:    #eef3fa;  /* (*) survol d'une surface neutre — tuile, lien d'action */

  /* Traits et texte */
  --ligne:     #dde4ec;  /* filet, bordure de cadre */
  --bordure:   #dde4ec;  /* alias de --ligne. C'est le nom employé par
                            structures_emettrices.php:353 et :458 ; la variable
                            n'existait pas, les deux <hr> rendaient `0px none`.
                            À faire disparaître une fois ces deux lignes
                            repassées à var(--ligne). */
  --texte:     #22303f;  /*                                 · sur blanc    13,4:1 */
  --muted:     #5a6a7c;  /* texte secondaire                · sur blanc     5,5:1 */

  /* Verdicts */
  --alerte:    #b3261e;  /* IRRÉVERSIBLE, et rien d'autre   · blanc dessus  6,5:1 */
  --alerte-h:  #8f1e18;  /* son survol                      · blanc dessus  8,9:1 */
  --alerte-fond: #fdecea;/* fond très pâle d'un survol/bandeau d'alerte */
  --ok:        #1d7a3e;  /* création, entrée, réussite      · blanc dessus  5,4:1 */
  --ok-h:      #17652f;  /* son survol                      · blanc dessus  7,2:1 */
  --orange:    #c9720b;  /* accent : montant client, réseau, bouton Suivi.
                            ⚠ blanc dessus 3,6:1 — SOUS le seuil AA. Conservé
                            tel quel (décisions de Morgan sur .btn-suivi et sur
                            le Total TTC), à ne pas étendre à de nouveaux fonds. */
  --orange-h:  #a85e09;  /* son survol                      · blanc dessus  4,9:1 */
  --warn-bg:   #fff6e5;  /* fond d'un état à surveiller     · --bleu dessus 10,8:1 */
  --attente:   #ba7517;  /* (*) bouton occupé, le temps de la requête.
                            ⚠ blanc dessus 3,7:1 — sous le seuil AA. */
  --seuil:     #e3b505;  /* repère jaune des jauges de marge (index.php,
                            widget Prix indicatif).
                            ⚠ blanc dessus 1,9:1 — À PROSCRIRE. Un libellé posé
                            sur ce jaune s'écrit en var(--texte) : 7,0:1. */
  --violet:    #6d28d9;  /* déclarée, plus aucun usage au 30/07 */
}
* { box-sizing: border-box; }
/* La place de la barre de défilement est TOUJOURS réservée (29/07, Morgan :
   « pourquoi la différence de marge des modules entre 2 onglets ? »).
   Mesure faite sur la feuille de style réelle : les colonnes font exactement
   523 et 723 px sur les trois onglets, séparateur à 540 px. La seule chose qui
   bougeait, c'était la fenêtre elle-même — la barre de défilement apparaît sur
   un onglet long, disparaît sur un onglet court, et le contenu centré se
   décale de la moitié de sa largeur à chaque passage.
   scrollbar-gutter réserve la gouttière en permanence : la mise en page ne
   sursaute plus d'un onglet à l'autre, ni d'une page à l'autre. */
html { scrollbar-gutter: stable; }
[hidden] { display: none !important; }
body { margin: 0; font-family: -apple-system, "Segoe UI", Arial, sans-serif; background: var(--fond); color: var(--texte); font-size: 15px; }
main { max-width: 1600px; margin: 0 auto; padding: 16px; }
h1 { color: var(--bleu); font-size: 24px; margin: 12px 0 16px; }
h2 { color: var(--bleu2); font-size: 17px; margin: 24px 0 8px; }
a { color: var(--bleu2); text-decoration: none; }
a:hover { text-decoration: underline; }
.muted { color: var(--muted); font-size: 13px; }

/* Barre de navigation (25/07, nuance de Morgan) : la marque « Cluster Keaten »
   et « Morgan · sortir » restent au vrai bord de l'écran (comme avant), SEULS
   Aujourd'hui/Réglages/les 3 menus du milieu (.navbox) s'alignent sur le
   gabarit de <main> (max-width 1100px centré) — pas toute la barre. */
.topbar { background: var(--bleu); color: #fff; display: flex; align-items: center; gap: 16px; padding: 10px 16px; flex-wrap: wrap; position: sticky; top: 0; z-index: 10; }
.topbar .brand { font-weight: 700; color: #fff; }
.topbar .brand:hover { text-decoration: none; opacity: .85; }
.topbar .brand span { font-weight: 400; opacity: .8; }
.topbar .who { font-size: 13px; opacity: .9; margin-left: auto; }
.topbar .who a { color: #fff; text-decoration: underline; }
/* Même technique de centrage que <main> (margin:auto entre deux bords fixes),
   pas un left:50%+transform séparé : les deux calculs peuvent diverger de
   quelques pixels selon les navigateurs, cassant l'alignement visuel avec le
   contenu en dessous (signalé par Morgan le 27/07). */
/* 31/07, Morgan : « Développement trop collé à Aujourd'hui — agencer les
   groupes de menus de manière équilibrée, recentrer Réglages/Webmaster et
   Développement vers le bloc principal D-P-A ». La navbox porte les trois
   groupes ; la resserrer rapproche les DEUX groupes latéraux du centre sans
   toucher au bloc central (qui reste centré sur la barre entière, cf. plus
   bas), et dégage la marque « Cluster Keaten · Aujourd'hui » à gauche. */
/* 31/07 (Morgan) : « rapprocher encore les blocs extérieurs vers le bloc DPA ».
   La largeur de la navbox EST la distance entre Développement et Réglages : la
   resserrer ramène les deux groupes extérieurs vers Diffusion-Production-Admin
   sans toucher au centrage du bloc du milieu. 1180 → 740 px : mesuré au
   navigateur en 1512 px de large, l'écart Développement ↔ Diffusion passe de
   195 à 125 px. Sous 1360 px de large c'est calc(100% - 620px) qui reprend la
   main, donc rien ne change sur les écrans étroits. */
.navbox { position: absolute; left: 0; right: 0; margin: 0 auto; width: 100%; max-width: min(820px, calc(100% - 620px)); padding: 0 16px; box-sizing: border-box; }
.topbar nav { display: flex; align-items: center; flex-wrap: wrap; width: 100%; }
.navgrp { display: flex; gap: 4px; flex-wrap: wrap; flex: 1; justify-content: center; }
/* 31/07 : Développement à gauche en symétrie de Réglages/Webmaster à droite.
   navcenter étant sorti du flux (absolu, cf. ci-dessous), navleft et navright
   se partagent la ligne : le premier calé à gauche, le second poussé à droite
   par margin-left:auto — les trois menus métier restent centrés sur la barre
   ENTIÈRE, indépendamment de la largeur des deux extrémités. */
/* Les deux groupes extérieurs ont la meme largeur reservee et poussent leur
   contenu VERS le centre : Developpement et Reglages/Webmaster se retrouvent
   alors a la meme distance du bloc Diffusion-Production-Admin (mesure : 84 px
   de chaque cote en 1512 px de large). */
.navgrp.navleft { flex: 0 0 auto; justify-content: flex-end; }
/* 2e bug trouvé le 27/07 (signalé par Morgan) : même avec navright en
   flex:0 0 auto, navcenter ne prenait que « le reste » (largeur totale moins
   Réglages) et se centrait DANS ce reste — donc toujours décalé, Réglages
   comptant encore dans le calcul. Seule solution fiable : sortir navcenter du
   flux (absolu, centré sur .navbox EN ENTIER, indépemment de la largeur de
   n'importe quel autre élément) ; navright reste dans le flux normal, poussé
   à droite par margin-left:auto. */
.navgrp.navcenter { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); flex: none; }
.navgrp.navright { flex: 0 0 auto; justify-content: flex-start; margin-left: auto; }
/* La largeur reservee identique n'est posee QUE sur les ecrans assez larges
   pour l'absorber : en dessous, les trois groupes ont deja juste la place de
   tenir sur une ligne et un plancher de 190 px les ferait se chevaucher. */
@media (min-width: 1300px) {
  .navgrp.navleft, .navgrp.navright { min-width: 190px; }
}
.topbar nav a { color: #dce6f5; padding: 6px 10px; border-radius: 6px; }
.topbar nav a.on, .topbar nav a:hover { background: rgba(255,255,255,.15); color: #fff; text-decoration: none; }

/* Cartes, messages */
.card { background: var(--carte); border: 1px solid var(--ligne); border-radius: 10px; padding: 14px 16px; margin: 12px 0; }
.card.narrow { max-width: 460px; }

/* ---- FICHE EN DEUX VOLETS (V3, 29/07) — le vrai geste d'ORPHEO ----
   Ma première tentative (V2) se contentait de ranger les mêmes cartes arrondies
   en deux colonnes : plus de boîtes, plus de bordures, plus de gouttières — et
   Morgan a eu raison de dire que c'était « même moins bien ».
   ORPHEO ne pose pas de cartes. Il pose UNE surface blanche continue, coupée en
   deux par un simple trait vertical : à gauche l'identité de la structure, à
   droite son activité. Les sections n'y sont séparées que par des filets fins.
   D'où : une .card à l'intérieur d'un volet perd bordure, arrondi et fond
   propre — il ne reste que son titre et un filet de séparation. */
/* align-items: STRETCH, surtout pas `start` (29/07 : « j'ai perdu le journal »).
   Avec `start`, une cellule ne s'étire pas à la hauteur de sa rangée : elle
   vaut la hauteur de son contenu. Or le journal a une base de zéro — c'est ce
   qui laisse le module de droite décider de la hauteur — donc la cellule
   valait zéro et le journal disparaissait purement et simplement.
   Les deux règles ne peuvent pas coexister : c'est l'étirement qui donne au
   journal la place qu'il doit remplir. */
/* minmax(0, 1fr) et NON 1fr (29/07, Morgan : « pourquoi la différence de marge
   des modules entre 2 onglets ? »). Une piste `1fr` a un minimum implicite égal
   à la largeur MINIMALE de son contenu : le calculateur et le formulaire Infos
   sont larges, ils poussaient donc la piste au-delà de sa part et la grille
   débordait — d'où un séparateur qui semblait se déplacer d'un onglet à l'autre
   alors que les 42 % n'ont jamais changé.
   minmax(0, …) supprime ce minimum : le contenu se plie, la colonne reste. */
.fiche { display: grid; grid-template-columns: 42% minmax(0, 1fr); align-items: stretch;
         background: var(--carte); border: 1px solid var(--ligne); border-radius: 10px;
         margin-top: 12px; }
/* `overflow: hidden` a été RETIRÉ (29/07, Morgan : « rendre la partie mail
   mobile afin qu'elle suive le scroll »). Un ancêtre en overflow hidden annule
   tout position:sticky à l'intérieur — c'est la règle la plus souvent oubliée
   du positionnement collant. Il servait à couper les angles du contenu sur les
   coins arrondis ; les cellules portent maintenant leur propre arrondi, ce qui
   donne le même dessin sans bloquer le collage. */
.fiche > .cell.gauche:first-child { border-radius: 10px 0 0 0; }
.fiche > .cell.droite:nth-child(2) { border-radius: 0 10px 0 0; }
.fiche > .cell.gauche:last-of-type { border-bottom-left-radius: 10px; }
.fiche > .cell.droite:last-child { border-bottom-right-radius: 10px; }
/* La grille est ce qui fait tenir la promesse « Envoyer un email à la même
   hauteur que le journal » : ses rangées sont communes aux deux colonnes, donc
   la deuxième rangée démarre au même endroit des deux côtés, que la fiche
   porte un contact ou douze. Deux colonnes qui coulent librement ne peuvent
   pas faire ça. */
/* La cellule est une PILE VERTICALE : c'est ce qui permet au journal de
   s'étirer sur la place restante. Déclaration supprimée par erreur en retirant
   le plafond de hauteur — sans elle, `flex: 1 1 0` sur le journal ne s'applique
   à rien et le bloc retombe à la hauteur de son titre. */
/* Marge du HAUT gérée par le premier enfant, pas par la cellule (29/07,
   Morgan : « je parle de cet espace-là » et « et de celui-là »).
   Les 4 px de la cellule s'ajoutaient aux marges propres de chaque bloc : le
   titre des contacts se retrouvait tantôt collé à la bordure, tantôt loin, et
   la barre d'onglets flottait au lieu de s'accrocher au bord du module.
   Cellule à zéro en haut : la carte pose ses 14 px de respiration, la barre
   d'onglets vient à ras comme un vrai onglet. */
.fiche > .cell { min-width: 0; padding: 0 20px 18px; display: flex; flex-direction: column; }
.fiche > .cell.gauche { border-right: 1px solid var(--ligne); background: #fcfdfe; }
.fiche .cell .card { background: none; border: 0; border-radius: 0; padding: 14px 0; margin: 0;
                     border-top: 1px solid var(--ligne); }
.fiche .cell .card:first-of-type { border-top: 0; }
.fiche .cell > .card > b:first-child { display: block; color: var(--bleu2); font-size: 16px; margin-bottom: 8px; }
/* La barre d'onglets appartient au volet droit : calée à gauche, sans la marge
   de centrage qu'elle a quand elle traverse toute la page. */
.fiche .cell.droite > .ftabs { margin: 0 -20px 10px; padding: 0 20px; justify-content: flex-start; }
/* Le premier bloc d'une cellule respire de 14 px sous la bordure. */
.fiche .cell > .card:first-child { padding-top: 14px; }
@media (max-width: 1000px) {
  .fiche { display: block; }
  .fiche > .cell.gauche { border-right: 0; border-bottom: 1px solid var(--ligne); }
}
/* Popup générique (27/07, Morgan : « chantier, généraliser les popups ») —
   <dialog> natif, sans JS de positionnement/overlay à maintenir. À réutiliser
   tel quel pour toute prochaine popup (ex. Rappel/Référent sur mails.php). */
/* 31/07 — une popup ne doit JAMAIS dépasser l'écran : elle est modale, donc
   si ses boutons sortent du champ de vision on ne peut plus ni valider, ni
   annuler, ni cliquer ailleurs dans l'app (Morgan, bloqué : « je n'arrive plus
   à cliquer sur PROD »). Hauteur bornée + défilement interne. */
dialog { background: var(--carte); border: 1px solid var(--ligne); border-radius: 10px; padding: 18px 20px; max-width: 420px; width: calc(100% - 32px); max-height: 86vh; overflow-y: auto; }
dialog::backdrop { background: rgba(20, 30, 45, .45); }
dialog .fld { margin-bottom: 10px; }
.flash { background: #e7f4ea; border: 1px solid #bfe3c8; color: var(--ok); padding: 10px 14px; border-radius: 8px; margin: 10px 0; }
.flash.err { background: #fdecea; border-color: #f5c6c1; color: var(--alerte); }

/* Tuiles du tableau de bord */
.tiles { display: flex; gap: 10px; flex-wrap: wrap; margin: 8px 0 16px; justify-content: center; }
.tile { background: var(--carte); border: 1px solid var(--ligne); border-radius: 10px; padding: 10px 16px; min-width: 110px; text-align: center; color: var(--texte); font-size: 13px; }
.tile b { display: block; font-size: 24px; color: var(--bleu); }
.tile.warn { background: var(--warn-bg); border-color: #f0dcae; }
.tile.ok b { color: var(--ok); }
/* Tuile SÉLECTIONNÉE (29/07) : sur Aujourd'hui, les tuiles chargent leur
   module sous la barre d'onglets au lieu de quitter la page — il faut donc
   voir laquelle est active, comme pour un onglet. */
.tile.on { border-color: var(--bleu); font-weight: 600; }
/* Tuile orange pleine (28/07) : sert au Total TTC du calculateur de prix de
   cession — le chiffre que voit le client, à distinguer d'un coup d'œil des
   montants HT internes. */
.tile.orange { background: var(--warn-bg); border-color: #f0dcae; }
.tile.orange b { color: var(--orange); }
/* Rangée des parts (%) du calculateur de prix de cession (28/07, Morgan :
   « adapter les tailles pour que ça occupe la largeur en ligne pleine… si
   écran de téléphone 2 lignes de 4 »). Les tuiles se partagent la largeur à
   parts égales (flex-basis 0) au lieu de garder leur largeur naturelle, ce qui
   laissait un blanc à droite et renvoyait la dernière seule à la ligne.
   Exactement 8 tuiles, d'où les 25 % sur téléphone = 2 lignes de 4. */
.tiles.parts { flex-wrap: nowrap; }
.tiles.parts .tile { flex: 1 1 0; min-width: 0; padding: 10px 6px; font-size: 12px; }
.tiles.parts .tile b { font-size: 20px; }
@media (max-width: 720px) {
  .tiles.parts { flex-wrap: wrap; }
  .tiles.parts .tile { flex: 1 1 calc(25% - 8px); }
}
a.tile:hover { text-decoration: none; border-color: var(--bleu2); }
/* Paliers de prix d'une date (annotation #48) : chaque tuile porte SA légende
   — « social, citoyen, local… », « c'est notre prix »… Ces phrases sont le
   cœur de la demande de Morgan, pas une décoration : elles disent à quoi on
   consent en descendant d'un palier. Elles ont donc besoin de place, d'où des
   tuiles plus larges que les tuiles-compteurs et un texte aligné à gauche. */
.tiles.paliers { flex-wrap: wrap; gap: 8px; justify-content: stretch; }
.tiles.paliers .tile { flex: 1 1 190px; min-width: 170px; padding: 10px 12px; }
.tiles.paliers .tile b { font-size: 20px; }
.tiles.paliers .tile:hover { border-color: var(--bleu2); }
@media (max-width: 720px) { .tiles.paliers .tile { flex: 1 1 calc(50% - 8px); } }

/* Sous-menus en cases (25/07, demande de Morgan) : Diffusion/Production/
   Admin & Finances/Réglages présentent leurs pages en cases cliquables plutôt
   qu'en simples onglets texte — la case active se distingue par une bordure
   bleue et un fond légèrement teinté (pas besoin de réécrire son nom). Sert
   aussi de base pour plus tard : une case se colorera dès qu'il y a une
   nouveauté à traiter dedans (ex. une nouvelle date), comme .tile.warn/.ok
   le font déjà sur Aujourd'hui. */
.subtiles { display: flex; gap: 10px; flex-wrap: wrap; margin: 8px 0 16px; justify-content: center; }
.subtile { background: var(--carte); border: 1px solid var(--ligne); border-radius: 10px; padding: 10px 16px; min-width: 110px; color: var(--texte); font-size: 13px; text-align: center; }
.subtile:hover { text-decoration: none; border-color: var(--bleu2); }
.subtile.on { border-color: var(--bleu); background: #eaf0f8; font-weight: 700; color: var(--bleu); }
.subtile.warn { background: var(--warn-bg); border-color: #f0dcae; }
.subtile.warn.on { background: #eaf0f8; }
.subtiles-mobile { display: none; }

/* Tableaux */
.tablewrap { overflow-x: auto; background: var(--carte); border: 1px solid var(--ligne); border-radius: 10px; }
table { border-collapse: collapse; width: 100%; font-size: 14px; }
th { background: #eef2f7; color: var(--bleu); text-align: left; padding: 8px 10px; white-space: nowrap; }
td { border-top: 1px solid var(--ligne); padding: 8px 10px; vertical-align: top; }
/* Relance en retard (28/07, Morgan, annotation #18 : « fond violet sous les
   tâches en retard ») — même teinte que .pastrow (dates passées, pipeline.php),
   même famille de « ça traîne, à traiter ». */
tr.late td { background: #f3f0fb; }
tr.okrow, tr.okrow td { background: #f2faf4; }
/* Date passée, statut ni Confirmé ni Annulé (27/07, Morgan) : à traiter en priorité. */
tr.pastrow, tr.pastrow td { background: #f3f0fb; }
/* Date à venir dans les 2 mois, année en cours (28/07, Morgan). */
tr.soonrow, tr.soonrow td { background: var(--warn-bg); }
/* Hauteur de ligne STRICTEMENT identique partout (28/07, Morgan : « évident
   normalement ») — un simple `height` se comporte comme un plancher en
   tableau (une ligne avec plusieurs tags/avertissements grandit quand même
   au-delà), d'où l'écart persistant entre pages. `overflow:hidden` en fait un
   vrai plafond, cette fois sans exception. Seule exception volontaire : une
   note dépliée par clic (voir <details> plus bas) peut faire grandir SA
   ligne — c'est une action explicite de l'utilisateur, pas un débordement
   accidentel de contenu. */
.pipeline-dates td { height: 72px; overflow: hidden; }
.pipeline-dates td:has(details[open]) { height: auto; overflow: visible; }
/* Colonne Date largeur fixe (28/07, Morgan : « les colonnes des dates aient
   la même largeur entre toutes les pages des années ») — sans ça, une année
   avec des dates courtes (jj/mm/aaaa) et une année avec du texte de période
   long (« samedis de décembre ») donnaient des tableaux de largeurs
   différentes d'une page Année à l'autre. */
.pipeline-dates th:first-child, .pipeline-dates td:first-child { width: 130px; max-width: 130px; overflow-wrap: break-word; }
/* Option sans date précise (texte de période seul, ex. « mi décembre ») —
   épinglée en haut (voir ORDER BY de pipeline.php), teinte bleue distincte
   de tous les autres codes couleur (28/07, Morgan). */
tr.perioderow, tr.perioderow td { background: #eaf2fb; }
td.num, th.num { text-align: right; white-space: nowrap; }
/* Actions secondaires (promouvoir/modifier/retirer…) plus discrètes (27/07,
   Morgan : « plus petit ou plus pâle pour optimiser l'ergonomie visuelle ») —
   réduites en taille, « retirer » garde sa couleur alerte (danger), le reste
   s'estompe un peu par rapport au contenu principal de la ligne. */
/* 29/07, Morgan : « ça doit tenir sans scroller horizontalement ! » — sur
   mails.php, 4 actions (traité/rappel/transférer/ignorer) forcées sur une
   seule ligne (nowrap) poussaient le tableau bien au-delà de .tablewrap,
   obligeant à défiler. Passé en flex + wrap : les actions s'enchaînent tant
   qu'il y a de la place, puis reviennent à la ligne — la colonne reste
   étroite au lieu d'imposer sa largeur à toute la ligne. */
/* 30/07 — `align-items: center` est indispensable depuis que les boutons ont
   une géométrie commune. Sans lui, la valeur par défaut `stretch` étirait
   chaque bouton à la hauteur de la cellule : mesuré sur devis.php, un « 📄 voir »
   passait de 30 à 48 px et la ligne de 47 à 65 px, sur 40 lignes. */
.rowactions { font-size: 12px; opacity: .85; display: flex; flex-wrap: wrap; align-items: center; gap: 2px 8px; max-width: 160px; }
.rowactions form { display: inline; }
/* mails.php (29/07, « t'as pas fini ton travail !!!! ») : les 4 actions
   passaient toujours sur 2 lignes malgré les 24 % de colonne, parce que le
   `max-width: 160px` de .rowactions ci-dessus continuait de s'appliquer et
   écrasait la largeur disponible. On l'annule ici (max-width: none) et on
   force la ligne unique (nowrap) : la colonne a désormais la place, le repli
   n'a plus lieu d'être. Les liens ne se coupent pas non plus au milieu d'un
   mot (« transférer »). */
.rowactions.empile { display: flex; flex-wrap: nowrap; gap: 0 12px; max-width: none; white-space: nowrap; }

/* 29/07, mails.php : colonnes à largeur fixe (posées via <colgroup>) plutôt
   que la largeur « naturelle » du contenu — c'est ça qui poussait le tableau
   hors du cadre, pas seulement les actions. Le texte s'enchaîne sur plusieurs
   lignes dans sa colonne au lieu de l'élargir (Structure + commentaire de
   transfert compris). Scopé à .mails-table pour ne pas changer les tableaux
   qui fonctionnaient déjà ailleurs. */
.mails-table table { table-layout: fixed; width: 100%; }
/* overflow-wrap SEUL (pas word-break: break-word, trop agressif — il avait
   coupé « Morgan » en « Morga »/« n » alors que la colonne Réf. a maintenant
   assez de place pour son plus long prénom sans jamais casser un mot ; il ne
   reste utile qu'en tout dernier recours, sur un mot qui ne tiendrait vraiment
   sur aucune ligne). */
.mails-table td, .mails-table th { overflow-wrap: break-word; white-space: normal; }
.mails-table th[data-notri] { white-space: normal; }
.bar { background: #eef2f7; border-radius: 6px; height: 12px; overflow: hidden; }
.bar span { display: block; height: 100%; background: var(--bleu2); }

/* Badges */
.prio { display: inline-block; width: 22px; height: 22px; line-height: 22px; text-align: center; border-radius: 50%; font-weight: 700; font-size: 12px; color: #fff; }
.prio.pA { background: var(--bleu); } .prio.pB { background: #5c7396; } .prio.pC { background: #b9c5d4; color: #333; }
.tag { font-size: 11px; padding: 1px 7px; border-radius: 9px; background: #eef2f7; }
.tag.err { background: #fdecea; color: var(--alerte); border: 1px solid #f5c6c1; }

/* Formulaires */
label { display: block; font-size: 12px; color: var(--muted); margin: 8px 0 2px; }
/* Un <label> utilisé comme bouton (.btn) doit ignorer le style de label de
   formulaire ci-dessus — sinon taille/couleur/marge divergent visuellement
   d'un vrai bouton/lien .btn (27/07, repéré par Morgan sur « + Créer réseau »
   vs « + Nouveau contact »). Sélecteur élément+classe, plus spécifique que le
   simple `label` ci-dessus : gagne quel que soit l'ordre dans le fichier. */
label.btn { display: inline-flex; font-size: 15px; color: #fff; margin: 0; line-height: normal; }
input, select, textarea { font: inherit; padding: 7px 9px; border: 1px solid var(--ligne); border-radius: 7px; background: #fff; width: 100%; }
input[type="checkbox"] { width: auto; }

/* ===========================================================================
   LES CINQ FAMILLES DE BOUTONS (30/07)
   ---------------------------------------------------------------------------
   DOCTRINE — à lire avant d'ajouter le moindre bouton.

   On démarche des programmateurs AU TÉLÉPHONE. Une hésitation d'une seconde
   sur un bouton, c'est un blanc dans la conversation. La couleur d'un bouton
   ne décore donc pas : elle répond à UNE seule question, « qu'est-ce que ça me
   coûte si je me trompe ? ». D'où cinq familles, pas vingt recettes.

     .btn         LE geste principal de l'écran. Bleu plein. Un seul par cadre :
                  Enregistrer, Créer, Envoyer, Valider. Variantes de sens :
                  `.btn.add` (vert) pour une CRÉATION, `.btn.add.reseau`
                  (orange) pour la création d'un réseau.
     .btn.light   Tous les autres gestes courants et RÉVERSIBLES : Annuler,
                  Fermer, Précédent, Clore, rouvrir, changer de page. Fond
                  clair, texte bleu. Aucune limite de nombre — c'est la famille
                  par défaut dès qu'il y a plus d'un bouton dans un cadre.
     .btn.ghost   Le geste qu'on propose sans l'inviter : option avancée,
                  repli, action rare. Transparent, simple filet.
     .danger      SUPPRIMER · RETIRER · DÉTACHER · FUSIONNER · définitif.
                  Rouge. Test : si l'action se rattrape en un clic, ce n'est
                  PAS un .danger — c'est un .btn.light ou un .linklike.
     .linklike    Une action au fil du texte ou dans une colonne de tableau :
                  modifier, voir, réactiver, épingler, assigner. NEUTRE et
                  bleue — c'est un lien, pas un avertissement. Se combine :
                  `class="linklike danger"` donne le lien ROUGE des gestes
                  irréversibles en tableau, sans y planter un pavé rouge.

   GÉOMÉTRIE UNIQUE. Les cinq familles partagent min-height 33px, inline-flex,
   padding 8px 14px, arrondi 7px, et 44px de haut sous 720px. C'est ce bloc —
   et pas chaque page — qui garantit qu'un bouton de 4 caractères et un de 20
   ont la même hauteur, et qu'un bouton de cadre s'aligne sur celui d'une ligne
   de tableau (mesuré avant : 33px dans une carte, 30px dans .rowactions).

   RÈGLE ABSOLUE : jamais de couleur ni de padding de bouton dans un `style=`
   en ligne. S'il manque une famille, elle s'ajoute ICI. C'est exactement en
   contournant ce bloc qu'on est arrivé à 20 recettes et 16 hauteurs.
   =========================================================================== */

/* Géométrie commune. `:where()` annule la spécificité des exclusions : la
   règle reste à (0,0,1) pour <button>, donc .btn-actions, .btn-suivi et
   button.tile continuent de gagner avec leur simple classe, comme avant.
   Les exclus ne sont pas des boutons mais des COMPOSANTS qui se trouvent être
   des <button> : tuile-chiffre (enfants en display:block), onglet, menu
   burger, croix de bandeau, repère de jauge. */
button:where(:not(.tile, .ft, .burger, .toast-x, .repere)), .btn, .linklike {
  font: inherit;
  /* 30/07 — `line-height` fait partie de la géométrie, au même titre que le
     padding. Sans lui (`normal`), un émoji impose sa propre boîte de ligne :
     mesuré sur devis.php, « 📄 voir » en 12px rendait un contenu de 28,5px,
     donc un bouton de 48px et une ligne de tableau de 65px au lieu de 47px.
     À 1.15, le contenu ne dépasse jamais le `min-height` ci-dessous, quel que
     soit le corps de la police ou la présence d'un pictogramme. */
  line-height: 1.15;
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 33px; padding: 8px 14px;
  border: 1px solid transparent; border-radius: 7px;
  text-align: center; text-decoration: none; cursor: pointer;
}

/* 1 — PRINCIPAL */
button, .btn { background: var(--bleu); color: #fff; }
button:hover, .btn:hover { background: var(--bleu2); color: #fff; text-decoration: none; }
button.add, .btn.add { background: var(--ok); margin-left: auto; }
button.add:hover, .btn.add:hover { background: var(--ok-h); }
.btn.add.reseau { background: var(--orange); }
.btn.add.reseau:hover { background: var(--orange-h); }

/* 2 — SECONDAIRE */
button.light, .btn.light { background: var(--clair); color: var(--bleu); }
button.light:hover, .btn.light:hover { background: var(--clair-h); color: var(--bleu); }

/* 3 — DISCRET (nouvelle famille) : présent, lisible, mais qui ne réclame rien.
   Le filet lui donne la même emprise qu'un bouton plein — il ne « saute » pas
   d'un pixel au survol, contrairement à un bouton sans bordure. */
button.ghost, .btn.ghost { background: none; color: var(--bleu2); border-color: var(--ligne); }
button.ghost:hover, .btn.ghost:hover { background: var(--survol); color: var(--bleu); border-color: var(--bleu2); }
/* Bascule « + Créer réseau » en pur CSS (27/07) : plus fiable qu'un <details>
   ouvert via onclick, qui ne s'ouvrait pas de façon systématique. */
.toggle-cb { display: none; }
.creer-reseau-panel { display: none; }
.toggle-cb:checked + .creer-reseau-panel { display: block; }
/* « + Nouveau contact » à gauche des onglets Clients/Réseau/Stat… (27/07) —
   annule le margin-left:auto hérité de .btn.add (pensé pour l'ancien
   emplacement, dans un bloc flex différent). */
/* Les onglets doivent se centrer sur toute la largeur, PAS dans le reste
   après le bouton (même piège que navcenter/navright ci-dessus) — le bouton
   sort donc du flux (absolu, à gauche), les onglets restent seuls dans le
   flux normal et se centrent sur .ftabs-with-btn en entier. */
.ftabs-with-btn { position: relative; display: flex; align-items: center; justify-content: center; min-height: 40px; margin: 4px 0 16px; }
.ftabs-with-btn .btn.add { position: absolute; left: 0; top: 50%; transform: translateY(-50%); margin-left: 0; }
.ftabs-with-btn .ftabs { margin: 0; width: 100%; }
/* 4 — DESTRUCTIF. Le rouge ne dit pas « attention » : il dit « on ne revient
   pas en arrière ». Il n'y a donc rien à négocier sur cette famille — soit le
   geste est irréversible et il est rouge, soit il ne l'est pas et il ne l'est
   pas. Mesuré au 30/07 : 26 gestes irréversibles, 3 portaient .danger. */
.danger, button.danger, .btn.danger { background: var(--alerte); color: #fff; border-color: transparent; }
.danger:hover, button.danger:hover, .btn.danger:hover { background: var(--alerte-h); color: #fff; }

/* 5 — LIEN D'ACTION. Couleur INVERSÉE le 30/07, c'était la faute centrale de
   la feuille : `linklike` naissait rouge alerte alors que 48 de ses 74 emplois
   sont des gestes neutres (modifier, réactiver, désactiver, épingler,
   assigner, archiver, voir, Clore, rouvrir) et que 22 autres portaient un
   `style="color:var(--bleu2)"` pour redevenir bleus. Autrement dit la valeur
   par défaut était fausse 70 fois sur 74. Elle est donc neutre par défaut, et
   rouge seulement sur demande explicite (.linklike.danger) — les 22 style= en
   ligne n'ont plus de raison d'être.
   Sélecteur de CLASSE et non `button.linklike` : les trois <a class="linklike">
   de l'app (cachets_frais.php:845 entre autres) n'étaient tout simplement pas
   stylés.
   Seule dérogation à la géométrie commune : le padding horizontal tombe à 6px.
   `.rowactions` est plafonné à 160px et `.rowactions.empile` porte les quatre
   actions de mails.php sur une ligne unique ; à 14px de padding, « transférer »
   ne tient plus et le tableau ressort de son cadre — c'est exactement le
   défilement horizontal que Morgan a fait corriger le 29/07. La hauteur, elle,
   reste celle de tout le monde : 33px. */
.linklike { background: none; color: var(--bleu2); border-color: transparent;
            padding: 8px 6px; text-decoration: underline; }
.linklike:hover { background: var(--survol); color: var(--bleu); text-decoration: underline; }
.linklike.danger { background: none; color: var(--alerte); }
.linklike.danger:hover { background: var(--alerte-fond); color: var(--alerte-h); }

/* Placement : un geste irréversible ne se colle JAMAIS à « modifier ». Dans
   une barre d'actions il part en bout de ligne, séparé par tout l'espace
   disponible. Cas mesuré qui a motivé la règle — structures_emettrices.php:
   372-383, quatre liens côte à côte (« ✉ envoyer », « modifier », « en congé »,
   « retirer ») dont un seul détruisait, tous au même endroit. */
.rowactions .danger, .actbtns .danger { margin-left: auto; padding-left: 12px; }

/* 30/07 — LA CELLULE D'ACTIONS EST UN CONTEXTE DENSE, et c'est le seul endroit
   où la géométrie commune cède sur une seule dimension : la hauteur.
   Motif mesuré, pas supposé : sur devis.php, un « 📄 voir » fait 28,5px de
   contenu pour une police de 12px — l'émoji impose sa propre boîte de ligne, et
   ni `line-height` ni `align-items` ne la contraignent. En ajoutant 8px de
   rembourrage haut et bas, la ligne de tableau passait de 49 à 62,6px : sur les
   40 lignes de devis.php, +577px de page, soit un quart de hauteur en plus.
   `height` fixe (et non `min-height`) est ce qui coupe la chaîne : la boîte ne
   dépend plus du glyphe. On retombe sur les 30px que ces liens faisaient déjà.
   Tout le reste — couleurs, arrondi, filet, familles — demeure commun ; seuls
   3px séparent une action de tableau d'un bouton de cadre, et c'est voulu. */
.rowactions button, .rowactions .btn, .rowactions .linklike {
  height: 30px; min-height: 0; padding: 0 6px;
}

.danger-zone { margin-top: 24px; }

/* Grille de formulaire */
.grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px 14px; }
.fld.w2 { grid-column: span 2; } .fld.w4 { grid-column: span 4; }
.fld.chk { align-self: end; }
.filters { display: flex; gap: 8px; flex-wrap: nowrap; align-items: center; background: var(--carte); border: 1px solid var(--ligne); border-radius: 10px; padding: 10px; }
.filters input, .filters select { width: auto; min-width: 0; flex: 0 1 auto; }
.filters select { font-size: 13px; padding: 7px 6px; text-overflow: ellipsis; }
.filters input[type="search"] { flex: 1 1 140px; min-width: 100px; }
.filters button, .filters .btn { flex: 0 0 auto; }
@media (max-width: 1100px) {
  .filters { flex-wrap: wrap; }
  .filters select { font-size: 14px; padding: 8px 10px; }
}
.inline { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin: 8px 0; }
.inline label { margin: 0; }
.inline label.opt { display: inline-flex; align-items: center; gap: 4px; }

/* Barre d'actions en tête de liste (Contacts, À trier).
   Les actions en lot ne concernent QUE des lignes cochées : tant que rien ne
   l'est, elles s'effacent et il ne reste que « + Nouveau contact ».
   Bascule en CSS pur — aucun JS.
   REPLI VOLONTAIRE : si le navigateur ignore :has(), les deux règles ci-dessous
   sont sans effet et la barre reste affichée en permanence, c'est-à-dire le
   comportement d'avant. Une fonction n'est donc jamais rendue inaccessible. */
.bulkbar { display: flex; gap: 8px 16px; align-items: flex-start; flex-wrap: wrap; }
/* Les actions occupent la place disponible : « + Nouveau contact » reste ainsi
   sur la première ligne, à droite, au lieu d'être renvoyé dessous par le wrap. */
.bulkbar .bulkacts { flex: 1 1 320px; min-width: 0; display: flex; flex-direction: column; }
/* Une ligne par intention, séparées par un filet — même grammaire que .actgrp. */
.bulkbar .bulkrow { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; padding: 7px 0; }
.bulkbar .bulkrow + .bulkrow { border-top: 1px solid var(--ligne); }
.bulkbar .bulkrow:first-child { padding-top: 0; }
.bulkbar .bulkrow:last-child { padding-bottom: 0; }
.bulkbar .bulklab { min-width: 74px; font-weight: 700; color: var(--bleu); font-size: 13px; }
.bulkbar label.opt { display: inline-flex; align-items: center; gap: 4px; }
body:has(.rowck) .bulkbar .bulkacts { display: none; }
body:has(.rowck:checked) .bulkbar .bulkacts { display: flex; }
.quick { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
.quick form { display: inline; }

/* Timeline du journal (§4.3, 25/07) : ligne verticale + point par entrée. */
.timeline { position: relative; margin: 12px 0 0 6px; padding-left: 22px; border-left: 2px solid var(--ligne); }
.tl-item { position: relative; padding-bottom: 16px; }
.tl-item:last-child { padding-bottom: 2px; }
.tl-item::before { content: ''; position: absolute; left: -28px; top: 5px; width: 11px; height: 11px;
  border-radius: 50%; background: var(--bleu2); border: 2px solid var(--carte); }
.tl-head { padding: 2px 0; }
.tl-date { color: var(--bleu); font-weight: 700; white-space: nowrap; }
.tl-actions { margin-top: 4px; }
.tl-detail { margin: 6px 0 0; white-space: pre-wrap; color: var(--texte); }
.tl-more summary { list-style: none; }
.tl-more summary::-webkit-details-marker { display: none; }
.tl-more[open] summary { margin-bottom: 10px; }

/* Actions de fiche, groupées par intention */
.actgrp { display: flex; gap: 8px; flex-wrap: wrap; align-items: center;
  padding: 11px 0; border-top: 1px solid var(--ligne); }
.actgrp:first-of-type { border-top: 0; padding-top: 4px; }
.acthead { display: flex; align-items: center; gap: 9px; min-width: 160px; }
.acticon { width: 28px; height: 28px; border-radius: 50%; display: inline-flex;
  align-items: center; justify-content: center; font-size: 14px; flex-shrink: 0; }
.acticon.ic-out { background: #e6f1fb; color: #0c447c; }
.acticon.ic-in  { background: #eaf3de; color: #27500a; }
.acticon.ic-org { background: #faeeda; color: #633806; }
.acticon.ic-cls { background: #f1efe8; color: #444441; }
.acticon.ic-qual { background: #dff0d8; color: #2c5e0d; }
/* Frise d'états Piste/Option/Confirmé (Qualifier) : chips <details>, sans JS */
.chiprow { display: flex; gap: 8px; flex-wrap: wrap; margin-left: auto; }
.chip { border: 1px solid var(--ligne); border-radius: 8px; background: #f1efe8; color: #444441; max-width: 100%; }
.chip summary { list-style: none; cursor: pointer; padding: 8px 14px; font-size: 13px; user-select: none; white-space: nowrap; }
.chip summary::-webkit-details-marker { display: none; }
.chip summary::after { content: ' ▾'; opacity: .6; }
.chip[open] summary::after { content: ' ▴'; }
.chip.chip-on { background: #dff0d8; color: #2c5e0d; border-color: #b9dba2; font-weight: 700; }
.chipbody { padding: 2px 14px 12px; border-top: 1px solid rgba(0,0,0,.08); white-space: normal; }
.chipbody .inline { margin: 8px 0 0; }
.chip-optlist { font-size: 12px; color: var(--muted); margin: 6px 0; line-height: 1.6; }
.actlbl { font-size: 13px; color: var(--texte); }
/* La ligne d'action se replie quand il n'y a PLUS la place (01/08). En `nowrap`
   avec `min-width: 260px` en dur, elle ne pouvait ni se couper ni se comprimer :
   dans la popup `dlg-actions` de contact.php à 375 px, qui n'offre que 325 px de
   contenu, le `span.actbtns` du bouton « ⏰ Programmer la relance » sortait à
   475 px alors que la popup s'arrête à 351 — mesuré 450/325, seul défilement
   latéral de cette popup (audit 043, §6). `wrap` ne change RIEN aux largeurs
   confortables : tant que les 260 px tiennent, la rangée reste sur une seule
   ligne et `.actbtns` garde son `margin-left: auto` à droite. `min()` remplace
   le plancher en dur pour que le plancher lui-même ne puisse plus déborder. */
.actline { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; flex: 1; margin: 0; min-width: min(260px, 100%); }
.actline input[type=text] { flex: 1; min-width: min(130px, 100%); }
.actbtns { display: flex; gap: 8px; margin-left: auto; align-items: center; flex-wrap: wrap; }

/* §4.1bis : liste des colonnes de Contacts, réordonnable par glissé-déposé */
#colDragList { display: flex; flex-direction: column; max-width: 340px; gap: 2px; margin-top: 6px; }
.coldrag { display: flex; align-items: center; gap: 8px; padding: 5px 8px; border-radius: 6px; border: 1px solid transparent; }
.coldrag:hover { background: var(--fond); border-color: var(--ligne); }
.coldrag.dragging { opacity: .4; }
.coldrag-poignee { cursor: grab; color: var(--muted); user-select: none; }
.coldrag .opt { margin: 0; }
/* Boutons du bandeau de suivi (contact.php:1032-1109).
   Doublons mesurés le 30/07 : .act-in a EXACTEMENT le fond de .btn.add, et
   .act-close celui de .btn.light — deux noms pour un même dessin, c'est ainsi
   qu'on arrive à 20 recettes. Ils restent ici en ALIAS le temps que les pages
   passent aux vraies classes, puis se suppriment.
   Attention au moment de reclasser : .btn.add porte un `margin-left: auto`
   (hérité de son emplacement en bout de .bulkbar) que .act-in n'a pas —
   remplacer .act-in par « btn add » pousserait le bouton à droite de sa
   rangée. Écrire simplement class="btn add" puis vérifier la rangée.
   .act-out SURVIT : son bleu moyen est la seule chose qui distingue un geste
   SORTANT (« ✉ Email envoyé ce jour », « 📞 Appel passé ») du bleu foncé
   « Enregistrer » et du vert « ✓ Réponse reçue ». Trois natures dans la même
   rangée, trois teintes — c'est le seul endroit de l'app où c'est justifié. */
button.act-out, .act-out { background: var(--bleu2); color: #fff; }
button.act-out:hover, .act-out:hover { background: var(--bleu); }
button.act-in, .act-in { background: var(--ok); color: #fff; }              /* ≡ .btn.add */
button.act-in:hover, .act-in:hover { background: var(--ok-h); }
button.act-close, .act-close { background: var(--clair); color: var(--bleu); } /* ≡ .btn.light */
button.act-close:hover, .act-close:hover { background: var(--clair-h); }
.actgrp form.inline { margin: 0; }

/* Onglets de la fiche contact */
.ftabs { display: flex; gap: 2px; border-bottom: 2px solid var(--ligne); margin: 4px 0 16px; flex-wrap: wrap; justify-content: center; }
.ft { padding: 10px 14px; font-size: 14px; color: var(--muted); border-bottom: 2px solid transparent; margin-bottom: -2px; text-decoration: none; }
.ft:hover { color: var(--bleu2); text-decoration: none; }
.ft.on { color: var(--bleu); border-bottom-color: var(--bleu); font-weight: 700; }
@media (max-width: 720px) { .ft { padding: 9px 10px; font-size: 13px; } }
/* Onglets Année de pipeline.php (28/07, Morgan : « largeurs identiques ») —
   `width` fixe (pas `min-width` : un simple plancher n'empêche pas l'onglet
   actif, en gras, d'être plus large que les autres — bug persistant signalé
   par Morgan) + `flex-shrink:0` pour qu'il ne se réduise pas non plus dans la
   ligne à défilement horizontal. */
.ft-annee { display: inline-block; width: 40px; flex-shrink: 0; text-align: center; font-variant-numeric: tabular-nums; }
@media (max-width: 720px) { .ft-annee { width: 32px; } }
/* Repli <select> à l'intérieur d'une barre .ftabs (28/07, « Plus d'années »
   sur pipeline.php) : mêmes dimensions/couleurs qu'un .ft, chrome natif du
   select neutralisé pour qu'il se fonde dans la barre au lieu de trancher. */
select.ft { appearance: none; -webkit-appearance: none; -moz-appearance: none; background: transparent; border: none; border-bottom: 2px solid transparent; font: inherit; cursor: pointer; }
select.ft.on { border-bottom-color: var(--bleu); }

a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: 3px solid #85b7eb; outline-offset: 2px;
}
input[type="checkbox"], input[type="radio"] { width: 20px; height: 20px; accent-color: var(--bleu); }

button.busy { background: var(--attente) !important; cursor: wait; }
button.busy::after { content: ''; display: inline-block; width: 12px; height: 12px; margin-left: 8px;
  border: 2px solid rgba(255,255,255,.4); border-top-color: #fff; border-radius: 50%;
  vertical-align: -2px; animation: tourne .8s linear infinite; }
@keyframes tourne { to { transform: rotate(360deg); } }

.pagin { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; margin: 10px 0; }
.pagin .btn { padding: 6px 12px; }
/* La page COURANTE n'est pas cliquable — elle ne doit donc pas ressembler à
   « Enregistrer ». Avant le 30/07 elle en était la copie au pixel près (même
   bleu plein, même .btn), au point qu'on cliquait dessus pour valider.
   Un repère, pas une action : pas de fond, chiffre en gras, filet discret.
   Sélecteur `span.btn` — c'est la forme émise par inc/layout.php:805, qui n'a
   pas besoin d'être modifiée. */
.pagin span.btn { background: none; color: var(--bleu); font-weight: 700;
                  border-color: var(--ligne); cursor: default; }
th a { text-decoration: none; }
th a:hover { text-decoration: underline; }

footer { text-align: center; color: var(--muted); font-size: 12px; padding: 24px 0 16px; }

.burger { display: none; background: none; border: 0; color: #fff; font-size: 24px; padding: 2px 10px; cursor: pointer; margin-left: auto; }
.navwho { display: none; }

@media (max-width: 720px) {
  .grid { grid-template-columns: 1fr 1fr; }
  .fld.w2, .fld.w4 { grid-column: span 2; }
  main { padding: 10px; }
  .topbar { gap: 8px; }
  .navbox { position: static; width: auto; max-width: none; padding: 0; transform: none; }
  .burger { display: block; }
  .topbar .who { display: none; }
  .topbar nav { display: none; flex-basis: 100%; flex-direction: column; gap: 2px; padding: 6px 0 4px; border-top: 1px solid rgba(255,255,255,.2); }
  .topbar nav.open { display: flex; }
  .navgrp, .navgrp.navcenter, .navgrp.navleft, .navgrp.navright { position: static; top: auto; left: auto; transform: none; margin-left: 0; flex: none; width: 100%; flex-direction: column; justify-content: flex-start; }
  .ftabs-with-btn { flex-direction: column; align-items: stretch; }
  .ftabs-with-btn .btn.add { position: static; transform: none; margin-bottom: 8px; }
  .topbar nav a { padding: 12px 10px; font-size: 16px; border-radius: 8px; }
  /* Sous-menus en cases (25/07) : sur téléphone, un menu déroulant remplace la
     rangée de pilules — évite d'empiler plusieurs lignes de cases sur un
     petit écran (demande de Morgan). font-size 16px : évite le zoom auto
     iOS au focus d'un <select>. */
  .subtiles { display: none; }
  .subtiles-mobile { display: block; width: 100%; margin: 8px 0 16px; padding: 10px; font-size: 16px; border-radius: 8px; border: 1px solid var(--ligne); }
  .navwho { display: block; opacity: .8; border-top: 1px solid rgba(255,255,255,.2); margin-top: 4px; }
  /* 44px : la cible tactile minimale. Vaut pour les cinq familles — un lien
     d'action de tableau se rate autant qu'un bouton s'il fait 20px de haut. */
  button, .btn { padding: 12px 16px; min-height: 44px; }
  .linklike { min-height: 44px; padding: 10px 8px; }
  input, select, textarea { font-size: 16px; }
  .quick form button { width: 100%; }
  .quick { flex-direction: column; }
  /* Sur mobile, « + Nouveau contact » repasse en pleine largeur sous les actions. */
  .bulkbar { flex-direction: column; align-items: stretch; }
  .bulkbar .btn.add { margin-left: 0; text-align: center; }
  .bulkbar .bulklab { min-width: 100%; }
  .tiles { gap: 8px; }
  .tile { flex: 1 1 40%; min-width: 0; }
  .filters { flex-direction: column; align-items: stretch; }
  .filters input, .filters select, .filters .btn, .filters button { width: 100%; }
}

/* Journal replié dans le volet gauche (V3, 29/07 — Morgan : « à gauche Contact
   et journal, dépliable »). C'est une archive : on la consulte, on n'y
   travaille pas. Sur une fiche ancienne elle fait des centaines de lignes, qui
   repousseraient les contacts hors de l'écran si elle s'ouvrait d'office. */
.journal-repliable > summary { cursor: pointer; color: var(--bleu2); font-size: 16px; padding: 4px 0; }
.journal-repliable[open] > summary { margin-bottom: 8px; }
/* Pas de hauteur plafonnée (29/07, Morgan : « le journal est coupé, dommage »).
   Le cadre à 60vh tronquait les entrées longues — un compte rendu de journée
   type, avec ses horaires et ses déplacements, ne tenait pas. Un journal se lit
   d'un bout à l'autre ; qu'il allonge la colonne est le moindre mal, et c'est
   justement pour ça qu'il est replié par défaut. */

/* Bandeau de tête d'une fiche (V3, 29/07 — Morgan : « hors modules… dans le
   même espace horizontal que le nom de la fiche et retour, mais à droite »).
   La visibilité n'est pas un champ du formulaire Infos : c'est un réglage de
   la fiche ENTIÈRE, qui doit se lire et se changer sans ouvrir un onglet.
   Elle remonte donc sur la ligne du retour, poussée à droite. */
.fiche-tete { display: flex; align-items: center; justify-content: space-between;
              gap: 16px; flex-wrap: wrap; margin: 4px 0 10px; }
.fiche-visibilite { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.fiche-visibilite label { display: inline-flex; align-items: center; gap: 5px; font-size: 14px; }
@media (max-width: 720px) { .fiche-tete { align-items: flex-start; flex-direction: column; gap: 6px; } }

/* Plus de défilement latéral de la PAGE (29/07, Morgan : « une solution valable
   pour toutes les fiches pour ne pas devoir scroller latéralement »).
   Le tableau des contacts porte six colonnes — Rôle, Nom, Prénom, Fonction,
   Email, Tél — dans un volet qui fait 38 % de l'écran. Il imposait sa largeur
   naturelle au volet, qui la répercutait sur la page entière : on scrollait
   horizontalement pour lire une fiche.
   Deux garde-fous, il faut les DEUX :
   ① le tableau défile dans son propre cadre, jamais la page ;
   ② table-layout:fixed, sinon le navigateur ignore les largeurs demandées et
      reprend la largeur du plus long contenu — une adresse mail sans espace
      suffit à tout faire déborder. */
.fiche .cell .tablewrap { overflow-x: auto; max-width: 100%; }

/* Vignettes de contacts (29/07) — remplacent le tableau dans le volet gauche.
   Resserrer sept colonnes dans 42 % ne pouvait pas marcher : « Fonction »
   s'affichait à trois lettres par ligne. Une vignette n'impose aucune largeur,
   se lit de haut en bas, et gagne même en clarté sur grand écran. */
/* Grille, pas une colonne (29/07, Morgan : « maintenant c'est petit et on a
   trop d'espace libre »). Empilées une par une, trois vignettes laissaient les
   deux tiers de la largeur en blanc dès que le volet dépassait 600 px. En
   grille auto-remplie, elles se mettent côte à côte quand il y a la place et
   repassent en colonne quand il n'y en a plus — sans point de rupture à
   deviner ni media query à maintenir.
   280 px est le seuil sous lequel une adresse mail commence à se couper. */
.pcards { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 10px; align-items: start; }
.pcard { border: 1px solid var(--ligne); border-radius: 8px; padding: 12px 14px; background: #fff; }
.pcard.muted { opacity: .65; }
.pcard-tags { margin-bottom: 5px; }
/* Tailles remontées d'un cran : le nom et les coordonnées sont ce qu'on vient
   lire, ils n'ont pas à être plus petits que le reste de la page. */
.pcard-nom { font-size: 17px; line-height: 1.3; }
.pcard-fonction { font-size: 14px; color: var(--muted); margin-top: 3px; }
.pcard-coord { font-size: 15px; margin-top: 6px; overflow-wrap: anywhere; }
.pcard-meta { font-size: 12px; color: var(--muted); margin-top: 6px; }
/* Les actions ne prennent la parole qu'au survol de la vignette : elles sont
   six et noieraient l'information si elles étaient toujours à pleine encre. */
.pcard-actions { margin-top: 8px; max-width: none; opacity: .55; transition: opacity .15s; }
.pcard:hover .pcard-actions, .pcard:focus-within .pcard-actions { opacity: 1; }

/* Dialogue LARGE (29/07, Morgan : « pour éditer un mail la popup doit être
   clairement plus grande »). 420 px suffisent pour deux champs et un bouton ;
   corriger le corps d'un mail archivé, non — on relisait le texte par la
   fenêtre d'un timbre-poste. */
dialog.large { max-width: 1080px; width: calc(100% - 48px); }
/* Standard des tailles de popup (31/07, Morgan : « standardiser les tailles ») :
   défaut = petite (420px, confirmations et 1-2 champs) · .moy = moyenne (720px,
   formulaires courants) · .large = grande (1080px, widgets riches : mail, prix,
   feuille de route, export). Les largeurs artisanales inline sont à résorber. */
dialog.moy { max-width: 720px; width: calc(100% - 40px); }
/* Édition de date en popup (30/07) : trois onglets + le widget Prix indicatif
   — 980px l'écrasaient (« gros problème d'affichage », curseur et verdicts en
   colonne). Même famille que .large, juste plus d'air. */
#dlg-edit-date { max-width: 1200px; width: calc(100% - 32px); }
/* L'éditeur en iframe sur la fiche : bords nets et fond assombri — les marges
   translucides laissaient « voir l'app » comme un défaut (30/07). */
#dlg-editdate-frame { border: 0; border-radius: 12px; box-shadow: 0 12px 48px rgba(15, 23, 42, .35); }
#dlg-editdate-frame::backdrop, #dlg-edit-date::backdrop { background: rgba(15, 23, 42, .55); }

/* Poignée de redimensionnement bridée à la VERTICALE (29/07, Morgan :
   « attention à ce que les poignées du champ ne puissent pas dépasser la
   largeur de ce dans quoi elles sont contenues »). Par défaut un <textarea>
   se redimensionne dans les deux sens : en l'étirant, on le faisait sortir de
   sa carte ou de son dialogue, et la page se mettait à défiler latéralement.
   La hauteur reste libre, c'est le seul réglage utile pour du texte. */
textarea { max-width: 100%; resize: vertical; }

/* Bouton unique qui remplace les cinq rangées d'actions (29/07). Il doit se
   voir sans crier : c'est le geste principal du volet, mais il ne s'utilise
   que quelques secondes par visite. */
.btn-actions { font-family: inherit; font-size: 15px; padding: 9px 16px; border-radius: 8px;
               border: 1px solid var(--bleu); background: var(--bleu); color: #fff; cursor: pointer; }
.btn-actions:hover { background: var(--bleu2); border-color: var(--bleu2); }

/* Calculateur de prix indicatif (29/07) — outil de téléphone : trois réglages
   sur une ligne, les quatre paliers en dessous, le résultat en gros. */
.calc { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 10px; align-items: flex-end; }
.calc-l { display: flex; flex-direction: column; gap: 3px; }
.calc-l label { font-size: 13px; color: var(--muted); }
.calc-l input, .calc-l select { width: 150px; }
/* La ligne de totaux revient à la ligne quand il le faut, TOUJOURS — mesuré :
   en `nowrap` elle occupe 614 px dans une colonne de 534 px à 1 024 px de
   fenêtre, et le débordement remonte jusqu'à <main>, donnant une barre de
   défilement horizontale à toute la page.
   Le repli était accroché à `@media (max-width: 900px)`, c'est-à-dire à la
   largeur de la FENÊTRE, alors que le problème dépend de la largeur de la
   COLONNE : le widget vit dans les 58 % de droite de la fiche, il est donc à
   l'étroit bien avant que la fenêtre ne le soit. Un repli inconditionnel n'a
   aucun inconvénient — sur un large écran la ligne tient de toute façon.
   Chaque total, lui, reste insécable : couper « 1 240 € » en deux serait pire
   que tout. */
.calc-out { display: flex; flex-wrap: wrap; column-gap: 18px; row-gap: 6px; margin-top: 12px;
            font-size: 15px; align-items: baseline; }
.calc-out > span { min-width: 0; white-space: nowrap; }
.calc-out b { font-size: 20px; color: var(--bleu); margin-left: 6px; }
.calc-out .calc-ttc b { color: var(--orange); font-size: 24px; }

/* ---- Bouton central du suivi (29/07) ----
   Morgan : « tout en haut de l'onglet Suivi, pleine largeur du module,
   couleur plus attrayante que le bleu neutre (voulu mais pas adapté ici) ».
   Le bleu de l'interface est celui des menus et des cadres : un bouton d'action
   qui le porte se fond dedans. L'orange est déjà la couleur d'accent de l'app
   (Total TTC, « + Nouveau ») — elle dit « c'est ici qu'on agit ». */
.btn-suivi { display: block; width: 100%; font-family: inherit; font-size: 16px; font-weight: 600;
             padding: 12px 18px; border-radius: 9px; border: 0; cursor: pointer;
             background: var(--orange); color: #fff; margin-top: 6px; }
.btn-suivi:hover { filter: brightness(1.07); }

/* ---- Mur de notes en post-it (29/07) ----
   Elles remplissent l'espace laissé libre par les actions parties en popup.
   Grille auto-remplie, comme les vignettes de contacts : côte à côte quand il
   y a la place, en colonne sinon. */
/* ---- MUR DE NOTES DÉPLAÇABLES (29/07) ----
   Morgan : « on les tient avec un clic et elles glissent librement dans la
   zone. Double clic dans la zone pour créer ». Ce n'est plus une grille mais un
   tableau de liège : chaque note porte sa position, le fond accueille le
   double-clic.
   Le mur ne pousse pas la première rangée de la fiche — base de zéro, comme le
   journal — donc le journal démarre toujours à la même hauteur. */
.mur { position: relative; margin-top: 14px; flex: 1 1 0; min-height: 300px;
       overflow: auto; border: 1px dashed transparent; border-radius: 8px; }
.mur:hover { border-color: var(--ligne); }
.mur .postit { position: absolute; width: 220px; cursor: grab; user-select: none; }
.mur .postit.saisi { cursor: grabbing; box-shadow: 0 6px 18px rgba(0,0,0,.18); z-index: 5;
                     transform: rotate(-1deg); }
/* Les actions et le texte redeviennent sélectionnables : on veut pouvoir
   recopier une note, et cliquer sur l'épingle sans déclencher un déplacement. */
.mur .postit-txt, .mur .postit-act { user-select: text; }
.mur .postit.neuf { width: 230px; cursor: default; }
.postit { background: #fffdf2; border: 1px solid #efe6c8; border-radius: 8px; padding: 10px 12px;
          box-shadow: 0 1px 2px rgba(0,0,0,.05); display: flex; flex-direction: column; gap: 8px; }
/* L'épinglée se distingue par un liseré, pas par une couleur criarde : il peut
   y en avoir plusieurs, et un mur bariolé ne se lit plus. */
.postit.pin { border-color: var(--orange); box-shadow: 0 1px 3px rgba(201,114,11,.22); }
.postit-txt { font-size: 14px; line-height: 1.45; overflow-wrap: anywhere; }
.postit-bas { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 12px; }
.postit-act { opacity: .45; transition: opacity .15s; white-space: nowrap; }
.postit:hover .postit-act { opacity: 1; }
.postit.neuf { background: #fff; border-style: dashed; }
.postit.neuf textarea { width: 100%; font-size: 14px; border: 0; background: none; resize: vertical; }
.postit.neuf textarea:focus { outline: none; }

/* ---- Rangée du bas à hauteur d'écran (29/07) ----
   Morgan : « dans la partie journal-mail la hauteur doit être la taille
   disponible dans le navigateur sous le bandeau des menus ».
   Journal et mail défilent donc CHACUN dans son cadre, au lieu d'allonger la
   page : on garde les deux sous les yeux, et le haut de la fiche ne part plus
   hors de l'écran dès qu'un journal fait trente entrées.
   56px = la barre de menus, collée en haut. */
/* ---- Hauteur du journal (29/07) ----
   Deux tentatives ont échoué avant celle-ci, et il faut dire pourquoi.
   ① Plafonner CHAQUE cellule à la hauteur de l'écran : trois zones se
      mettaient à défiler séparément (« j'ai des scroll de partout »).
   ② Donner au journal `flex: 1 1 0` pour qu'il se plie à la hauteur décidée
      par le module de droite : élégant sur le papier, mais la hauteur d'une
      cellule de grille se calcule à partir de son contenu, et un contenu de
      base zéro donne une cellule vide — le journal disparaissait, puis se
      réduisait à son seul titre.

   Cette version renonce à faire dépendre le journal de son voisin et lui donne
   une hauteur PROPRE. On perd l'alignement parfait du bas avec l'envoi de mail ;
   on gagne un affichage qui ne peut plus s'effondrer, et un seul ascenseur.

   overflow-x: hidden a été RETIRÉ (29/07, Morgan : « le journal est beaucoup
   trop bas sur date et info », redemandé trois fois). Il coupait bien les URL
   d'erreur SMTP, mais au prix d'un effet de bord invisible à la lecture :
   quand un seul axe vaut `hidden`, l'autre — resté `visible` — est CALCULÉ en
   `auto` (CSS Overflow 3 § 3.2). Le <details> devenait donc son propre
   conteneur de défilement, et le `position: sticky` de son titre se calait sur
   LUI au lieu de la fenêtre : les 37 px de `top` s'appliquaient en permanence,
   même page non défilée. D'où le vide sous la carte Contacts — mesuré à 37 px
   sur Dates et Infos, absent sur Suivi où la règle de `.cell.bas` impose
   `top: 0`. Et, du même coup, le titre ne collait plus jamais sous la barre
   bleue au défilement : son scrollport, c'était lui-même, qui ne défile pas.
   Le défilement latéral est tenu autrement, et mieux : `overflow-wrap:
   anywhere` sur TOUT le journal. Contrairement à `break-word`, il réduit aussi
   la largeur MINIMALE du contenu — une URL d'un seul tenant ne peut donc plus
   pousser la colonne, il n'y a plus rien à couper. Vérifié à 420, 760, 1280 et
   1920 px sur la fiche au journal le plus fourni : scrollWidth == clientWidth. */
/* Le journal REMPLIT la hauteur décidée par le module de droite.
   Base de zéro : il ne compte pas dans la hauteur naturelle de sa colonne,
   donc c'est bien le voisin qui fixe la rangée ; il s'étire ensuite pour
   occuper ce qui reste, et lui seul défile.
   Cette règle avait échoué une première fois — non par elle-même, mais parce
   que `.fiche` portait encore `align-items: start`, qui empêchait la cellule de
   s'étirer : contenu de base zéro, cellule de hauteur zéro. J'ai reculé sur un
   max-height fixe au lieu de constater que la cause était corrigée entre-temps.
   Le plafond arbitraire à 62 vh laissait un grand vide sous un module long. */
/* Le journal se DÉROULE en entier (29/07, Morgan, redemandé sans relâche) :
   il n'est plus une boîte à hauteur fixe qui coupe la dernière entrée au
   milieu d'une phrase. Il pousse la page, et c'est le bloc d'envoi de mail qui
   glisse pour suivre le défilement — d'où le `position: sticky` en face. La
   règle « le journal remplit ce qui reste en défilant tout seul » est donc
   caduque : elle produisait exactement le rognage qu'il refuse. */
.fiche .journal-repliable[open] { flex: 0 0 auto; min-height: 0; }
.journal-repliable { min-width: 0; overflow-wrap: anywhere; }
.journal-repliable .tl-item, .journal-repliable .tl-head { min-width: 0; }
/* Titres de la rangée du bas : collants, alignés, et opaques (29/07, Morgan :
   « remonter journal et mail », « des bouts de journal restent visibles et je
   ne trouve pas ça joli », « bien aligner journal et mail »).

   Les trois défauts avaient la même cause : la cellule qui défile gardait une
   marge en haut. Le contenu passait donc DANS cet interstice, au-dessus d'un
   titre collant qui commençait un cran plus bas — d'où les « modifier ✕
   supprimer » qui dépassaient. La marge est supprimée et le titre remonte à
   ras, avec un fond plein et un filet qui marque la séparation.

   Les deux colonnes reçoivent exactement le même traitement : c'est ce qui
   met « Journal » et « Envoyer un email » sur la même ligne, au pixel près —
   ils étaient décalés de la seule différence entre leurs marges d'origine. */
.fiche > .cell.bas { padding-top: 0; }
.fiche > .cell.bas > details.card,
.fiche > .cell.bas > .card { margin-top: 0; padding-top: 0; border-top: 0; }
.fiche > .cell.bas > details.card > summary { position: sticky; top: 0; z-index: 2;
    margin: 0 -20px; padding: 12px 20px 10px; font-size: 16px; color: var(--bleu2);
    box-shadow: inset 0 -1px 0 var(--ligne); cursor: pointer; }
/* Même taille et même couleur des deux côtés : sans ça les deux titres, bien
   qu'alignés en haut, n'ont pas la même ligne de base et l'œil voit un décalage
   qui n'existe pas dans la mise en page. */
.fiche > .cell.gauche.bas > details.card > summary { background: #fcfdfe; }
.fiche > .cell.droite.bas > details.card > summary { background: var(--carte); }

/* Le titre du journal se tient PAREIL sur les trois onglets (29/07, Morgan :
   « dans le suivi il y a un liseret sous journal, cool — mais plus sur Dates »).
   Les règles ci-dessus sont accrochées à `.cell.bas`, qui n'existe que sur
   l'onglet Suivi : sur Dates et Infos le journal remonte dans la colonne de
   gauche et perdait donc son filet. La règle suit maintenant le journal, pas
   sa position dans la grille.
   Le collant se cale sous la barre de navigation (37 px, mesuré) et non à 0 :
   depuis que le journal déroule et pousse la page, c'est la FENÊTRE qui défile,
   et un titre collé à 0 passerait sous la barre bleue. */
.fiche details.journal-repliable > summary { position: sticky; top: 37px; z-index: 2;
    margin: 0 -20px; padding: 12px 20px 10px; font-size: 16px; color: var(--bleu2);
    background: #fcfdfe; box-shadow: inset 0 -1px 0 var(--ligne); cursor: pointer; }
/* Et il démarre à la MÊME hauteur sur les trois onglets (29/07, Morgan : « le
   journal est beaucoup trop bas sur date et info »).
   Le titre du journal porte déjà ses 12 px de respiration dans sa barre
   collante. Les 14 px de `padding-top` que `.fiche .cell .card` donne à toute
   carte s'y ajoutaient donc en double sur Dates et Infos — là où la rangée du
   bas les remet à zéro sur Suivi. Ce sont ces 14 px, plus les 37 px du collant
   détourné, qui séparaient les deux mises en page.
   Ce qui marque la coupure d'avec les contacts, ce n'est PAS le filet de
   `.fiche .cell .card` — `:first-of-type` compte par type d'élément, et le
   journal est le premier <details> de sa colonne, donc il perd son bord haut
   (vérifié : borderTopWidth = 0). C'est le filet sous le titre, porté par le
   box-shadow de la barre collante, qui joue ce rôle. */
.fiche .cell > details.journal-repliable { padding-top: 0; }
/* Le collant du journal se cale sous la barre bleue sur les trois onglets.
   La règle générique de la rangée du bas le colle à 0 : tant que le journal
   était son propre conteneur de défilement, ce 0 ne s'appliquait jamais et
   personne ne l'a vu. Maintenant que le collant fonctionne pour de bon, un
   titre à 0 passerait derrière la barre de navigation sur Suivi. */
.fiche > .cell.bas > details.journal-repliable > summary { top: 37px; }
/* Sous 720 px la barre bleue passe en mode téléphone : le menu se replie
   derrière le bouton ☰ et elle mesure 64 px, pas 37 (mesuré aux deux tailles).
   Un titre collé à 37 px y disparaîtrait derrière elle. Le défaut n'existait
   pas avant : le collant du journal ne s'est jamais déclenché tant que le
   journal était son propre conteneur de défilement. */
@media (max-width: 720px) {
  .fiche details.journal-repliable > summary,
  .fiche > .cell.bas > details.journal-repliable > summary { top: 64px; }
}

/* Le nom de la marche, à gauche du verdict — c'est lui qu'on cherche des yeux
   (« où j'en suis sur normal/max ? »), le chiffre ne vient qu'après. */
.verdict-marche { flex: 0 0 auto; font-weight: 600; min-width: 148px; }

/* Une tuile RESTE une tuile même quand c'est un <button> (29/07, Morgan :
   « problème de couleurs »). La règle générale `button:hover` a une
   spécificité supérieure à `.tile` : au survol — et après un clic, tant que le
   bouton garde le focus — le fond passait en bleu foncé pendant que le chiffre
   et la légende gardaient leur bleu et leur gris. Texte bleu sur fond bleu.
   Les états sont donc redéfinis explicitement pour les tuiles cliquables. */
button.tile { background: var(--carte); color: var(--texte); }
button.tile:hover, button.tile:focus { background: #eef3fa; border-color: var(--bleu2); }
button.tile.ok { background: var(--carte); }
button.tile.warn, button.tile.warn:hover { background: var(--warn-bg); }
button.tile.orange { background: var(--warn-bg); }
button.tile.orange:hover, button.tile.orange:focus { background: #ffeecd; border-color: var(--orange); }
button.tile.on { background: #eaf0f8; }

/* Sections d'un cadre groupé (29/07, Morgan : « Événements / Devis / Proposer
   pourraient être dans un même cadre ? »). Trois gestes d'entretien de la fiche
   occupaient trois cadres pleine largeur, donc autant de poids visuel que les
   dates elles-mêmes. Un seul cadre, des filets pour séparer. */
.sect { border-top: 1px solid var(--ligne); padding: 12px 0 4px; }
.sect:first-of-type { border-top: 0; padding-top: 6px; }
.sect > summary { cursor: pointer; }
.sect .card { border: 0; padding: 0; margin: 0; background: none; }
/* Les numéros s'empilent dans le dialogue, un par ligne. */
#dp-tels input { display: block; width: 100%; }

/* « + Ajouter un contact » descend au BAS de sa colonne (29/07, Morgan :
   « descendre ajouter contact »). Collé sous la dernière vignette, il laissait
   un grand vide en dessous de lui et se lisait comme une sixième vignette
   ratée. Poussé en bas, le vide passe AVANT lui : les contacts restent
   groupés en haut, l'action de création se range là où on la cherche quand on
   a fini de lire. */
/* La carte Contacts garde sa hauteur NATURELLE (29/07). Elle était extensible
   pour coller « + Ajouter un contact » en bas de la colonne — mais elle
   s'étirait alors différemment selon l'onglet (487 px sur Suivi, 756 sur Dates,
   493 sur Infos, mesurés), et le journal, qui la suit, bougeait d'autant.
   Entre un bouton joliment placé et un journal qui ne se déplace jamais, c'est
   le journal qui gagne : Morgan l'a demandé trois fois. */
.btn-ajout-contact { align-self: flex-start; margin-top: 4px; }

/* ---- Curseur de prix et repères de forfait (29/07) ----
   Morgan : « au stade du curseur on n'a pas fini le forfait. Mettre des
   repères forfaits. » Le curseur n'impose donc aucune hypothèse de salaire :
   il montre ce que le prix visé fait à chacune, ligne par ligne en dessous. */
/* Le rembourrage du bas porte toute la pile : zones à 90px + 52px de hauteur
   = 142px, moins les 72px occupés par le contrôle natif → 70px minimum. */
.curseur { position: relative; margin: 14px 0 0; padding: 44px 0 78px; }
/* La piste native devient TRANSPARENTE : c'est notre barre peinte, en dessous,
   qui porte l'information. On ne garde du contrôle natif que le pouce et le
   comportement clavier — qu'aucune réimplémentation ne vaut. */
.curseur input[type=range] { width: 100%; height: 28px; display: block; margin: 0;
                             -webkit-appearance: none; appearance: none;
                             background: transparent; position: relative; z-index: 2; }
.curseur input[type=range]::-webkit-slider-runnable-track { height: 10px; background: transparent; }
.curseur input[type=range]::-moz-range-track { height: 10px; background: transparent; }
.curseur input[type=range]::-webkit-slider-thumb { -webkit-appearance: none;
    width: 20px; height: 20px; border-radius: 50%; background: #4b4f5c;
    border: 2px solid #fff; box-shadow: 0 1px 4px rgba(0,0,0,.35); margin-top: -5px; cursor: grab; }
.curseur input[type=range]::-moz-range-thumb { width: 20px; height: 20px; border-radius: 50%;
    background: #4b4f5c; border: 2px solid #fff; box-shadow: 0 1px 4px rgba(0,0,0,.35); cursor: grab; }
/* La barre peinte, calée exactement sur l'axe du curseur (22 px de padding
   haut + 5 px pour centrer une barre de 10 px dans les 20 px du contrôle). */
/* left/right à 10 px = le demi-pouce. Le pouce d'un curseur natif ne parcourt
   pas toute la largeur : il est en retrait d'un demi-pouce à chaque bout. La
   barre peinte, les zones et les repères adoptent le même retrait, sinon tout
   se décale par rapport au pouce — jusqu'à 10 px au milieu, et ça se voit. */
.piste-fond { position: absolute; left: 10px; right: 10px; top: 53px; height: 10px;
              border-radius: 5px; z-index: 1;
              background: var(--ligne); }
/* Les traits verticaux traversent tout le bloc : ce sont des règles, pas des
   marqueurs ponctuels. On lit d'un coup la distance entre le prix visé, le
   point où tout est payé, et le tarif annoncé. */
/* Trait PLEIN et non pointillé (30/07, Morgan : « les pointillés ne sont pas
   visibles »). Sur une barre de couleur vive, un pointillé de 2 px se dissout —
   il ne reste que deux ou trois carrés isolés. Un trait plein blanc cerné
   d'ombre se lit sur n'importe quel fond, clair comme foncé, sans avoir à
   choisir sa couleur en fonction de la zone qu'il traverse. */
/* z-index 1 = DERRIÈRE le pouce (qui est à 2) mais devant la barre peinte.
   Le trait passait par-dessus le pouce et le barrait d'un trait blanc, ce qui
   se lisait comme un défaut d'affichage (30/07, Morgan). Dessous, le pouce se
   pose dessus et l'alignement se voit au lieu de se deviner. */
.guide { position: absolute; top: 26px; bottom: 4px; width: 3px; z-index: 1;
         margin-left: -1.5px;   /* le trait est CENTRÉ sur sa valeur, pas posé à sa droite */
         background: #fff; box-shadow: 0 0 0 1px rgba(0,0,0,.28);
         border-radius: 2px; pointer-events: none; }
/* L'étiquette, elle, reste au-dessus de tout : c'est le bouton qu'on clique. */
.guide-lab { z-index: 4; }
/* L'étiquette est un BOUTON : cliquer pose la cession exactement au seuil, ce
   qu'un glissement au pas de 25 € ne permet pas d'atteindre à coup sûr. Elle
   est donc le seul élément du repère à recevoir le clic — le trait, lui, reste
   transparent pour ne pas voler le clic destiné à la piste. */
.guide-lab { position: absolute; top: -22px; left: 50%; transform: translateX(-50%);
             font-size: 12px; font-weight: 700; white-space: nowrap;
             padding: 1px 7px; border-radius: 5px; min-height: 0; border: 1px solid;
             background: #fff; cursor: pointer; pointer-events: auto; }
.guide-eq .guide-lab { color: var(--orange); border-color: #f0dcae; }
.guide-eq .guide-lab:hover { background: var(--warn-bg); }
/* Près du bord droit, l'étiquette bascule à gauche du trait pour ne pas sortir
   de la carte. */
/* Près des bords, l'étiquette cesse d'être centrée pour ne pas sortir. */
.guide.a-gauche .guide-lab { left: auto; right: 0; transform: none; }
.guide.a-droite .guide-lab { left: 0; transform: none; }
.curseur-reperes { position: absolute; left: 0; right: 0; top: 22px; height: 40px; }
.repere { position: absolute; transform: translateX(-50%); background: none; border: 0;
          padding: 0; cursor: pointer; font-family: inherit; color: var(--muted); }
.repere-tick { display: block; width: 2px; height: 8px; background: var(--ligne); margin: 0 auto 2px; }
.repere-lab { display: block; font-size: 11px; line-height: 1.2; white-space: nowrap; }
.repere:hover .repere-lab { color: var(--bleu2); }
.repere:hover .repere-tick { background: var(--bleu2); }
/* Une ligne par hypothèse de salaire : la pastille donne la couleur, le texte
   dit combien et pourquoi. Lire quatre lignes vaut mieux qu'une grille de
   seize cases qu'on ne parcourt jamais entièrement. */
/* Grille à deux colonnes, PAS un positionnement absolu (29/07 : l'encart
   recouvrait la fin du verdict — « budgets fléchés entamés à 94 % » passait
   sous la boîte). Une boîte posée en absolu ne réserve aucune place : le texte
   passe dessous dès qu'il est long, et il l'est justement quand il y a
   quelque chose à lire. La grille donne à chacun sa colonne ; le recouvrement
   devient structurellement impossible. */
/* Hauteur constante de deux lignes, quel qu'en soit le nombre affiché (29/07,
   Morgan : « cela doit être fixe, or cela bouge encore quand les lignes du
   dessus passent de 1 à 2 »). Sans ça, tout ce qui suit — les totaux — sautait
   d'une trentaine de pixels chaque fois qu'on franchissait un seuil en glissant
   le curseur, et c'est le genre de secousse qui fait perdre sa lecture.

   Un `min-height` ne suffit pas : c'est un MINIMUM, il cède dès qu'une ligne
   revient à la ligne dans une colonne étroite. La hauteur est donc tenue par la
   structure — les deux emplacements existent toujours, celui qui ne sert pas
   est rendu invisible sans quitter le flux — et chaque ligne tient sur une
   seule ligne de texte, sans jamais déborder latéralement. */
/* 03/08, Morgan : « repartir horizontalement, "cachets normaux" "cachets max"
   ou "cachets mini" selon les circonstances. On va gagner en hauteur et
   supprimer des chiffres faux ». Empiles en colonne, chacun prenait une ligne
   pleine et la popup debordait en hauteur. En rangee ils tiennent cote a cote
   et se replient si la place manque — jamais de scroll lateral. */
.verdicts { margin-top: 10px; display: flex; flex-direction: row; flex-wrap: wrap;
            gap: 2px 18px; justify-content: flex-start; align-items: baseline; }
.verdict { display: flex; align-items: baseline; gap: 12px; font-size: 15px;
           flex: 1 1 auto; min-width: 260px; max-width: 100%;
           background: none; border: 0; padding: 6px 8px; border-radius: 7px; cursor: pointer;
           font-family: inherit; color: inherit; text-align: left; }
/* `visibility` et non `display` : la place reste prise. */
.verdict.vide { visibility: hidden; }
.verdict:hover { background: #f2f6fb; }
.verdict.vide:hover { background: none; }
/* Le verdict à gauche, l'hypothèse nommée en encart à droite (29/07, Morgan).
   Le montant des cachets a disparu de la ligne : il répétait une information
   des paramètres et repoussait le verdict sur une seconde ligne. Il reste
   accessible au survol, en infobulle. */
.verdict-txt { flex: 1 1 auto; }
/* L'encart unique se cale à DROITE de la zone des verdicts, là où étaient les
   quatre étiquettes fixes (29/07, Morgan : « tu places l'encart dans la zone de
   la troisième capture »). */

.verdict-pastille { flex: 0 0 11px; width: 11px; height: 11px; border-radius: 50%; background: var(--ligne); }
.verdict-txt { min-width: 0; overflow-wrap: anywhere; font-size: 15px; }

/* Le panneau d'envoi de mail SUIT le défilement (29/07, Morgan : « qu'elle
   suive le scroll et se retrouve à droite du journal peu importe où on est
   dans le journal »). Sur une fiche au journal fourni, il partait hors écran
   dès la troisième entrée : on devait remonter pour écrire, puis redescendre
   pour relire. Il se cale sous la barre de menus, qui est elle-même collante.
   align-self: flex-start est nécessaire : sans lui, l'étirement de la cellule
   donne au bloc toute la hauteur et il n'a plus de place où coller. */
.fiche > .cell.droite.bas > details.card { position: sticky; top: 58px; align-self: flex-start; width: 100%; }

.ligne-compteur { display: flex; align-items: center; justify-content: space-between;
                  gap: 12px; flex-wrap: wrap; margin: 10px 2px 6px; }
.ligne-compteur .pagin { margin: 0; }

/* Fourchette de salaires atteignable au prix visé (29/07). Elle se met à jour
   pendant qu'on glisse le curseur : c'est la réponse à « à ce prix-là, je peux
   payer quoi ? », qui est la seule question qu'on se pose à ce moment-là. */

/* Un repère n'est pas un bouton d'action : la règle générale button:hover le
   repeignait en bleu plein au survol — le même piège que sur les tuiles. */
.repere, .repere:hover, .repere:focus { background: none; }

/* Quand la grille a DEUX rangées, celles du haut perdent leur marge basse
   (29/07). Sans ça la seconde rangée démarre 18 px plus bas que le journal des
   onglets à une rangée, où il suit directement les contacts — mesuré : 582 px
   contre 564. C'est la dernière source d'écart entre les onglets. */
.fiche.deux > .cell:not(.bas) { padding-bottom: 0; }

/* Légende de fond du tableau de liège (29/07). Un mur vide ne dit rien de ce
   qu'on peut y faire ; elle reste en filigrane DERRIÈRE les notes et laisse
   passer les clics — sinon elle bloquerait le double-clic qu'elle annonce. */
.mur-legende { position: absolute; inset: 0; display: flex; flex-direction: column;
               align-items: center; justify-content: center; text-align: center;
               color: var(--muted); opacity: .5; font-size: 15px; pointer-events: none;
               line-height: 1.6; }
.mur-legende span { font-size: 13px; opacity: .85; }

/* Le repère du prix annoncé (29/07). Il ne se lit pas comme les autres : c'est
   le point de départ de la négociation, pas une hypothèse de salaire. */

/* La zone grisée du curseur (29/07). Elle ne masque rien et ne bloque rien :
   on peut toujours poser le pouce dedans, c'est une réticence, pas un mur.
   pointer-events: none — sinon elle avalerait le clic sur la piste, qui est le
   geste principal du widget. */
.curseur-ombre { position: absolute; left: 0; top: 0; height: 20px; border-radius: 4px;
                 background: rgba(90, 106, 124, .18);
                 border-right: 2px solid rgba(90, 106, 124, .45);
                 pointer-events: none; z-index: 1; }

/* La tuile des GUSO (29/07) : présente dans les stats, mais hors de tout
   comptage. Le gris dit qu'elle ne s'additionne pas aux autres — c'est la
   seule chose qu'on risque de faire par mégarde en la voyant sur la rangée. */
.tile.muted-tile { border-style: dashed; }
.tile.muted-tile b { color: var(--muted); }

/* ---- Widget « Prix indicatif », disposition du 29/07 dictée par Morgan ----
   « Transport, Cession, Total HT, Total TTC en haut ; km, €/km et mesurer sur
   le côté ; le curseur en dessous ; sous le curseur le type de cachet et la
   synthèse de marge. »
   Le résultat en premier, les réglages à côté : chaque cause est désormais à
   portée de son effet, alors qu'avant le widget les séparait par toute sa
   hauteur. */
/* Tout tient sur UNE LIGNE (29/07, Morgan : « compact ! là les déplacements ça
   ne va pas du tout, utilise la longueur plutôt que la hauteur »). Le bloc
   déplacement empilait ses trois champs verticalement : il faisait à lui seul
   quatre fois la hauteur des résultats et creusait un trou à gauche.
   Une seule rangée souple : les quatre résultats à gauche, les trois réglages
   de déplacement poussés à droite, tout aligné sur la même ligne de base. */
/* Alignement par le HAUT (29/07, Morgan : « mieux aligner… les libellés alignés
   par le haut ? »). En `flex-end`, les libellés flottaient à des hauteurs
   différentes parce que les valeurs n'ont pas la même taille — le TTC est en
   26 px, les autres en 22, le champ de saisie en 21 avec sa bordure. Ce sont
   les LIBELLÉS qu'on parcourt des yeux : ce sont eux qui doivent former une
   ligne. */
.calc-tete { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 6px 20px;
             margin-top: 4px; }
.calc-res { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 4px 20px; }
/* Chaque résultat occupe une largeur FIXE (29/07, Morgan : « les déplacements
   passent à la ligne, verrouille tout »). En largeur automatique, passer de
   1 600 à 2 100 € élargissait la rangée de quelques dizaines de pixels et
   suffisait à renvoyer le bloc déplacement à la ligne suivante. */
.calc-res .res { display: flex; flex-direction: column; gap: 2px; min-width: 92px; }
.calc-res .res-ttc { min-width: 112px; }
.calc-res .res-saisie { min-width: 108px; }
.calc-res b { font-variant-numeric: tabular-nums; }
/* Hauteur de ligne fixe sur les libellés : sans elle, un libellé qui passe sur
   deux lignes décalerait sa valeur d'un cran par rapport aux autres. */
.calc-res label, .calc-depl .calc-l label { font-size: 12px; color: var(--muted);
                                            white-space: nowrap; line-height: 18px; }
.calc-res b { font-size: 19px; color: var(--bleu); line-height: 1.15; white-space: nowrap; }
.calc-res .res-ttc b { color: var(--orange); font-size: 22px; }
/* La cession est la seule valeur qu'on saisit : elle se distingue par le champ
   lui-même, pas par une couleur de plus. */
.calc-res .res-saisie input { width: 104px; font-size: 21px; font-weight: 700; color: var(--bleu);
                              padding: 1px 4px; }
/* margin-left: auto — le déplacement se colle à droite tant qu'il y a la place,
   et passe à la ligne de lui-même quand il n'y en a plus. */
/* Le bloc déplacement est une COLONNE : une rangée de champs qui ne se coupe
   jamais, et sous elle la phrase du levier. Tant que le levier était un
   quatrième élément de la rangée, son apparition élargissait le bloc et
   renvoyait tout à la ligne dès qu'on saisissait un kilométrage — exactement ce
   que Morgan signalait. */
.calc-depl { display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
             margin-left: auto; min-width: 0; }
/* La rangée se replie quand il n'y a PLUS la place (02/08). En `nowrap` elle
   mesurait 297 px INCOMPRESSIBLES — 87 (Km aller-retour) + 104 (Remboursé) +
   74 (Facturé) + deux gaps de 16 — et la popup de fiche à 375 px n'en offre
   que 233 : `.calc-tete` 297/233, donc `<dialog id="dlg-edit-date">` 351/341.
   C'était le seul défilement latéral restant de l'onglet Finances, et il est
   ANTÉRIEUR à la refonte des onglets du 01/08 (mesure identique avant/après).
   `wrap` ne change RIEN aux largeurs confortables : `.calc-depl` est plafonné
   à 340 px, donc tant qu'il dispose de ses 297 px la rangée reste sur une
   seule ligne — vérifié à 1280, 1512 et 1920. La disposition compacte du 29/07
   (« utilise la longueur plutôt que la hauteur ») est intacte ; la rangée ne se
   coupe que là où l'alternative était de déborder.
   Pas de media query ICI, volontairement : la contrainte est la largeur du
   CONTENEUR, pas celle de l'écran. Le widget vit aussi dans une colonne étroite
   sur grand écran (fiche, atelier), où un seuil en `max-width: 480px` ne se
   déclencherait pas alors que le débordement, lui, serait bien là. */
.calc-champs { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 8px 16px; }
.calc-depl .calc-l { gap: 2px; }
.calc-depl select { width: 104px; }
.calc-depl input[type=number] { width: 74px; }
/* Le levier passe TOUJOURS sous la rangée : c'est une phrase, elle n'a pas à
   se battre pour de la largeur avec les champs qui la produisent. */
/* Hauteur réservée en permanence : la phrase du levier n'apparaît qu'avec un
   kilométrage, et sans réservation son apparition décalait le curseur. */
/* Le levier revient à la ligne s'il le faut, et sa hauteur est réservée pour
   deux lignes : en `nowrap` il élargissait le bloc déplacement à 440 px, ce qui
   renvoyait tout le bloc sous la rangée des résultats dès qu'on saisissait un
   kilométrage. Une phrase explicative n'a pas à commander la mise en page. */
.calc-levier { margin: 0; font-size: 12px; min-height: 17px; max-width: 320px; }
.calc-depl { max-width: 340px; }
/* La synthèse : une phrase, jamais deux lignes de chiffres. */
.synthese { margin: 10px 0 0; font-size: 15px; font-weight: 600; }

/* La ligne de titre du widget porte la synthèse à droite (29/07). Une seule
   ligne, jamais deux : `nowrap` empêche l'en-tête de changer de hauteur à
   chaque cran du curseur — c'est exactement le défaut corrigé plus bas sur la
   zone des verdicts, il n'a pas à réapparaître ici. */
/* La synthèse occupe sa PROPRE ligne, alignée à gauche (29/07, Morgan : « fixer
   la phrase pour qu'elle ne bouge pas, que seuls les chiffres changent »).
   Alignée à droite, elle se déplaçait tout entière au moindre chiffre de plus :
   c'est le bord droit qui restait fixe, donc tous les mots glissaient. À gauche,
   son premier mot est cloué, et seul ce qui suit un montant peut bouger — d'au
   plus un caractère, chiffres à chasse constante.
   Elle est aussi trop longue pour tenir à côté du titre dans une colonne de
   868 px : lui donner sa ligne évite un retour à la ligne qui, lui, changerait
   la hauteur de l'en-tête. */
.calc-entete { display: block; }
.calc-entete .synthese { display: block; margin: 3px 0 0; text-align: left; }
.synthese .nb { font-variant-numeric: tabular-nums; }

/* Les verdicts à gauche, le coût de référence à droite (29/07). Une GRILLE,
   pas un positionnement absolu : l'encart précédent était en absolu et
   recouvrait la fin des verdicts dès que le texte s'allongeait. Ici chacun a sa
   colonne, le chevauchement est impossible par construction. */
/* L'encadre « Cout d'une date » qui occupait la 2e colonne a ete supprime le
   03/08 (il portait 1 148 quand le bouton disait 1 147). Une seule colonne. */
.verdicts-zone { display: grid; grid-template-columns: minmax(0, 1fr);
                 gap: 10px 20px; align-items: center; }
/* L'encadré ne doit pas dépasser la zone qu'il accompagne (29/07, Morgan :
   « diminue le cadre de droite pour que le bas arrive au niveau de la deuxième
   ligne »). Deux lignes de contenu, corps réduit, marges minces : c'est un
   rappel de constantes qu'on consulte du coin de l'œil, pas un tableau. */
.cout-ref { border: 1px solid var(--ligne); border-radius: 8px; padding: 5px 10px 6px;
            background: #fbfcfe; font-size: 11px; line-height: 1.4; }
.cout-t { font-weight: 700; color: var(--bleu2); font-size: 11px; }
.cout-postes { display: flex; flex-wrap: wrap; gap: 2px 12px; color: var(--muted); }
.cout-postes i { font-style: normal; opacity: .8; }
.cout-tot { display: flex; align-items: baseline; gap: 10px; border-top: 1px solid var(--ligne);
            margin-top: 2px; padding-top: 2px; }
.cout-tot b { font-size: 14px; color: var(--texte); }
.cout-tot .muted { font-size: 10px; }
@media (max-width: 1100px) {
  .verdicts-zone { grid-template-columns: minmax(0, 1fr); }
  .cout-ref { min-width: 0; }
}
/* Le taux de TVA dans le libellé plutôt qu'en note de bas de widget : il ne
   qualifie que ce montant-là, et une note qu'on lit une fois n'a pas à occuper
   une ligne en permanence. Idem pour l'aller-retour, dit par « aller simple ». */
.calc-res .tva { opacity: .7; }

/* La media query `@media (max-width: 480px)` qui vivait ici a été RETIRÉE le
   02/08 : elle disait déjà `.calc-champs { flex-wrap: wrap }`, désormais posé
   une bonne fois plus haut sur la règle elle-même. Deux endroits pour un même
   comportement, c'est une divergence en attente. Le seuil en largeur d'ÉCRAN
   ratait en outre le cas d'une colonne étroite sur grand écran. Son
   `.calc-depl { max-width: 100% }` ne servait à rien : `.calc-depl` porte déjà
   `min-width: 0`, donc il se rétrécit à 233 px sans que le `max-width: 340px`
   l'en empêche — c'est bien son enfant `.calc-champs` qui débordait, et lui
   seul. Vérifié par mesure, popup à 341/341 sans cette déclaration. */

/* ---- Événements de la structure (30/07) : une ligne par événement ----
   Des pastilles ne pouvaient porter que le nom ; dès qu'un événement a sa
   période, sa fenêtre de démarchage, son lieu et les compagnies passées, il lui
   faut sa ligne. Les informations absentes ne laissent pas de trou : une ligne
   à moitié pleine de tirets ne se lit pas. */
.entete-bloc { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.evts { margin-top: 8px; display: flex; flex-direction: column; gap: 2px; }
.evt-l { padding: 7px 10px; border-radius: 8px; }
.evt-l:nth-child(odd) { background: #fafbfd; }
.evt-h { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.evt-h b { overflow-wrap: anywhere; }
.evt-i { font-size: 12.5px; color: var(--muted); margin-top: 2px; overflow-wrap: anywhere; }
.evt-n { font-size: 13px; margin-top: 4px; overflow-wrap: anywhere; }
/* Deux colonnes de champs dans une popup, une seule en colonne étroite. */
.grid2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 14px; }
@media (max-width: 700px) { .grid2 { grid-template-columns: minmax(0, 1fr); } }

/* ---- Message de confirmation en surimpression (30/07) ----
   Il ne pousse rien : `position: fixed` le sort du flux, donc le titre de la
   page ne descend plus de soixante pixels à chaque enregistrement. En bas à
   droite, hors du chemin du regard qui reste sur ce qu'on vient de modifier.
   Il ne bloque pas non plus le clic : `pointer-events` n'est rétabli que sur la
   croix, pour qu'un toast posé sur un bouton ne l'empêche pas d'être cliqué.

   01/08 — IL A DÉMÉNAGÉ DE 68 PIXELS, et ce n'est pas un détail. La bulle
   Agenda (voir plus bas) occupe désormais right:18px / bottom:18px : c'était
   EXACTEMENT la place du toast. Sans ce déplacement, un message d'ERREUR
   d'enregistrement — qui reste affiché jusqu'à fermeture manuelle — finirait
   caché sous la bulle, et on croirait avoir sauvegardé. Deux précautions
   plutôt qu'une : `bottom: 86px` (56 de bulle + 18 de marge + 12 d'écart) le
   met hors d'atteinte, et `z-index: 90` le fait passer PAR-DESSUS le panneau
   ouvert, qui commence lui aussi à 86px. Si la bulle change de taille, cette
   valeur change avec elle. */
.toast { position: fixed; right: 18px; bottom: 86px; z-index: 90;
         display: flex; align-items: center; gap: 12px;
         max-width: min(440px, calc(100vw - 36px));
         padding: 11px 12px 11px 16px; border-radius: 10px;
         background: #e7f4ea; color: #14532d; border: 1px solid #bfe3c8;
         box-shadow: 0 6px 22px rgba(0,0,0,.14); font-size: 14.5px;
         pointer-events: none;
         animation: toast-entre .18s ease-out; }
.toast.err { background: #fdecea; color: var(--alerte); border-color: #f5c6c1; }
.toast > span { overflow-wrap: anywhere; }
.toast-x { pointer-events: auto; background: none; border: 0; cursor: pointer;
           color: inherit; opacity: .5; font-size: 13px; padding: 2px 4px; line-height: 1; }
.toast-x:hover { opacity: 1; }
.toast.part { opacity: 0; transform: translateY(6px); transition: opacity .25s, transform .25s; }
@keyframes toast-entre { from { opacity: 0; transform: translateY(10px); } }
/* Un utilisateur qui a demandé moins d'animations n'en subit aucune. */
@media (prefers-reduced-motion: reduce) {
  .toast { animation: none; }
  .toast.part { transition: none; }
}
/* Un COMPTEUR se centre, il ne s'aligne pas à droite (30/07, Morgan :
   « centrer »). `.num` sert aux montants, qu'on lit en colonne pour comparer
   les unités — d'où l'alignement à droite. Un nombre de sets à un chiffre n'a
   rien à comparer : aligné à droite sous un en-tête aligné à gauche, il donnait
   juste l'impression que la colonne était de travers. */
td.cnt, th.cnt { text-align: center; white-space: nowrap; }

/* ---- Zones nommées sous la piste de prix (30/07) ----
   Elles remplacent quatre repères chiffrés qui marquaient le MILIEU d'une zone,
   c'est-à-dire des endroits où il ne se passe rien — et dont deux se
   touchaient. Ce qui compte, ce sont les frontières.
   Chaque zone porte son nom et, en dessous en petit, ce qu'elle veut dire :
   Morgan a explicitement écarté l'infobulle (« non, en petit en seconde ligne
   sous mini et tout »). La phrase complète reste en `title` pour les zones
   étroites où le texte court est tronqué.
   Une zone plus fine que 4 % de la piste se cache : à cette largeur elle
   n'afficherait qu'une bouillie de lettres coupées. */
/* Corps agrandi (30/07, Morgan : « on a l'espace pour agrandir les titres et
   les légendes, sur 2 lignes max »). La place existait : la piste n'occupe que
   sa hauteur de curseur, tout le reste était du blanc.
   La légende est bornée à DEUX lignes par `line-clamp` : au-delà elle
   déborderait sur les verdicts, et une zone étroite pourrait sinon pousser la
   piste entière vers le bas. Le texte tronqué reste lisible en entier dans
   l'infobulle. */
/* 30/07 — décalées de 74 à 90px pour laisser la bande des bornes respirer :
   elles commençaient exactement là où le texte des bornes se posait. */
.curseur-zones { position: absolute; left: 10px; right: 10px; top: 90px; height: 52px; }
.curseur-zones { pointer-events: none; }
.curseur-zones .zn { pointer-events: auto; position: absolute; top: 0; height: 100%;
                     border-left: 1px solid var(--ligne); padding: 3px 8px 0 7px;
                     overflow: hidden; }
.curseur-zones .zn:first-child { border-left: 0; padding-left: 0; }
.zn-n { display: block; font-size: 14px; font-weight: 700; color: var(--bleu2);
        line-height: 1.3; white-space: nowrap; }
.zn-p { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
        font-size: 12.5px; color: var(--muted); line-height: 1.3;
        overflow: hidden; overflow-wrap: anywhere; }
.zn-perte .zn-n { color: var(--alerte); }
.zn-ideal .zn-n { color: var(--ok); }
/* Le repère du prix annoncé passe SOUS les zones, sinon son étiquette orange
   recouvre les noms. */
/* Le repère du prix annoncé est AU-DESSUS de la piste : sous elle, il occupait
   une ligne entière pour quatre chiffres et se battait avec les noms de zones.
   L'étiquette précède le trait dans le HTML, donc elle se pose naturellement
   au-dessus. */
/* Les bornes de la piste, discrètes mais présentes : sans elles l'échelle est
   invérifiable à l'œil, et on confond le plafond avec le TTC affiché. */
/* 30/07 — les bornes étaient à 66px et le POUCE descend jusqu'à 68px (20px de
   diamètre, centré sur une piste dont l'axe est à 58px), plus 2px de liseré
   blanc et son ombre portée. Le pouce barrait donc « 663 € HT · 699 € TTC »
   dès qu'il approchait d'un bord. Elles passent sous lui, à 74px : le pouce
   s'arrête à 70px ombre comprise, il reste 4px de garde. */
.piste-bornes { position: absolute; left: 10px; right: 10px; top: 74px;
                display: flex; justify-content: space-between;
                font-size: 10.5px; color: var(--muted); pointer-events: none; }

/* 30/07 — la ligne de verdict RETENUE par un clic. Morgan choisit une hypothèse
   de cachets, puis explore les prix sous cette hypothèse : il faut donc voir
   laquelle est active, sinon la synthèse parle d'un niveau qu'on a oublié avoir
   choisi. Un liseré et un fond très pâle suffisent — la couleur du niveau, elle,
   dit déjà si le prix passe ou non, et on ne la double pas. */
.verdict.retenu { background: var(--selection); border-radius: 7px;
                  box-shadow: inset 3px 0 0 var(--bleu2); padding-left: 9px; }

/* 30/07, Morgan : « modifier / supprimer ne sont pas alignés correctement à
   droite » dans Dates & concerts de la fiche. La cellule `empile` alignait ses
   gestes à gauche d'une colonne devenue large : ils flottaient au milieu de la
   ligne. Alignés à droite, comme toute colonne d'actions de tableau. Portée
   limitée au bloc des dates : mails.php utilise la même variante à gauche, et
   c'est voulu là-bas. */
#dates-bloc .rowactions.empile { justify-content: flex-end; }

/* 30/07, Morgan : « améliorer le visuel pour rendre compréhensible la
   possibilité de déplacement de colonnes ? petite zone type poignées ? ».
   Une poignée ⋮⋮ devant chaque en-tête déplaçable, et le curseur « main
   ouverte » : l'affordance des tableurs, sans un octet de JS en plus. Discrète
   au repos, franche au survol — elle invite sans encombrer la ligne. Pendant le
   glissement, la colonne saisie se signale. */
#tabContacts th[draggable="true"] { cursor: grab; white-space: nowrap; }
#tabContacts th[draggable="true"]::before {
  content: "⋮⋮"; letter-spacing: -2px; margin-right: 7px;
  color: var(--muted); opacity: .45; font-weight: 400;
}
#tabContacts th[draggable="true"]:hover::before { opacity: 1; color: var(--bleu2); }
#tabContacts th[draggable="true"]:active { cursor: grabbing; }

/* 30/07, Morgan : « optimiser l'espace et rendre ultra compact ces commandes.
   PARTOUT SUR LE SITE !!! ». La cause : la règle générale donne aux champs
   100 % de largeur — juste pour un formulaire de SAISIE, absurde pour une
   BARRE DE COMMANDE où « étiquette + champ + bouton » doivent tenir sur une
   ligne. Ici on borne les champs des deux motifs de commande de la maison
   (.bulkbar et .inline) à leur besoin réel : la barre entière devient une
   ligne, l'écran respire. Les formulaires de saisie (.fld) ne changent pas. */
.bulkbar .bulkrow select, .bulkbar .bulkrow input:not([type=checkbox]),
.inline select, .inline input[type=date], .inline input[type=number] {
  width: auto; min-width: 110px; max-width: 240px;
}
.bulkbar .bulklab { font-weight: 600; min-width: 64px; }
.bulkbar .bulkrow { padding: 4px 0; }
.bulkbar .bulkrow button, .inline button { white-space: nowrap; }

/* 30/07, Morgan : « sur une ligne au maximum ! ». La barre de commandes entière
   devient UNE ligne : les commandes se posent côte à côte et ne reviennent à la
   ligne que si l'écran manque. Les filets qui séparaient les rangées n'ont plus
   d'objet — l'espacement fait le travail. */
.bulkbar { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 26px; }
.bulkbar .bulkrow { padding: 0; }
.bulkbar .bulkrow + .bulkrow { border-top: 0; }

/* Vignettes en grille (30/07, Morgan sur Musiciens : « mieux utiliser
   l'espace ») — .cards n'avait AUCUNE règle : chaque vignette prenait sa
   ligne. Grille fluide, autant de colonnes que la largeur en accepte. */
/* 03/08, Morgan : « hauteur universelle pour toutes les cartes (partout !
   diff prod et admin !) ». C'etait `align-items:start` : chaque carte prenait
   sa propre hauteur, d'ou des bas de cartes en escalier des qu'une note etait
   plus longue que la voisine. `stretch` egalise la rangee. Un seul endroit :
   `.cards` sert en Diffusion, Production et Admin — la charte, pas un ecran.
   `.card` passe en colonne flex pour que sa derniere ligne (« prochain : … »)
   se pose en bas plutot que de flotter au milieu du vide gagne. */
.cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:12px;align-items:stretch}
.cards > .card{display:flex;flex-direction:column}
.cards > .card > .alerte-carte:last-child{margin-top:auto}

/* ---------------------------------------------------------------------------
   COLONNE DE LECTURE — pour les articles (31/07, Morgan : « centrer pleine page
   et plus lisible l'article fourni »). Un article n'est pas un tableau de bord :
   il se lit, donc il lui faut une longueur de ligne courte (une ligne trop
   large fatigue l'oeil, qui perd le debut de la suivante), un interligne
   genereux et des titres qui respirent. La carte, elle, prend la page entiere
   et centre cette colonne.
   --------------------------------------------------------------------------- */
.card.lecture { max-width: 1100px; margin: 0 auto; padding: 28px 32px; }
/* Largeur en PIXELS et non en `ch` : `ch` depend de la police du bloc, donc un
   titre en gras et en 19 px obtenait une colonne plus large que ses paragraphes
   et debordait de 95 px sur leur gauche. Une seule mesure pour tous. */
.card.lecture > * { max-width: 660px; margin-left: auto; margin-right: auto; }
.card.lecture p { line-height: 1.7; margin: 0 auto 14px; }
.card.lecture h2 { margin: 30px auto 10px; padding-top: 10px; border-top: 1px solid var(--ligne); font-size: 19px; }
.card.lecture h2:first-of-type { border-top: 0; padding-top: 0; }
.card.lecture h3 { margin: 20px auto 8px; font-size: 16px; }
.card.lecture ul, .card.lecture ol { line-height: 1.7; margin: 0 auto 14px; padding-left: 22px; }
.card.lecture li { margin-bottom: 6px; }
.card.lecture table { width: 100%; }
.card.lecture .tablewrap { max-width: 100%; }
@media (max-width: 720px) { .card.lecture { padding: 18px 16px; } }

/* ---------------------------------------------------------------------------
   DEUX COLONNES — `cols` + `col large` / `col etroite`.
   Ces classes étaient utilisées par jalons.php, veille_groupes.php et les deux
   écrans de Planification SANS jamais avoir existé dans cette feuille : les
   « colonnes » s'empilaient donc bêtement l'une sous l'autre. Le rapport large
   / étroit vaut ce qu'il vaut (2 pour 1) : la colonne étroite porte l'aide et
   les compteurs, la large porte le travail. Sous 1000 px on repasse en une
   seule colonne — jamais de scroll latéral, règle maison.
   --------------------------------------------------------------------------- */
.cols { display: flex; gap: 16px; align-items: flex-start; }
.cols > .col { min-width: 0; }              /* sans ça un tableau large déborde */
.col.large { flex: 2 1 0; }
.col.etroite { flex: 1 1 0; }
@media (max-width: 1000px) {
  .cols { flex-direction: column; }
  .col.large, .col.etroite { flex: none; width: 100%; }
}

/* Popup PLEIN PAGE (31/07, Morgan : « popup plein page nécessaire ») — pour les
   popups qui embarquent un écran entier, comme l'onglet Trajets d'une date
   confirmée : 1 080 px n'y suffisent pas. */
dialog.plein { max-width: none; width: calc(100vw - 32px); max-height: calc(100vh - 32px); height: calc(100vh - 32px); }
@media (max-width: 720px) { dialog.plein { width: 100vw; height: 100vh; max-height: 100vh; border-radius: 0; } }

/* ---------------------------------------------------------------------------
   PLAN DE ROUTE — composition des voitures et mise en page de l'écran (31/07).
   Tout est préfixé .pr- : aucune règle existante n'est modifiée.
   --------------------------------------------------------------------------- */
.pr-sec { margin: 0 0 16px; }
.pr-sec:last-of-type { margin-bottom: 0; }
.pr-sec-t { display: flex; align-items: center; gap: 10px; font-size: 12px; text-transform: uppercase;
            letter-spacing: .6px; color: var(--muted); font-weight: 600; margin: 0 0 8px; }
/* `order` : sans lui, ::after reste le dernier enfant et le bouton de création
   se colle au libellé de section — le filet doit passer ENTRE les deux. */
.pr-sec-t::after { content: ""; flex: 1; height: 1px; background: var(--ligne); order: 5; }
.pr-sec-t > .btn { text-transform: none; letter-spacing: 0; font-weight: 400; order: 9; margin-left: 0; }
.pr-arr { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 6px 14px; align-items: center; }
.pr-arr > label { font-weight: 400; display: inline-flex; align-items: center; gap: 7px; white-space: nowrap; }
.pr-note { margin: 6px 0 0; font-size: 12px; }
.pr-duo { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 8px 14px; }
.pr-duo > div > label { display: block; font-size: 12px; color: var(--muted); margin: 0 0 3px; }
.pr-voitures { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 10px; align-items: start; }
.pr-voiture { border: 1px solid var(--ligne); border-radius: 10px; padding: 9px 11px; background: var(--carte); }
.pr-vt { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin: 0 0 8px; }
.pr-vt > b { font-size: 13px; }
.pr-champ { margin: 0 0 8px; }
.pr-champ:last-child { margin-bottom: 0; }
.pr-champ > label { display: block; font-size: 12px; color: var(--muted); margin: 0 0 3px; }
.pr-pass { display: flex; flex-wrap: wrap; gap: 2px 12px; }
.pr-pass > label { font-weight: 400; display: inline-flex; align-items: center; gap: 6px; font-size: 13px; }
.pr-pass-vide { font-size: 13px; }
.pr-reglages { border: 1px solid var(--ligne); border-radius: 10px; padding: 8px 11px; margin: 0 0 16px; }
.pr-reglages > summary { cursor: pointer; color: var(--muted); font-size: 13px; list-style-position: outside; }
.pr-reglages[open] > summary { margin-bottom: 8px; }
.pr-pied { display: flex; justify-content: flex-end; margin-top: 14px; }
.pr-barre { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: 14px 0; }
.pr-etapes { margin: 8px 0 0; line-height: 1.7; }
@media (max-width: 640px) { .pr-arr { grid-template-columns: minmax(0, 1fr); } .pr-arr > label { margin-top: 6px; } }

/* Tiroir « Infos » d'une fiche (31/07) : les champs d'identification se
   remplissent une fois et se relisent rarement. Fermé, le tiroir garde un
   résumé d'une ligne — « La Gacilly (56) · mi décembre · Piste · Philippe
   ROBERT » — pour qu'on sache ce qu'il contient sans l'ouvrir. */
details.tiroir > summary { cursor: pointer; font-weight: 600; color: var(--bleu2); list-style: none; }
details.tiroir > summary::-webkit-details-marker { display: none; }
details.tiroir > summary::before { content: '▸ '; opacity: .7; }
details.tiroir[open] > summary::before { content: '▾ '; }
details.tiroir > summary .muted { font-weight: 400; }

/* ===========================================================================
   CHARTE — blocs posés le 31/07 depuis 032_CHARTE_UI §10. Aucun ne modifie une
   règle existante : tous ajoutent ce qui manquait, et que le code employait
   déjà sans le savoir.
   =========================================================================== */

/* 1. Pastilles d'état. `.tag` ne déclarait AUCUNE couleur de texte : elle
   héritait de son parent, donc blanche dans un <button> — c'est ce qui rendait
   les onglets de popup invisibles (contraste mesuré 1,00). `.ok` et `.warn`
   étaient employées par le code sans exister ici. */
.tag { color: var(--muted); }
.tag.ok     { background: #e7f4ea;        color: var(--ok);     border: 1px solid #bfe3c8; }
.tag.warn   { background: var(--warn-bg); color: var(--orange); border: 1px solid #f0dcae; }
/* 02/08 — la 4e couleur du code de Morgan (« vert complétée, jaune presque
   finie, orange commencée, rouge rien »). Le JAUNE est reservé à l'étape
   optionnelle que personne n'a tranchée : elle appelle une décision, pas du
   travail. Distinct de l'orange, qui dit « commencé ». */
.tag.jaune  { background: #fdf6d8; color: #8a6d1f; border: 1px solid #ecd98b; }
/* Remplaçant : le bleu est le code maison, il ne doit exister qu'ici. */
.tag.rempla { background: var(--clair);   color: var(--bleu2); }

/* 2. Grille de cartes : une seule, avec une variante serrée pour les cartes
   denses. Vocation à remplacer .pcards, .dc-cards et .mus-cartes. */
.cards.serree  { grid-template-columns: repeat(auto-fill, minmax(258px, 300px)); }
.cards.centree { justify-content: center; }

/* 2 bis. LA CARTE DE DATE A UNE HAUTEUR UNIQUE — 03/08, Morgan, réclamé une
   dizaine de fois : « toutes les cartes de toutes les pages doivent avoir la
   MÊME hauteur (diff, prod ET admin) ». Ce n'est PAS « égalisées par rangée » :
   c'est UNE hauteur, la même d'un écran à l'autre.

   Ce qui n'allait pas, mesuré en production le 03/08 (Chrome, fenêtre 1400 px,
   hauteurs relevées à la boîte près sur les pages rendues par ~/ck_tmp/_verif.php) :
     Diffusion  .cards.serree  184 → 286 px    (pipeline.php, vues En cours et Confirmées)
     Production .dc-cards      209 → 240 px    (dates_confirmees.php)
     Administration .dca-cards 115 → 157 px    (dates_confirmees_admin.php, 4 états)
   Trois écrans, trois tailles, et des écarts DANS chaque écran : une carte à
   trois lignes (date classée) à côté d'une carte à sept (Get In, FdR, voitures,
   note, alerte). Le regard ne peut pas balayer une grille dont chaque tuile a
   sa propre taille.

   LA MESURE FIXE LA RÈGLE : la carte la plus chargée du parc mesurait 286 px.
   La hauteur standard est posée à 300 px — au-dessus du pire cas constaté, avec
   la place d'une ligne de plus. Une seule variable, c'est le seul bouton à
   tourner le jour où une ligne s'ajoute à la carte ; personne n'a à écrire une
   exception dans son écran.

   `min-height` et non `height` : si un jour une carte dépasse malgré tout, elle
   grandit — elle ne se fait ni rogner ni doter d'un ascenseur interne (Morgan :
   « sans scroll interne ni écrasement »). Le `stretch` de la grille remet alors
   toute la rangée à la même taille.

   Les trois conteneurs sont nommés ici et NULLE PART AILLEURS. `.dc-cards` et
   `.dca-cards` sont en sursis : la charte prévoit `.cards.serree` pour les
   remplacer (voir 2 ci-dessus), et le jour où les deux écrans l'emploieront,
   les deux sélecteurs disparaîtront d'ici sans que la règle change. */
:root { --h-carte-date: 300px; }

.cards.serree > .card,
.dc-cards     > .card,
.dca-cards    > .card {
  min-height: var(--h-carte-date);
  display: flex; flex-direction: column;
  }

/* Le contenu court ne flotte pas : la dernière ligne (⚠ prochain : …, l'alerte,
   « classée le … ») se pose au BAS de la carte, le vide gagné reste au milieu.
   `!important` à contrecœur, et pour une seule raison : les trois écrans posent
   cette dernière ligne avec un `style="margin:8px 0 0"` ÉCRIT DANS LE PHP, et
   un style en ligne bat toujours la feuille. Le `!important` saute le jour où
   ces `style=` sautent — le patch est décrit dans le rapport du 03/08. */
.cards.serree > .card > :last-child,
.dc-cards     > .card > :last-child,
.dca-cards    > .card > :last-child { margin-top: auto !important; }

/* Les rangées s'égalisent. Répéter la classe (`.dc-cards.dc-cards`) n'est pas
   une coquetterie : dates_confirmees.php:1431 et dates_confirmees_admin.php:663
   écrivent `align-items:start` dans un <style> posé DANS la page, donc lu après
   cette feuille ; à égalité de poids, le dernier gagne. La classe doublée pèse
   plus lourd et rend la main à la charte. Comme pour le `!important` ci-dessus,
   ces deux sélecteurs redeviennent `.dc-cards, .dca-cards` dès que les <style>
   en question sont retirés du PHP. */
.cards.serree, .dc-cards.dc-cards, .dca-cards.dca-cards { align-items: stretch; }

/* 3. La carte cliquable (patron .dc-card de dates_confirmees.php). */
.card.ouvrable { cursor: pointer; }
.card.ouvrable:hover { border-color: var(--bleu2); }
.card.ouvrable:focus-visible { outline: 2px solid var(--bleu2); outline-offset: 2px; }
.card.faite { opacity: .55; }
.card .alerte-carte { margin-top: 8px; font-size: 12px; }

/* 4. Onglets de popup (patron .dc-tab). La COULEUR est obligatoire : sans elle
   un <button> hérite du blanc de `button { color: #fff }`. */
.tabbar { display: flex; flex-wrap: wrap; gap: 4px;
          border-bottom: 1px solid var(--ligne); margin-bottom: 14px; padding-bottom: 8px; }
.tabbar > button { font: inherit; font-size: 13px; padding: 6px 10px; border-radius: 8px;
                   border: 1px solid transparent; background: none; color: var(--muted);
                   cursor: pointer; display: inline-flex; align-items: center; gap: 6px; }
.tabbar > button:hover { color: var(--bleu2); }
.tabbar > button.on { background: #eef2f7; border-color: var(--ligne);
                      color: var(--bleu); font-weight: 600; }
.tabpane { display: none; }
.tabpane.on { display: block; }

/* Croix de fermeture, posée automatiquement sur toute <dialog> par
   fermeture_popup_script() (inc/layout.php). Elle flotte en haut à droite, au
   -dessus du contenu, sans réserver de place — les popups existantes n'ont pas
   à être retouchées. */
dialog > .ck-x { position: absolute; top: 8px; right: 10px; z-index: 5;
                 background: none; border: 0; padding: 2px 6px; margin: 0;
                 font-size: 22px; line-height: 1; color: var(--muted); cursor: pointer; }
dialog > .ck-x:hover { background: none; color: var(--alerte); }

/* --- Éditeur du widget mail (31/07) : gras, italique, listes et liens. La zone
   imite le <textarea> qu'elle remplace — même bordure, même fond, même rayon —
   pour que rien ne change à l'œil quand le script prend la main. Sans
   JavaScript, c'est le textarea qui reste, et l'écran est celui d'avant. ----- */
.wm-barre { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 6px; }
.wm-barre .wm-o { padding: 4px 9px; font-size: 13px; }
.wm-zone { border: 1px solid var(--ligne); border-radius: 7px; background: #fff;
           padding: 8px 10px; min-height: 240px; max-height: 52vh;
           overflow-y: auto; line-height: 1.55; }
.wm-zone:focus-visible { outline: 2px solid var(--bleu2); outline-offset: 1px; }
.wm-zone p { margin: 0 0 12px; }
.wm-zone a { color: var(--bleu2); }
.wm-liens { display: flex; flex-wrap: wrap; gap: 4px; align-items: center; margin-top: 6px; }
.wm-liens .wm-l { padding: 4px 9px; font-size: 13px; cursor: grab; }
.wm-liens .wm-l:active { cursor: grabbing; }

/* --- Le menu d'actions replié (⋮). C'est l'ÉTAT REPLIÉ de .rowactions et
   .actbtns, pas une famille de boutons de plus : le contenu du panneau reste
   du .linklike et du .btn.ghost. Rendu en `popover`, donc dans le TOP LAYER :
   il ne prend aucune place dans le flux (la ligne ne s'étire plus) et AUCUN
   `overflow` ancêtre ne le coupe — mesuré le 01/08, en position absolue il
   était rogné de 156 px par .tablewrap{overflow-x:auto}. Fermeture au clic
   extérieur et à Échap : native, zéro JS. Le POSITIONNEMENT, lui, est en JS
   (voir menu_actions_script() dans inc/layout.php) : l'ancrage CSS
   (position-area) n'est pas encore sûr partout. ------------------------- */
.ckmenu-btn { font-size: 16px; line-height: 1.2; padding: 2px 9px; }
.ckmenu-pop { position: fixed; margin: 0; inset: auto;
              border: 1px solid var(--ligne); border-radius: 10px; padding: 8px;
              background: var(--carte); min-width: 210px; max-width: min(280px, 92vw);
              box-shadow: 0 10px 30px rgba(0,0,0,.18);
              flex-direction: column; gap: 4px; align-items: stretch; text-align: left; }
.ckmenu-pop:popover-open { display: flex; }
.ckmenu-pop form { display: block; }
.ckmenu-pop .btn, .ckmenu-pop button, .ckmenu-pop .linklike { width: 100%; text-align: left; }
.ckmenu-pop input[type=text] { width: 100%; }

/* ========================= LA BULLE AGENDA (01/08) =========================
   Morgan : « une bulle en bas à droite (comme on trouve pour des chatbots sur
   des sites), un icône agenda bien visible qui nous suit partout et permet
   d'ouvrir une popup ». Audit : 01_SPECS/042_AUDIT_CALENDRIER_BULLE.md.

   Le panneau est une <dialog> et pas un bloc flottant. Ce n'est pas un choix
   d'esthétique, c'est la seule solution qui marche : mesuré au navigateur
   (audit §2.1), pendant qu'une popup modale est ouverte, un position:fixed à
   z-index maximal, un popover et un dialog.show() sont TOUS injoignables — le
   reste du document est inerte et aucun z-index n'y échappe. Seule une
   <dialog> empilée reste cliquable. La <dialog> apporte en prime, sans une
   ligne, ce qu'un panneau fait main demanderait d'écrire : Échap ferme, le
   focus ne s'échappe pas, et il revient au bouton à la fermeture.

   La largeur suit la taille n°3 de la charte (1080 px, comme dialog.large) :
   la grille du mois fait 629 px et ne bouge pas de 1280 à 1920 px, le panneau
   lui en offre davantage que l'écran plein, qui perd 75 à 112 px en bandeaux
   au-dessus d'elle (audit §1.3 et §2.3). */
.ck-bulle { position: fixed; right: 18px; bottom: 18px; z-index: 61;
            width: 56px; height: 56px; padding: 0; border: 0; border-radius: 50%;
            background: var(--bleu); color: #fff; cursor: pointer;
            display: flex; align-items: center; justify-content: center;
            box-shadow: 0 6px 20px rgba(0, 0, 0, .22);
            transition: transform .15s ease, background .15s ease; }
.ck-bulle:hover { background: var(--bleu2); transform: translateY(-2px); }
.ck-bulle:focus-visible { outline: 3px solid var(--bleu2); outline-offset: 3px; }
/* Le chiffre du jour est dans l'icône : elle dit ce qu'elle ouvre. */
.ck-bulle svg { display: block; }

/* Ancré au coin plutôt que centré : la popup sort de la bulle, comme un
   panneau de chatbot, et l'oeil garde le lien entre le geste et son résultat.
   `inset: auto 18px 86px auto` neutralise le centrage par marges du <dialog>
   modal ; 86px = la hauteur de la bulle et ses marges, la même valeur que le
   toast — les trois se suivent, ils ne se recouvrent jamais. */
#ck-bulle-panneau { position: fixed; inset: auto 18px 86px auto; margin: 0;
                    padding: 0; z-index: 62;
                    width: min(1080px, calc(100vw - 36px)); max-width: none;
                    max-height: calc(100vh - 116px);
                    border: 1px solid var(--ligne); border-radius: 14px;
                    background: #fff; overflow: hidden;
                    box-shadow: 0 14px 44px rgba(0, 0, 0, .26);
                    display: none; flex-direction: column; }
#ck-bulle-panneau[open] { display: flex; }
#ck-bulle-panneau::backdrop { background: rgba(0, 0, 0, .28); }
.ck-bulle-tete { flex: 0 0 auto; display: flex; align-items: center; gap: 12px;
                 padding: 8px 10px 8px 14px; border-bottom: 1px solid var(--ligne);
                 background: var(--clair); }
.ck-bulle-tete b { font-size: 15px; }
.ck-bulle-note { font-size: 12.5px; }
.ck-bulle-plein { margin-left: auto; font-size: 13px; white-space: nowrap; }
.ck-bulle-x { background: none; border: 0; cursor: pointer; line-height: 1;
              font-size: 20px; padding: 0 4px; color: var(--muted); }
.ck-bulle-x:hover { color: var(--texte); }
/* La hauteur nominale couvre la grille (629 px) et sa carte ; `flex` la laisse
   se comprimer quand la fenêtre est plus basse, et c'est le panneau qui défile
   à l'intérieur — jamais la page derrière. */
#ck-bulle-cadre { flex: 1 1 auto; display: block; width: 100%; height: 700px;
                  min-height: 260px; border: 0; }

/* L'agenda posé DANS chaque popup (voir inc/layout.php) : sous une <dialog>
   modale le reste du document est inerte, donc le bouton flottant n'y reçoit
   plus le clic. Même place et même discrétion que la croix de fermeture, dont
   il se range à gauche — 28 px, la largeur que la croix occupe. */
dialog > .ck-agenda-mini { position: absolute; top: 8px; right: 38px; z-index: 5;
                           background: none; border: 0; padding: 3px 5px; margin: 0;
                           line-height: 0; color: var(--muted); cursor: pointer; }
dialog > .ck-agenda-mini:hover { color: var(--bleu); }

/* Sous 720px l'app bascule en vue liste (point de rupture de l'app, pas 640) :
   le panneau prend toute la largeur et monte depuis le bas, comme un tiroir.
   Il monte AUTANT QUE LE MOIS LE DEMANDE, pas jusqu'en haut : mesuré, un mois
   à une seule date ne fait que 85 px de liste, et un tiroir plein écran
   n'aurait montré que du blanc. Le plafond reste l'écran moins ses marges. */
@media (max-width: 720px) {
  .ck-bulle { right: 12px; bottom: 12px; width: 50px; height: 50px; }
  #ck-bulle-panneau { inset: auto 12px 12px 12px; width: auto; height: auto;
                      max-height: calc(100vh - 24px); border-radius: 12px; }
  .ck-bulle-note { display: none; }   /* la place manque, la tête reste lisible */
}
@media (prefers-reduced-motion: reduce) {
  .ck-bulle, .ck-bulle:hover { transition: none; transform: none; }
}

/* Forfaits de salaire (03/08) — les boutons qui ECRIVENT dans le champ
   `salaires_tcc`. Ils ne portent aucune valeur propre : ce sont des raccourcis
   de saisie, pas une seconde source. Enroules, jamais de scroll lateral. */
.sal-forfaits { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.sal-forfaits .btn { font-size: 13px; padding: 4px 10px; }
.sal-forfaits .btn b { font-weight: 600; }
.sal-forfaits .muted { font-size: 12px; }

/* ---------------------------------------------------------------------------
   LE MENU D'UNE CARTE-FICHE — EN HAUT À DROITE (03/08, Morgan : « les menus
   vont en haut à droite (compléter la charte et déployer partout) », puis
   « en haut à droite, pas de modifier »).
   Le bouton « Modifier » occupait une ligne pleine en bas de chaque carte —
   43 fois sur Fiches Clients. Il est devenu la première entrée du menu : le
   geste courant reste le plus haut de la liste, il perd sa ligne, pas son rang.
   Posé sur `.card.fv`, le gabarit partagé par Fiches Clients, Fiches salariés
   et Producteurs : une seule règle, trois écrans.
   `position:absolute` dans une carte `position:relative` — le menu ne prend
   AUCUNE place dans le flux, donc il ne pousse pas le contenu et ne casse pas
   la hauteur unique des cartes posée le même jour.
--------------------------------------------------------------------------- */
.card.fv { position: relative; }
.card.fv > .fv-menu { position: absolute; top: 8px; right: 8px; z-index: 3; }
/* L'en-tête garde sa place : sans cette réserve, un titre long passerait sous
   le menu. 44 px = le bouton (32) plus sa marge. */
.card.fv > .entete-bloc { padding-right: 44px; }

/* ---------------------------------------------------------------------------
   EXCEPTION ASSUMÉE À LA CHARTE (03/08, Morgan : « partager la largeur de
   l'écran pour 2 fiches producteur. Sinon c'est illisible. Exception à la
   charte »).
   Une fiche producteur porte des identifiants longs et INSÉCABLES — SIRET,
   IBAN, numéros de licence. La grille standard (colonnes de 300 px) les
   coupait en trois lignes. Ici deux colonnes se partagent la largeur, et se
   replient en une seule sous 900 px : jamais de scroll latéral.
   Nommée `cards-larges` plutôt que rattachée à l'écran : la prochaine liste
   de fiches à identifiants longs réutilisera la même exception au lieu d'en
   inventer une autre.
--------------------------------------------------------------------------- */
.cards.cards-larges { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 900px) { .cards.cards-larges { grid-template-columns: minmax(0, 1fr); } }

/* ---------------------------------------------------------------------------
   CARTES ÉTIRÉES (03/08, Morgan, pour Fiches salariés SEULEMENT) : « 4 fiches
   occupent tout l'espace horizontal pour Titulaire. Et à chaque ligne le
   nombre de cartes remplit l'espace ! »
   La charte utilise `auto-fill`, qui GARDE les colonnes vides à droite : quatre
   titulaires laissaient un blanc sur un large écran. `auto-fit` effondre les
   colonnes inutilisées, donc les cartes présentes se partagent toute la
   largeur — et une ligne de deux remplaçants remplit la sienne pareillement.
   Le plancher de 240 px empêche une carte isolée de s'étirer sur 2 000 px.
--------------------------------------------------------------------------- */
.cards.cards-etirees { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }

/* ---------------------------------------------------------------------------
   LES LIGNES D'UNE CARTE-FICHE — AÉRÉES (03/08, Morgan : « la hauteur n'aurait
   pas dû changer. Conséquence tu aurais pu aérer les infos pour rendre cela
   lisible »).
   Élargir les cartes producteur avait raccourci la carte au lieu de la rendre
   lisible : six identifiants longs (SIRET, APE, licence, TVA, IBAN, barème)
   restaient collés les uns aux autres. On rend l'espace gagné à la lecture.
   `overflow-wrap` sur les seules valeurs : un IBAN qui déborde se coupe plutôt
   que de pousser la carte — jamais de scroll latéral.
--------------------------------------------------------------------------- */
.card.fv > .fv-l { margin: 7px 0; line-height: 1.55; overflow-wrap: anywhere; }
.card.fv > .fv-l + .fv-l { border-top: 1px solid var(--ligne); padding-top: 7px; }
/* Correctif du même jour : une carte peut passer UNE ligne qui en contient
   plusieurs (la fiche producteur groupe SIRET, APE, licence, TVA, IBAN et
   barème dans un seul bloc). La respiration doit donc valoir aussi pour les
   sous-lignes, sinon elle ne se voit nulle part là où elle sert le plus. */
.card.fv > .fv-l > div { margin: 6px 0; line-height: 1.55; overflow-wrap: anywhere; }
.card.fv > .fv-l > div + div { border-top: 1px solid var(--ligne); padding-top: 6px; }
