:root {
  /* 10.13.0 — la HAUTEUR où se pose tout ce qui flotte par-dessus l'application : une question, la
     palette Cmd+K, l'assistant, l'écran de verrouillage. Il y en avait trois (7 %, 12 %, centré) :
     la confirmation « Passer la suite ? » s'ouvrait au-dessus de l'assistant qu'elle concernait, et
     on voyait les questions « une fois au centre, une fois en haut ». Une seule hauteur, un seul
     endroit où le regard les cherche. */
  --haut-fenetre: 10vh;
  --bg: #f5f7fa;
  --panel: #ffffff;
  --border: #e6eaf0;
  /* Le trait d'un bouton discret (`.btn-ghost`) : plus marqué qu'une bordure de carte, moins que
     le texte. Il était utilisé depuis la 9.4.2 et défini NULLE PART (10.12.0) — une variable
     inconnue rend sa déclaration invalide, et la bordure retombait sur la couleur du texte : un
     trait plein en clair, blanc vif en sombre, y compris sur un bouton éteint. */
  --line: #c9d1db;
  --text: #1b2430;
  --muted: #7b8794;
  --primary: #0f9d8f;
  --primary-hover: #0c877b;
  --primary-soft: #e6f5f3;
  --primary-soft-2: #cdeee9;
  --ink-soft: #eef1f5;
  --danger: #d2413b;
  --danger-soft: #fdecea;
  --success: #1c8f5a;
  --success-soft: #e3f6ec;
  --warning: #c26a12;
  --warning-soft: #fff1e3;
  --info: #2a6fd6;
  --info-soft: #e8f0fc;
  --radius: 14px;
  --radius-sm: 9px;
  --shadow: 0 1px 2px rgba(20, 40, 60, .04), 0 6px 20px rgba(20, 40, 60, .05);
}

* { box-sizing: border-box; }
[hidden] { display: none !important; } /* sinon un display:flex de classe l'emporte sur l'attribut hidden (menu « Plus », champ taux, bouton Retirer, palette) */
html, body { margin: 0; height: 100%; }
body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  font-size: 14px; color: var(--text); background: var(--bg);
  -webkit-font-smoothing: antialiased;
}
::selection { background: var(--primary-soft-2); }

#app { display: flex; height: 100vh; }

/* ---------- sidebar ---------- */
.sidebar {
  width: 224px; background: var(--panel); border-inline-end: 1px solid var(--border);
  display: flex; flex-direction: column; padding: 22px 14px; flex-shrink: 0;
}
/* L'en-tête de la barre est le sélecteur d'entreprise (7.14.0) : c'est là que le nom du dossier
   ouvert est écrit, donc c'est là qu'on s'attend à en changer. Avant, il fallait passer par
   Paramètres → Dossiers. Il reste un en-tête tant qu'on ne le survole pas : pas de bordure, pas de
   fond — mais il a un chevron, un curseur de clic et un état au survol, les trois signes qui font
   qu'on essaie de cliquer. */
.brand-wrap { position: relative; padding-bottom: 22px; }
.brand {
  width: 100%; padding: 6px 8px; display: flex; align-items: center; gap: 10px; text-align: start;
  font: inherit; color: inherit; background: transparent; border: 1px solid transparent;
  border-radius: 12px; cursor: pointer;
}
.brand:hover, .brand[aria-expanded="true"] { background: var(--ink-soft); border-color: var(--border); }
.brand-txt { min-width: 0; flex: 1 1 auto; display: block; }
.brand-chev { font-size: 10px; color: var(--muted); flex-shrink: 0; }
.brand-mark { width: 34px; height: 34px; border-radius: 10px; background: linear-gradient(135deg, #12b3a2, #0f9d8f 60%, #0b7f74); color: #fff; display: grid; place-items: center; font-weight: 800; font-size: 15px; letter-spacing: -.5px; box-shadow: 0 4px 12px rgba(15,157,143,.28); flex-shrink: 0; }
.brand-name { font-size: 16px; font-weight: 700; letter-spacing: -.2px; line-height: 1.1; display: block; }
.brand-sub { font-size: 11px; color: var(--muted); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: block; }

/* Le menu des dossiers. Au-dessus de la barre latérale mais sous les fenêtres modales (400) : une
   question posée par-dessus doit rester devant. */
/* 10.12.0 — le menu prenait la largeur de l'en-tête (195 px) et coupait ses propres gestes :
   « Partager cette entre… », « Rejoindre un dossie… », et le nom de l'entreprise ouverte. Rien ne
   le rogne au-dessus de lui : il prend la largeur de son texte, au moins celle de l'en-tête. */
.dos-menu {
  position: absolute; inset-inline-start: 0; top: 100%; margin-top: -14px; z-index: 70;
  min-width: 100%; width: max-content; max-width: min(340px, calc(100vw - 32px));
  background: var(--panel); border: 1px solid var(--border); border-radius: 12px; padding: 6px;
  box-shadow: 0 14px 40px rgba(20, 40, 60, .18); max-height: 60vh; overflow-y: auto;
}
.dos-menu button {
  display: flex; width: 100%; align-items: baseline; gap: 8px; text-align: start; font: inherit;
  background: none; border: none; border-radius: 8px; padding: 8px 10px; cursor: pointer; color: var(--text);
}
.dos-menu button:hover { background: var(--ink-soft); }
.dos-menu button.on { background: var(--primary-soft); color: var(--primary); font-weight: 600; }
.dos-menu .dm-mark { width: 14px; flex-shrink: 0; font-size: 11px; }
.dos-menu .dm-nom { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dos-menu .dm-tag { font-size: 10px; color: var(--muted); flex-shrink: 0; }
.dos-menu hr { border: none; border-top: 1px solid var(--border); margin: 6px 4px; }
.dos-menu .dm-titre { font-size: 10px; text-transform: uppercase; letter-spacing: .8px; color: var(--muted); font-weight: 700; padding: 6px 10px 2px; }
nav { display: flex; flex-direction: column; gap: 2px; overflow-y: auto; }
/* 10.12.0 — la barre de l'app entreprise ne défile QUE quand `ajusterNav` (app.js) l'a décidé, sur
   sa mise en page SANS barre de défilement. En `auto` permanent, un débordement d'UN pixel faisait
   paraître une barre de 10 px (Linux, Windows) ; les libellés perdaient autant de large,
   « Facturation récurrente » passait sur deux lignes, et ces 14 px de plus entretenaient le
   débordement — deux mises en page stables, et l'on tombait sur l'une ou l'autre selon l'ordre du
   dessin. Le Cabinet (sept entrées, un `nav` sans identifiant) n'est pas concerné. */
#nav:not(.deborde) { overflow-y: hidden; }
/* Une barre qui défile doit avoir l'air de défiler. Sur macOS la barre de défilement est masquée tant
   qu'on ne fait pas défiler : la liste paraissait donc finie là où elle était coupée. On la force,
   fine et discrète, mais visible — et seulement quand il y a vraiment de quoi défiler (7.0.0). */
nav.deborde { scrollbar-width: thin; scrollbar-color: #c8d0d9 transparent; }
nav.deborde::-webkit-scrollbar { width: 8px; }
nav.deborde::-webkit-scrollbar-thumb { background: #c8d0d9; border-radius: 4px; border: 2px solid var(--panel); }
nav.deborde::-webkit-scrollbar-thumb:hover { background: #aab3be; }
body.dark nav.deborde::-webkit-scrollbar-thumb { background: #3c4654; }
.nav-group { font-size: 10px; text-transform: uppercase; letter-spacing: 1.2px; color: #aab3be; font-weight: 700; padding: 12px 12px 3px; }
.nav-group:first-child { padding-top: 4px; }
body.dark .nav-group { color: #6c7785; }
/* 10.13.0 — une famille se replie sur son intertitre. L'intertitre devient un BOUTON, et il le dit
   au repos par trois signes (7.14.0) : le chevron, le curseur, et un fond au survol. Replié, il porte
   le compte de ce qui attend dedans (`.ng-count`), à la place exacte des compteurs des entrées. */
.nav-fam, .nav-fam-liens { display: flex; flex-direction: column; gap: 2px; }
button.nav-group {
  display: flex; align-items: center; gap: 6px; width: 100%; margin-top: 8px; padding: 5px 12px 4px 8px;
  background: none; border: none; border-radius: 8px; cursor: pointer; text-align: start; font-family: inherit;
}
.nav-fam:first-child > button.nav-group { margin-top: 0; }
button.nav-group:hover { background: var(--ink-soft); color: var(--muted); }
button.nav-group:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
.nav-group .ng-chev { width: 12px; height: 12px; flex: none; stroke: currentColor; fill: none; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; transform: rotate(90deg); transition: transform .15s; }
.nav-fam.repliee .ng-chev { transform: rotate(0deg); }
.nav-group .ng-nom { flex: 1 1 auto; }
.nav-group .ng-count { text-transform: none; letter-spacing: 0; font-size: 11px; }
@media (prefers-reduced-motion: reduce) { .nav-group .ng-chev { transition: none; } }
nav a {
  color: #4b5563; text-decoration: none; padding: 8px 12px; border-radius: 10px; font-weight: 500;
  display: flex; align-items: center; gap: 10px; transition: background .12s, color .12s;
}
nav a svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; opacity: .8; }
nav a:hover { background: var(--ink-soft); color: var(--text); }
/* L'état actif ne change pas la GÉOMÉTRIE d'une entrée : le fond teinté et la couleur le disent. En
   gras, « Facturation récurrente » passait à la ligne — 34 px au repos, 48 une fois cliquée — et
   tout ce qui la suit descendait de 14 px sous le curseur au moment du clic (10.12.0). */
nav a.active { background: var(--primary-soft); color: var(--primary); }
nav a.active svg { opacity: 1; }
.sidebar-foot { margin-top: auto; display: flex; flex-direction: column; gap: 2px; padding-top: 12px; border-top: 1px solid var(--border); }
.sidebar-foot .btn-ghost { color: var(--muted); text-align: start; justify-content: flex-start; }
.sidebar-foot .btn-ghost:hover { color: var(--text); background: var(--ink-soft); }
/* Paramètres et Aide : mêmes liens que dans nav, mais dans le pied, qui ne défile jamais (7.0.0). */
.sidebar-foot .foot-link {
  color: #4b5563; text-decoration: none; padding: 8px 12px; border-radius: 10px; font-weight: 500;
  font-size: 13px; display: flex; align-items: center; gap: 10px;
}
.sidebar-foot .foot-link svg { width: 17px; height: 17px; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; opacity: .8; }
.sidebar-foot .foot-link:hover { background: var(--ink-soft); color: var(--text); }
.sidebar-foot .foot-link.active { background: var(--primary-soft); color: var(--primary); }
/* La recherche générale, dessinée comme un champ au-dessus de <nav> : elle ne défile jamais, elle
   dit sa touche, et elle est cliquable — trois choses qui manquaient à un raccourci clavier. */
.nav-search {
  font: inherit; font-size: 13px; margin: 0 0 8px; width: 100%; cursor: pointer;
  display: flex; align-items: center; gap: 8px; text-align: start;
  padding: 8px 10px; border-radius: 10px; border: 1px solid var(--border);
  background: var(--ink-soft); color: var(--muted);
}
.nav-search:hover { border-color: var(--primary); color: var(--text); }
.nav-search svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 1.9; stroke-linecap: round; }
.nav-search span { flex: 1; }
.nav-search kbd {
  font: inherit; font-size: 11px; padding: 1px 5px; border-radius: 5px;
  border: 1px solid var(--border); background: var(--panel); color: var(--muted);
}

/* L'état vide qui apprend quelque chose (7.8.0) : à la place d'une phrase grise qui décrit un
   itinéraire, un panneau qui dit à quoi sert la page et porte les vrais boutons. */
.vide-utile { max-width: 720px; }
.vide-utile h2 { margin-bottom: 6px; }
.vide-utile p { line-height: 1.6; color: var(--muted); }

/* ---------- page « Tous les modules » (7.0.0) ---------- */
.mod-row { display: grid; grid-template-columns: 30px 1fr auto; gap: 12px; align-items: start;
  padding: 14px 2px; border-top: 1px solid var(--border); }
.mod-row:first-child { border-top: none; }
.mod-row .mod-t { font-weight: 700; }
/* Une OPTION d'un module se lit sous son module, en retrait : la classe était posée depuis la 9.1.0
   et n'avait aucune règle — « Grand livre, balance, états financiers » se lisait comme un module de
   plus, au même rang que Comptabilité (10.12.0). */
.mod-row.mod-sub { margin-inline-start: 42px; }
.mod-row:not(.on) { opacity: .62; }
.mod-row:not(.on):hover { opacity: 1; }
.mod-check { display: flex; align-items: center; justify-content: center; padding-top: 2px; }
.mod-check input { width: 17px; height: 17px; cursor: pointer; }
.mod-lock { color: var(--primary); font-size: 14px; cursor: help; }
/* Dans l'assistant, la liste doit tenir en entier sous la question : six modules, la phrase du
   dessus et celle du dessous. Sinon le choix se fait à moitié, et la phrase qui dit qu'on ne perd
   rien — la seule qui rende le tri acceptable — reste sous la coupe. */
.mods-serre .mod-row { padding: 9px 2px; grid-template-columns: 26px 1fr; gap: 10px; }
.mods-serre .mod-row .small { line-height: 1.35; }
.mt-s { margin-top: 3px; }

/* Le bandeau du jeu d'exemple (7.0.0) : il doit se remarquer sans faire peur, et ne jamais
   ressembler à une alerte d'erreur — ce n'est pas un problème, c'est un mode. 10.14.0 : un bac à
   sable LILAS, une couleur qu'aucun statut n'emploie. L'orange était celui des avertissements :
   posé en haut de chaque page, il disait « attention » à quelqu'un qu'on invitait à jouer. */
.banner.demo-banner {
  background: #f1ecff; color: #4d3399; border: 1px solid #ddd1fb;
  align-items: center; line-height: 1.45; font-weight: 500; justify-content: flex-start;
}
/* `.banner.demo-banner`, deux classes : `.banner` (une classe, écrite 650 lignes plus bas) repeignait
   le bandeau en orange avertissement — le lilas n'a JAMAIS été appliqué avant la 10.14.0, et seule
   une capture l'a montré. La règle générale gagne toujours (7.23.0, 7.27.0, 7.30.0) : on mesure. */
body.dark .banner.demo-banner { background: #2a2340; color: #d2c4ff; border-color: #43386b; }
.demo-banner b { font-weight: 700; }
.demo-banner .db-ico svg { width: 22px; height: 22px; display: block; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.demo-banner .db-txt { flex: 1 1 360px; min-width: 0; }
.demo-banner .db-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.banner.demo-banner .btn:not(.btn-primary):not(.btn-danger) { background: #fff; color: #4d3399; border-color: #cdbdf7; white-space: nowrap; }
body.dark .banner.demo-banner .btn:not(.btn-primary):not(.btn-danger) { background: #362c55; color: #e2d8ff; border-color: #564a85; }
/* 10.14.1 — le RAPPEL, sur une ligne, hors de la page d'accueil de chaque application : la phrase
   entière s'y lit une fois ; répétée sur trois lignes en tête de chaque écran, elle poussait l'écran
   de travail sous le bas d'un portable. Trois classes : `.banner.demo-banner` en a deux. */
.banner.demo-banner.court { padding-block: 6px; margin-bottom: 12px; }
.demo-banner.court .db-ico svg { width: 18px; height: 18px; }

/* Le repère « bêta » (7.25.0). Discret mais permanent : il vit dans le nom de l'application, en
   haut à gauche, et dans l'écran des mises à jour. Il ne doit pas crier — une bêta est un mode
   normal du logiciel, pas une erreur — mais il ne doit jamais passer inaperçu non plus. */
.beta-tag {
  display: inline-block; vertical-align: middle;
  font-size: 9px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
  padding: 1px 5px; border-radius: 4px; line-height: 1.5;
  background: #ede0ff; color: #5b2ea6; border: 1px solid #d6bcf5;
}
body.dark .beta-tag { background: #35255a; color: #cdb0f5; border-color: #4c3878; }
.mod-banner { align-items: center; }

/* ---------- « Tes premiers pas » (7.0.0) ---------- */
/* Il remplace, tant qu'il y a des étapes à faire, les quatre compteurs à zéro et le graphique de
   douze mois vides qui occupaient 548 px avant la première phrase utile. */
.premiers-pas h2 { display: flex; align-items: center; gap: 8px; }
.pp-compte { background: var(--primary-soft); color: var(--primary); border-radius: 999px; padding: 1px 8px; font-size: 11px; }
.pp-list { list-style: none; margin: 0; padding: 0; counter-reset: pp; }
.pp-list li {
  display: grid; grid-template-columns: 26px 1fr auto; gap: 12px; align-items: center;
  padding: 11px 2px; border-top: 1px solid var(--border);
}
.pp-list li:first-child { border-top: none; }
.pp-txt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pp-marque {
  width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--border);
  display: grid; place-items: center; font-size: 12px; font-weight: 800; color: transparent;
}
.pp-list li.fait .pp-marque { background: var(--primary); border-color: var(--primary); color: #fff; }
.pp-list li.fait .pp-txt strong { color: var(--muted); text-decoration: line-through; text-decoration-color: var(--border); }
.pp-list li.fait .pp-txt .small { display: none; }         /* une étape faite n'a plus besoin d'être expliquée */
.pp-list li.encours .pp-marque { border-color: var(--primary); }
/* La pastille de l'étape en cours déborde de 8 px de chaque côté pour respirer — SANS élargir la
   liste (10.14.1). Posée par une marge négative, elle rendait la liste 8 px plus large que sa
   colonne : un débordement horizontal que `e2e:cabinet-jour1` mesurait sur chaque écran qui la
   montre. Une ombre ne compte pas dans ce qui défile, et le découpage la borne à l'horizontale. */
.pp-list li.encours { background: var(--primary-soft); border-radius: 10px;
  box-shadow: 0 0 0 8px var(--primary-soft); clip-path: inset(0 -8px round 10px); }
.pp-list li.encours + li { border-top-color: transparent; }
/* Une étape FACULTATIVE (la découverte, le comptable) le dit à côté de son titre, en gris : elle
   attend sans presser (10.14.0). */
.pp-facult { display: inline-block; margin-inline-start: 6px; vertical-align: 1px; font-size: 10.5px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .5px; color: var(--muted); background: var(--ink-soft); border-radius: 999px; padding: 1px 7px; }

/* Un lien DANS une phrase ou dans une cellule (10.12.0) : aucune règle ne les stylait, et « la
   trésorerie » d'une phrase de la Paie, « Voir les relances » des Statistiques, le numéro de facture
   de la page Relances gardaient le bleu brut du navigateur (rgb(0, 0, 238)) — lisible en clair par
   chance, illisible en sombre, et étranger à tout le reste de l'écran. La sonde de contraste ne
   regardait que les boutons et les champs : elle regarde maintenant les liens, et le bleu brut est
   une faute à lui seul. Un lien qui porte une classe a déjà la sienne. Le Cabinet en profite : c'est
   la règle de son `a.lien-aide`, pour tous. */
#view a[href]:not([class]), #modal-root a[href]:not([class]) { color: var(--primary); font-weight: 600; text-decoration: underline; text-underline-offset: 2px; }
#view a[href]:not([class]):hover, #modal-root a[href]:not([class]):hover { text-decoration-thickness: 2px; }

/* Le lien vers l'article d'aide de la page où l'on est. */
.help-link { color: var(--primary); text-decoration: none; font-weight: 600; }
.help-link:hover { text-decoration: underline; }
.todo-more { margin-top: 6px; }

/* Le bandeau d'une pièce émise (7.0.0). Avant, c'était une ligne grise de 12 px sous le titre, et la
   seule sortie était cachée dans « Plus ▾ » : on cliquait dans vingt champs gris sans qu'il se passe
   rien, sans message et sans erreur, et on en concluait que l'application était cassée. */
.lock-banner { align-items: center; line-height: 1.5; font-weight: 500; }
.lock-banner b { font-weight: 800; }
.lock-banner .lock-go { display: flex; align-items: center; gap: 8px; white-space: nowrap; }
.lock-banner .lock-go .small { color: var(--muted); cursor: help; }
/* Le fond blanc n'est là que pour le bouton NEUTRE : sur un bandeau teinté, un `.btn` gris paraît
   sale. Un bouton qui porte déjà sa couleur (primaire, danger) doit en être exclu — sinon on lui
   repeint le fond en blanc en lui laissant son texte blanc, et il devient un rectangle vide.
   C'est ce qui est arrivé à « Corriger par un avoir… » : mesuré dans l'application, 155 × 32 px de
   blanc sur blanc, la seule sortie offerte par le bandeau d'une facture émise. */
.lock-banner .btn:not(.btn-primary):not(.btn-danger) { background: #fff; }
body.dark .lock-banner .btn:not(.btn-primary):not(.btn-danger) { background: rgba(255,255,255,.08); }

/* Le champ que la validation vient de refuser (7.0.0) : on l'amène à l'écran, on y met le curseur,
   et on le marque — le message seul obligeait à relire tout le formulaire pour trouver lequel. */
.champ-faute > input, .champ-faute > textarea, .champ-faute > select,
.champ-faute .combo-btn, .champ-faute .d-in, input.champ-faute, .combo.champ-faute .combo-btn {
  border-color: var(--danger) !important;
  box-shadow: 0 0 0 3px var(--danger-soft);
}
.champ-faute > .fl, .champ-faute > span:first-child { color: var(--danger); }
/* Un choix fait en cliquant une carte (le métier, dans l'assistant) n'a pas de champ à rougir :
   c'est la grille entière qui est refusée. Elle se redessine au premier clic, donc la marque part
   d'elle-même. */
.act-grid.champ-faute { border-radius: 12px; box-shadow: 0 0 0 2px var(--danger), 0 0 0 5px var(--danger-soft); }
/* Une valeur DOUTEUSE sans être refusée (un RIB dont la clé ne tombe pas, 10.12.0) : orange, pas
   rouge — on prévient, on ne bloque pas. `!important` comme `.champ-faute` : une marque de refus ou
   de doute doit gagner contre TOUT habillage de conteneur (grille, ligne, en-tête), pas seulement
   contre la règle générale des champs — qui ne pèse plus rien depuis la 10.14.1. */
input.champ-doute { border-color: var(--warning) !important; }

/* Ce qui est obligatoire se voit AVANT d'appuyer sur « Enregistrer ». L'attribut `required` posé sur
   un <input> d'une fenêtre modale est inerte : rien ne soumet le formulaire, c'est un bouton qui
   lit les valeurs. On le dit donc nous-mêmes — une étoile rouge, et une légende en pied. */
.field.obligatoire > span:first-child::after, .field.obligatoire > .fl::after {
  content: ' *'; color: var(--danger); font-weight: 700;
}
.oblig-note { font-size: 12px; color: var(--muted); margin-inline-end: auto; align-self: center; }
/* La numérotation continue (10.14.0) : la dernière pièce émise ailleurs, et la raison d'un refus
   sur la ligne, en orange — on prévient pendant la frappe, on refuse à l'enregistrement. */
#num-table .num-derniere, #nf .num-derniere { width: 110px; }
/* Les colonnes se décident sur l'en-tête, jamais sur ce qu'on tape : un numéro plus long ne doit pas
   déplacer le champ voisin sous le curseur. */
#num-table { table-layout: fixed; }
#num-table th:first-child { width: 28%; }
#num-table th:nth-child(2) { width: 190px; }
/* Le préfixe d'un numéro se lit d'un bloc : « FAC-2026- » passait sur deux lignes à côté du champ,
   qui prenait toute la largeur du flex. */
.num-prefixe { white-space: nowrap; flex: none; }
.num-motif { color: var(--warning); }
/* La place du motif est réservée : il apparaît sans rien pousser. */
#num-motif, #nf-motif { min-height: 1.5em; margin-block: 4px 6px; }
.oblig-note b { color: var(--danger); }

/* Le lien d'une bulle vers l'article qui développe. */
#info-pop .ip-more { margin: 10px 0 0; padding-top: 8px; border-top: 1px solid var(--border); }
#info-pop .ip-more a { color: var(--primary); font-weight: 600; text-decoration: none; }
#info-pop .ip-more a:hover { text-decoration: underline; }

/* ---------- main ---------- */
/* `--main-haut` : ce qu'une barre COLLANTE doit remonter pour coller au bord de la fenêtre. Un
   `top: 0` colle sous le padding du conteneur qui défile, et les lignes du tableau passaient
   au-dessus de la barre, dans ces 32 px (10.12.0, U-06). */
/* 10.14.0 — un en-tête de tableau collant (`top: 0`) colle sous la marge haute du conteneur, à 32 px :
   un défilement qui posait le tableau AU BORD (une visite, un raccourci vers un panneau) le faisait
   recouvrir la première ligne — celle qu'on voulait montrer (vu à l'écran : le 📎 de « Le trombone »).
   Les défilements s'arrêtent donc à la même marge que le contenu. */
main { --main-haut: 32px; flex: 1; overflow-y: auto; padding: var(--main-haut) 36px 48px; scroll-padding-block-start: var(--main-haut); }
/* La hauteur d'un CHAMP (38 px), pas celle d'un bouton (35) : la liste d'années n'existe que sur
   certains onglets (Marges, Paie…), et l'en-tête grandissait de 3 px quand elle paraissait — les
   onglets descendaient sous le curseur au moment du clic (10.12.0, un état actif ne change pas la
   géométrie). */
.page-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 22px; gap: 12px; flex-wrap: wrap; min-height: 38px; }
.page-head h1 { margin: 0; font-size: 24px; font-weight: 700; letter-spacing: -.4px; }
/* La barre d'actions revient à la ligne plutôt que de sortir de la fenêtre (7.13.0). Sur l'éditeur
   d'un devis facturable elle porte jusqu'à neuf commandes : à 1280 px, « Émettre la facture » —
   le bouton principal — était coupé, et toute la page avait une barre de défilement horizontale.
   Mesuré dans l'application (scrollWidth > clientWidth), pas déduit. */
.page-head .actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; justify-content: flex-end; }
/* Un `select` hérite de `width: 100%` de la règle générale des champs de formulaire. Dans une barre
   d'actions — qui est un conteneur FLEX — chacun réclame donc toute la ligne : les trois sélecteurs
   de la page Statistiques s'empilaient sur trois rangées, étirés d'un bord à l'autre, et l'en-tête
   occupait un tiers de l'écran. `.filters` avait déjà son `width: auto` depuis longtemps ; les
   barres d'actions ne l'avaient jamais eu. Quatre pages étaient concernées : Marges,
   Immobilisations, Statistiques et Comptabilité.
   Le `max-width` empêche un libellé long (« 1ᵉʳ trimestre · janv.–mars ») de reprendre toute la place. */
.page-head .actions select,
.page-head .actions input:not([type=checkbox]):not([type=radio]) {
  width: auto; max-width: 280px; background: var(--panel);
}

.panel { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); padding: 22px 24px; margin-bottom: 18px; box-shadow: var(--shadow); }
/* Un lien qui promet un réglage précis l'amène et le désigne : sans repère, on atterrit au milieu
   d'une pile de panneaux identiques et on cherche le titre à la main. */
.panel.flash { border-color: var(--primary); box-shadow: 0 0 0 3px rgba(15, 157, 143, .18); }
/* ---------- l'échelle de titres (9.4.3) ----------
   Avant, UN seul style de titre de section existait dans les deux applications : 11 px, gris,
   en capitales, `letter-spacing: 1.2px`. Il donnait exactement le même poids à « COMPTABILITÉ »
   qu'à « ABONNEMENTS », et il était recopié à l'identique sept fois dans cette feuille sous le nom
   `.k-label` — un mécanisme recopié diverge toujours (7.29.0).

   Un titre gris de 11 px ne hiérarchise rien : il décore. Trois niveaux, un rôle chacun :
     h1         la PAGE                        24 px, couleur du texte
     .panel h2  la SECTION d'une page          15,5 px, couleur du texte
     .eyebrow   la SUR-ÉTIQUETTE en capitales  11 px, grise — au-dessus d'un chiffre, jamais
                comme titre : « CHIFFRE D'AFFAIRES » posé sur « 40 077,900 DT » est une unité de
                lecture, pas une section.
   Un titre de section est donc lisible de loin, et une capitale grise ne sert plus qu'à ce pour
   quoi elle est bonne. */
.panel h2 {
  margin: 0 0 14px; font-size: 15.5px; font-weight: 650; letter-spacing: -.15px;
  color: var(--text); text-transform: none;
}
/* Le second niveau DANS un panneau (« Les touches », « Le brouillard ») : il doit se distinguer du
   titre de la section sans redevenir un titre de page. */
.panel h3 { margin: 22px 0 10px; font-size: 13.5px; font-weight: 650; letter-spacing: -.1px; color: var(--text); }
.panel h3:first-child { margin-top: 0; }
/* 10.12.0 — une hauteur de ligne FIXE, celle d'une bulle « i » (15 px, relevée d'un pixel). Sans
   elle, la carte qui n'a pas de bulle avait une étiquette de 12 px contre 15 à ses voisines, et son
   chiffre montait de 3 px : « Coût des ventes » plus haut que « Chiffre d'affaires » et « Marge
   totale », sur la même rangée. Une rangée de cartes se lit comme une ligne. */
.eyebrow, .k-label, .stat .lbl {
  font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: 1px; font-weight: 700;
  line-height: 16px;
}

.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 14px 20px; }
.grid-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 14px 20px; }
.span-2 { grid-column: span 2; }
/* 10.12.0 — une annonce qui se récrit pendant la frappe (« Sortent 10 : il en restera 30 ») passe
   d'une ligne à trois : sans hauteur réservée, la fenêtre se recentrait et « Enregistrer » bougeait
   sous le curseur à chaque chiffre. Ce qui apparaît ne pousse rien. */
.annonce-stable { min-height: 3lh; font-size: 12px; line-height: 1.4; }
/* 10.14.0 — une annonce qui peut devenir un ENCADRÉ (les points à régler de « Clôturer jusqu'à… »)
   réserve la hauteur de l'encadré de trois lignes, marges comprises : 3lh ne comptait que le texte, et
   la fenêtre grandissait de 13 px au moment où l'on choisissait le mois. */
.annonce-stable.encadre { min-height: 84px; margin-block-start: 10px; } /* 3 × 13 px × 1,55 + 22 px de marges */
/* Une phrase et le geste qui la lève, dans une rangée qui passe à la ligne sans coller le bouton
   sous le texte (T-56, 9.8.8). Partagée par les deux applications (10.14.1). */
.ctrl-geste { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 10px; }
.annonce-stable.encadre > .warn-box, .annonce-stable.encadre > .ok-box, .annonce-stable.encadre > p { margin: 0; }
/* Toute la ligne d'une grille, quel que soit son nombre de colonnes. La classe était posée par le
   formulaire d'un contrat récurrent et n'existait nulle part : « Notes sur la facture » tenait dans
   un tiers de la fenêtre, et « Contrat actif… » sur quatre lignes à côté (10.12.0). */
.span-3 { grid-column: 1 / -1; }

/* ---------- forms ---------- */
/* .field (et non label.field) : les champs date et les listes déroulantes sont des <div>,
   un <label> y renverrait le clic du bouton calendrier vers le champ texte. */
.field { display: flex; flex-direction: column; gap: 5px; font-size: 12px; color: var(--muted); font-weight: 600; }
/* Le gras de `.field` est celui de l'ÉTIQUETTE. Une phrase posée dans un champ (« Ce que tu as en
   rayon aujourd'hui… », la remarque sous un bloc) en héritait, et se lisait comme un second libellé
   (10.12.0, parcours d'une entreprise). Une phrase reste une phrase. */
.field p { font-weight: 400; }
/* La remarque d'un RIB douteux (10.12.0) : dans la ligne du libellé, jamais dessous — ce qui apparaît
   selon une valeur vit dans une place qui existe déjà. Vide, elle ne prend aucune place. */
.rib-note { color: var(--warning); margin-inline-start: 6px; }
.rib-note:empty { display: none; }
/* Énuméré type par type, ce sélecteur oubliait `search` : cinq listes affichaient donc une petite
   boîte native, non dimensionnée, dont le texte était coupé au milieu d'un mot — à côté de six
   listes correctement stylées. On exclut ce qui doit garder son apparence native plutôt que
   d'énumérer ce qui doit être stylé : le prochain type d'input sera juste.
   10.14.1 — la chaîne vit dans un `:where()`, qui ne pèse RIEN. Écrite nue en 7.9.0, elle avait fait
   passer cette règle de 0,1,1 (`input[type=text]`) à 0,4,1 : toute règle de champ posée dans un
   conteneur a perdu contre elle, en silence, dans les deux applications — vingt-six règles, dont
   le rembourrage des cases de nombre des lignes (le prix d'un achat de 4 500 DT s'affichait
   « 4500,00 », coupé), la place du bouton « Afficher » d'un mot de passe, la police à chasse fixe
   d'une clé de licence, le champ de la palette, la bordure verte d'un champ qui a le curseur. On
   l'avait rattrapée neuf fois, règle par règle, à coups de `:not()` recopiés (7.23.0, 7.27.0,
   7.30.0, 9.4.4, 10.12.0…). La règle commune est une valeur PAR DÉFAUT : elle pèse désormais ce
   que pèse `select` ou `textarea` (0,0,1), et n'importe quelle classe la corrige. Un test calcule
   la spécificité de chaque règle de champ des deux feuilles et tombe si l'une d'elles perd. */
input:where(:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=range])), select, textarea {
  font: inherit; font-size: 14px; color: var(--text); padding: 9px 11px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); background: #fbfcfd; width: 100%; transition: border-color .12s, box-shadow .12s, background .12s;
}
input:hover, select:hover, textarea:hover { border-color: #d5dbe3; }
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--primary); background: #fff; box-shadow: 0 0 0 3px rgba(15,157,143,.15); }
textarea { resize: vertical; min-height: 68px; }
/* Deux champs posés l'un sous l'autre dans une fenêtre, sans grille : sans écart, le libellé du second
   se collait à la case du premier (« Rôle » sous « Nom », 0 px — vu à la souris, 10.14.1). Les marges
   se fondent avec celle de ce qui précède : sous un titre, rien ne s'ajoute. */
.modal > * + .field { margin-block-start: 14px; }
/* Une valeur courte se lit en gras dans sa case (le libellé est gris, la valeur ressort) ; un TEXTE
   long — le message d'une relance, des notes, des conditions — se lit mal en gras sur dix lignes.
   Une zone de texte d'un formulaire reprend la graisse de la lecture (10.14.1, vu dans la relance). */
.field textarea { font-weight: 400; line-height: 1.5; }
input.num { text-align: end; font-variant-numeric: tabular-nums; }
/* Un sélecteur de couleur étiré sur toute la largeur du champ ressemble à un trait de séparation,
   pas à un bouton qu'on clique. Il garde la taille d'une pastille. (Il était étiré sur 537 px tant
   que la règle commune pesait 0,4,1 — mesuré dans l'application ; elle ne pèse plus rien.) */
input[type=color] {
  height: 40px; width: 72px; padding: 4px; cursor: pointer;
}
.inline { display: flex; gap: 8px; align-items: center; }
/* Un sélecteur dans une rangée de contrôles prend sa largeur, pas toute la ligne : la règle générale
   des champs (width:100 %) étirait le mois du paquet sur 1 078 px (10.12.0 — même faute que les
   barres d'en-tête en 7.23.0). */
.inline > select { width: auto; }
/* `.check` est un conteneur FLEX : chaque morceau de texte y devient un élément séparé de 8 px, et
   se replie tout seul quand la place manque. Le libellé d'une case tient donc dans UN élément
   (10.12.0) — « Timbre fiscal (1,000 DT) » s'écrivait en quatre morceaux sur deux lignes, la
   parenthèse ouvrante seule sous le mot « Timbre ». Un test le tient dans les deux applications. */
.check { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text); font-weight: 500; }
.check input { accent-color: var(--primary); width: 16px; height: 16px; }
/* Un choix à deux valeurs posé sur une ligne qui ne bouge pas (10.12.0, Seuil de rentabilité) : la
   ligne garde sa place quoi qu'on choisisse. Les boutons radio restent de vrais champs (clavier,
   flèches, étiquette) : ils sont seulement redessinés, jamais retirés. Le choix actif se dit par le
   fond ET la couleur, sans gras — un état actif ne change pas la géométrie. */
.fv-liste { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 32px; }
.fv-ligne { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding-block: 6px; border-bottom: 1px solid var(--border); font-size: 13px; }
.choix2 { display: inline-flex; flex: none; border: 1px solid var(--border); border-radius: 8px; padding: 2px; gap: 2px; }
.choix2 label { position: relative; cursor: pointer; }
.choix2 input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.choix2 label span { display: block; padding: 3px 10px; border-radius: 6px; font-size: 12px; color: var(--muted); }
.choix2 input:checked + span { background: var(--primary-soft); color: var(--primary); }
.choix2 input:focus-visible + span { outline: 2px solid var(--primary); outline-offset: 1px; }
@media (max-width: 1100px) { .fv-liste { grid-template-columns: minmax(0, 1fr); } }

/* ---------- buttons ---------- */
.btn {
  font: inherit; font-size: 13px; font-weight: 600; padding: 9px 15px; border-radius: 10px; border: 1px solid transparent;
  cursor: pointer; background: var(--ink-soft); color: var(--text); white-space: nowrap; display: inline-flex; align-items: center; gap: 6px;
  transition: background .12s, transform .05s, box-shadow .12s;
}
.btn:hover { background: #e3e8ee; }
.btn:active { transform: translateY(1px); }
.btn-primary { background: var(--primary); color: #fff; box-shadow: 0 4px 12px rgba(15,157,143,.25); }
.btn-primary:hover { background: var(--primary-hover); }
.btn-danger { background: var(--danger-soft); color: var(--danger); }
.btn-danger:hover { background: #f9d7d3; }
/* Un bouton se reconnaît AU REPOS, jamais au survol : personne ne survole ce qu'il ne voit pas.
   `btn-ghost` n'avait ni fond ni bordure, et son texte était celui du corps : « Voir » sur chaque
   ligne de « À faire », « Exporter en CSV », « Enregistrer ma clé… » se lisaient comme du gras.
   C'est le défaut nommé en Cabinet 1.0.0 — « elle est vide », disait Skander devant un bouton
   parfaitement fonctionnel — et il vivait encore dans neuf boutons, mesurés par
   `npm run e2e:cabinet-jour1`. Une bordure discrète suffit : le bouton reste au second plan, mais
   il existe. */
.btn-ghost { background: transparent; border-color: var(--line); }
.btn-ghost:hover { background: var(--ink-soft); }
/* Les deux endroits où un `btn-ghost` est une ENTRÉE DE MENU et non un bouton : une bordure y
   dessinerait un cadre au milieu d'une liste. Ils portent déjà l'autre signe — une couleur qui
   n'est pas celle du texte courant — et c'est ce que le contrôle exige. */
.sidebar-foot .btn-ghost, .setup-foot .btn-ghost { border-color: transparent; }
.btn-sm { padding: 6px 11px; font-size: 12px; border-radius: 8px; }
.btn:disabled { opacity: .5; cursor: not-allowed; }
/* Un bouton principal ÉTEINT ne garde pas sa couleur (10.12.0, vu au test humain) : à 50 %
   d'opacité, un vert d'eau plein restait le bloc le plus visible de l'écran, et en thème sombre il
   se lisait comme un bouton ordinaire qu'on peut cliquer. Il prend l'allure de n'importe quel bouton
   éteint ; la raison, elle, est écrite à côté (9.4.5). Posé APRÈS les deux thèmes, et pour les
   deux applications, qui partagent cette feuille. */
.btn-primary:disabled, .btn-primary:disabled:hover,
body.dark .btn-primary:disabled, body.dark .btn-primary:disabled:hover { background: var(--panel); color: var(--muted); border-color: var(--border); box-shadow: none; }

/* ---------- tables ---------- */
/* overflow: clip (et non hidden) : les coins restent arrondis, mais la table ne devient pas
   une zone de défilement, ce qui laisse l'en-tête collant fonctionner sur les longues listes. */
table.list { width: 100%; border-collapse: separate; border-spacing: 0; background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); overflow: clip; box-shadow: var(--shadow); }
table.list th { text-align: start; font-size: 11px; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); padding: 12px 16px; background: #fafbfc; border-bottom: 1px solid var(--border); font-weight: 700; }
table.list thead th { position: sticky; top: 0; z-index: 2; }
table.list td { padding: 13px 16px; border-bottom: 1px solid var(--border); vertical-align: middle; }
table.list tr:last-child td { border-bottom: none; }
table.list tr.clickable { cursor: pointer; }
table.list tr.clickable:hover td { background: #f7fbfa; }
td.r, th.r { text-align: end; font-variant-numeric: tabular-nums; }
/* Une cellule qui tient sur UNE ligne, le texte entier dans son infobulle (9.4.5). Le livre-journal
   du Cabinet faisait 5 888 px pour dix-sept pièces : ce n'était pas le nombre de lignes, c'était
   chaque ligne repliée sur trois, parce qu'un libellé de facture porte le nom du client. Un journal
   se PARCOURT — on y cherche une pièce — et une colonne dont le texte revient à la ligne triple la
   hauteur de toutes les autres. Le texte complet reste lisible au survol et intact à l'export. */
/* Un champ qui compte dans une unité le DIT à côté de lui (9.4.8). « Jour de relance : 10 » ne
   disait pas dix quoi — dix jours ? le 10 ? — et c'est la date qui déclenche les relances de tout
   un portefeuille. Le mot encadre le champ au lieu de flotter en légende dessous, parce qu'une
   légende sous un champ se lit après l'avoir rempli. */
.suffixe { display: inline-flex; align-items: center; gap: 7px; }
/* 10.14.1 — la largeur compte AUSSI la marge intérieure (11 px de chaque côté) et les flèches du
   champ de nombre. `5.5ch` ne se voyait pas tant que la règle commune gagnait (NUM-01) ; une fois
   rendue à ce champ, elle affichait « 1 » pour le 10 : le zéro passait sous les flèches. */
.suffixe input { width: calc(2ch + 46px); text-align: center; }
.suffixe-av, .suffixe-ap { color: var(--muted); font-size: 13px; white-space: nowrap; }

td.tronq { max-width: 190px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Un morceau de texte coupé DANS une cellule (une adresse email sous un téléphone) : la cellule
   garde ses deux lignes, le morceau garde la sienne (10.14.0). */
/* Le nom d'un tiers est ce qu'on lit d'abord : « Ahmed Ben Amor » se cassait sur trois lignes à
   côté de colonnes insécables (10.14.0). */
td.nom-tiers { min-width: 120px; }
.ellipse { display: inline-block; max-width: 18ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }
td.tronq.lg { max-width: 320px; }
/* Un piège de SPÉCIFICITÉ, vieux comme les tableaux de l'application. `table.list th` (une classe,
   deux éléments) l'emporte sur `th.r` (une classe, un élément) : l'en-tête restait donc aligné à
   GAUCHE au-dessus d'une colonne de chiffres alignés à DROITE. « En stock » finissait 80 px à
   gauche de sa propre valeur, et on lisait la ligne de travers. 153 en-têtes étaient concernés,
   dans les deux applications — elles partagent cette feuille.
   On ne peut pas le voir en relisant `<th class="r">` : il faut MESURER dans l'application réelle,
   ce que fait `npm run e2e:colonnes`. */
table.list th.r, table.lines-edit th.r, table.mini th.r { text-align: end; }
table.lines-edit th.c, table.list th.c, table.mini th.c { text-align: center; }
.nw { white-space: nowrap; }
td.actions { white-space: nowrap; text-align: end; }
/* Deux boutons voisins ne doivent pas dépendre d'une espace dans le gabarit pour être séparés :
   `${cond ? '<button…>' : ''}<button…>` les collait l'un à l'autre, et ça ne se voit qu'à l'écran.
   La règle ne visait que `td.actions` — et deux boutons dans une cellule qui ne porte PAS cette
   classe restaient séparés par la seule espace du gabarit, 3,6 px (« PDF » et « Modifier » de la
   page Paie). Un correctif qui dépend d'une classe qu'on pense à mettre n'est pas un correctif :
   il vise maintenant TOUTE cellule, et nomme les deux grappes qui décident de leur propre densité
   — le menu de ligne (`gap: 4px`) et les pictogrammes de l'éditeur, juste en dessous. */
td:not(.row-actions):not(.line-tools) .btn + .btn { margin-inline-start: 6px; }
.empty { padding: 48px 24px; text-align: center; color: var(--muted); background: var(--panel); border: 1px dashed #d5dbe3; border-radius: var(--radius); }
/* Le même état vide, DANS un panneau (9.4.5/9.4.6). 48 px de haut et un cadre pointillé, c'est la
   bonne présence quand la page entière est vide — c'est le premier écran, il faut qu'il occupe la
   place. Au milieu d'une fiche déjà pleine, le même bloc consacrait 250 px à « aucune relance
   enregistrée » et repoussait tout le reste : un état vide secondaire s'annonce, il ne se
   contemple pas. */
.empty.mini { padding: 14px 16px; text-align: start; border: none; background: transparent; }
/* Dans un panneau, l'annonce s'aligne sur le titre qu'elle suit : décalée de 16 px, elle paraissait
   appartenir à autre chose (10.12.0). */
.panel > .empty.mini { padding-inline: 0; }

/* ---------- badges ---------- */
/* Une pastille est UN mot, elle ne se coupe jamais (10.12.0, vu au test humain) : « en attente » passait
   sur deux lignes dans sa pilule, au fond de la colonne Réponse de la Révision — une pastille cassée
   se lit comme un défaut d'affichage, pas comme un état. La liste des dossiers le disait pour elle
   seule depuis la 9.4.4 ; c'est la règle de toutes les pastilles. */
.badge { display: inline-block; padding: 3px 10px; border-radius: 999px; font-size: 11px; font-weight: 600; background: var(--ink-soft); color: var(--muted); white-space: nowrap; }
.badge.envoyé, .badge.envoyée { background: var(--info-soft); color: var(--info); }
.badge.accepté, .badge.payée { background: var(--success-soft); color: var(--success); }
.badge.refusé, .badge.annulée { background: var(--danger-soft); color: var(--danger); }
.badge.retard, .badge.partielle { background: var(--warning-soft); color: var(--warning); }
.badge.émis { background: var(--info-soft); color: var(--info); }
.status-cell { display: flex; align-items: center; gap: 8px; min-height: 40px; }
.lock-note { margin-top: 4px; }
.page-head > div > h1 { margin: 0; }

/* ---------- paiements ---------- */
.pay-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; margin-bottom: 14px; }
.pay-grid > div { background: var(--bg); border-radius: 12px; padding: 12px 14px; }
.pay-grid .v { font-size: 18px; font-weight: 700; margin-top: 4px; font-variant-numeric: tabular-nums; }
.pay-grid .v.due { color: var(--warning); }
.pay-grid .v.ok { color: var(--success); }
table.list.compact th { padding: 9px 12px; }
table.list.compact td { padding: 9px 12px; font-size: 13px; }
table.list tr.total-row td { background: var(--bg); }
.scroll-x { overflow-x: auto; }
.mb { margin-bottom: 12px; }
/* Un champ fermé se lit fermé partout. Sur une facture émise, les champs texte gardaient le fond et
   l'encre d'un champ qu'on peut modifier, pendant que les listes voisines se grisaient — la même
   pièce disait « modifiable » et « fermé » (10.12.0). La chaîne de `:not()` est gardée NUE ici,
   exprès, alors que la règle commune ne pèse plus rien (10.14.1) : elle fait peser cette règle plus
   lourd que n'importe quel habillage de conteneur (une ligne de facture, une grille de saisie) —
   c'est le seul état qui doive gagner contre eux. */
input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=range]):disabled,
select:disabled, textarea:disabled { background: #f3f5f7; color: #5b6673; cursor: not-allowed; }

/* ---------- dashboard ---------- */
.stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; margin-bottom: 20px; }
.stat { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); padding: 18px 20px; box-shadow: var(--shadow); position: relative; overflow: hidden; }
.stat::before { content: ""; position: absolute; inset-inline-start: 0; top: 0; bottom: 0; width: 4px; background: var(--primary); opacity: .9; }
.stat:nth-child(2)::before { background: #2a6fd6; }
.stat:nth-child(3)::before { background: #c26a12; }
.stat:nth-child(4)::before { background: #7c5cd6; }
.stat .val { font-size: 22px; font-weight: 700; margin-top: 6px; letter-spacing: -.4px; font-variant-numeric: tabular-nums; }
/* L'écart passe sous le montant quand la carte est étroite, mais jamais coupé en deux. */
.stat .val { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; }
.stat .sub { font-size: 12px; color: var(--muted); margin-top: 3px; }
/* 10.12.0 — une rangée de cartes se lit comme UNE ligne : l'étiquette, le chiffre et le
   commentaire de chaque carte partagent les pistes de leurs voisines (subgrid). La hauteur de
   ligne fixe de l'étiquette réglait le cas d'une carte sans bulle « i » (3 px) ; elle ne pouvait
   rien contre une étiquette qui passe sur DEUX lignes à 1280 px : « Délai moyen de paiement »
   descendait son chiffre de 16 px sous celui de « Facturé HT », sur onze écrans que la sonde de
   rangée a mesurés. Une carte compte trois éléments au plus (étiquette, chiffre, commentaire) ; la
   variante `.rangee` du Cabinet pose les siens sur une seule ligne et garde son `display: block`. */
.stats:not(.rangee) > .stat { display: grid; grid-template-rows: subgrid; grid-row: span 3; row-gap: 0; }
/* Une carte qui mène à la liste qu'elle résume le DIT : curseur, chevron, et un relief au survol.
   Sans ces trois signes, personne n'essaie de cliquer un chiffre (7.15.0). */
.stat[data-stat], .stat[data-cstat] { cursor: pointer; transition: border-color .12s, box-shadow .12s, transform .12s; }
.stat[data-stat]:hover, .stat[data-cstat]:hover { border-color: var(--primary); box-shadow: 0 8px 22px rgba(15,157,143,.14); transform: translateY(-1px); }
.stat[data-stat]:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.stat[data-stat]::after, .stat[data-cstat]::after {
  content: '›'; position: absolute; inset-inline-end: 14px; top: 12px; font-size: 17px; line-height: 1;
  color: var(--muted); opacity: .5;
}
.stat[data-stat]:hover::after { color: var(--primary); opacity: 1; }

/* ---------- editor ---------- */
.editor { display: grid; grid-template-columns: 1fr 430px; gap: 20px; align-items: start; }
.editor .preview { position: sticky; top: 0; }
.preview iframe { width: 100%; height: calc(100vh - 96px); border: 1px solid var(--border); border-radius: var(--radius); background: #e9edf1; box-shadow: var(--shadow); }

table.lines-edit { width: 100%; border-collapse: collapse; }
table.lines-edit th { text-align: start; font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .8px; padding: 4px 6px 8px; font-weight: 700; }
table.lines-edit td { padding: 5px 6px; vertical-align: top; }
table.lines-edit tr + tr td { border-top: 1px solid var(--ink-soft); padding-top: 10px; }
table.lines-edit input, table.lines-edit select, table.lines-edit textarea { padding: 7px 9px; font-size: 13px; }
table.lines-edit textarea { min-height: 32px; margin-top: 5px; }
table.lines-edit .total { text-align: end; padding-top: 12px; font-weight: 600; white-space: nowrap; font-variant-numeric: tabular-nums; }
/* La grille des lignes d'un DOCUMENT se range selon la place qu'elle a (10.12.0). Mesuré à côté de
   l'aperçu : 98 px pour la désignation à 1440, 84 px à 1280, et 45 px pour le prix unitaire —
   « ence murale » visible sur « Dépose et évacuation de l'ancienne faïence murale ». En disposition
   automatique, la colonne la plus importante de la pièce était celle qui cédait, et les colonnes
   sautaient pendant la frappe. Chaque ligne est une GRILLE : étroite, la désignation prend toute la
   largeur et les chiffres passent dessous, sous des en-têtes rangés de la même façon ; large (sans
   l'aperçu, ou sur un grand écran), tout tient sur une rangée. La largeur qui décide est celle du
   CADRE, pas celle de la fenêtre : c'est l'aperçu qui la mange. */
.lignes-cadre { container-type: inline-size; }
table.lignes-doc, table.lignes-doc > thead, table.lignes-doc > tbody { display: block; }
table.lignes-doc tr {
  display: grid; column-gap: 2px; align-items: start;
  grid-template-columns: 64px 104px 108px 84px minmax(0, 1fr) 126px;
  grid-template-areas: "lab lab lab lab lab outils" "qte unite pu tva total total";
}
/* Chaque ligne est SA grille : une colonne « auto » y prendrait la largeur de son propre contenu.
   Les outils (↑ ↓ ⧉ ✕, 114 px et leur marge) valent 126 px dans une ligne et 0 dans l'en-tête, qui
   n'en a pas — tous les titres se décalaient alors d'une colonne vers la droite. La colonne est
   donc FIXE, la même partout. */
table.lignes-doc th, table.lignes-doc td { display: block; min-width: 0; }
table.lignes-doc tr > :nth-child(1) { grid-area: lab; }
table.lignes-doc tr > :nth-child(2) { grid-area: qte; }
table.lignes-doc tr > :nth-child(3) { grid-area: unite; }
table.lignes-doc tr > :nth-child(4) { grid-area: pu; }
table.lignes-doc tr > :nth-child(5) { grid-area: tva; }
table.lignes-doc tr > :nth-child(6) { grid-area: total; }
table.lignes-doc tr > :nth-child(7) { grid-area: outils; }
/* Une ligne se sépare de la suivante par un trait sur la LIGNE entière : posé sur chaque cellule,
   il coupait la ligne en deux au passage à la seconde rangée. */
table.lignes-doc tbody tr + tr { border-top: 1px solid var(--ink-soft); padding-top: 5px; }
table.lignes-doc tr + tr td { border-top: none; padding-top: 5px; }
table.lignes-doc tbody tr + tr td.total { padding-top: 12px; }
table.lignes-doc th { padding-bottom: 4px; }
/* La bascule sur UNE rangée se décide sur ce qui reste à la désignation : 602 px de colonnes fixes et
   leurs espaces, plus 340 px pour elle. À 880 px, elle n'en gardait que 293 sur un portable sans
   l'aperçu — c'est le parcours qui l'a mesuré. */
@container (min-width: 960px) {
  table.lignes-doc tr {
    grid-template-columns: minmax(0, 1fr) 64px 104px 108px 84px 116px 126px;
    grid-template-areas: "lab qte unite pu tva total outils";
  }
  table.lignes-doc th { padding-bottom: 8px; }
}
/* En COLONNE, calée à droite : la « Marge estimée » qui apparaît au premier coût connu se pose SOUS
   la carte des totaux. En rangée, elle devenait sa voisine et la carte sautait de 250 px vers la
   gauche au moment où l'on tape — le total quittait la colonne des montants (10.12.0, H-E1 : ce qui
   apparaît selon une valeur ne pousse pas ce qu'on regarde). */
.totals-box { margin-top: 14px; display: flex; flex-direction: column; align-items: flex-end; }
.totals-box table { background: var(--primary-soft); border-radius: 12px; padding: 8px 6px; }
.totals-box table td { padding: 4px 14px; text-align: end; font-variant-numeric: tabular-nums; color: #3d4a57; }
.totals-box table tr.grand td { font-weight: 700; font-size: 18px; color: var(--primary); padding-top: 8px; letter-spacing: -.3px; }
/* Une phrase dans le cadre des totaux (10.14.0) : elle passe à la ligne au lieu d'élargir le cadre
   — sinon les montants s'écartent au moment où elle apparaît. */
/* Les articles restés à 0 % d'un ancien régime (10.14.0) : la phrase et son geste sur une rangée qui
   passe à la ligne sans coller le bouton sous le texte (T-56). */
/* La phrase sous le taux d'un article, chez qui ne facture pas de TVA : une précision, pas un libellé. */
.field .taux-regime { font-weight: 400; }
.regime-cat { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 10px; }
/* Une remarque qui PRÉVIENT sous les totaux garde sa couleur : la règle des cellules (0,1,2, et
   0,2,3 en sombre) battait `.warn-text`, et l'avertissement d'une retenue sur un fournisseur
   exonéré (10.15.0, H7) se lisait gris comme une simple note. */
.totals-box table td.warn-text, body.dark .totals-box table td.warn-text { color: var(--warning); }
.totals-box .note-totaux { max-width: 17rem; margin-inline-start: auto; white-space: normal; line-height: 1.35; }
.catalog-pick { display: flex; gap: 8px; align-items: center; margin: 4px 0 14px; }

/* ---------- modal ---------- */
/* Ordre des couches, du plus bas au plus haut : palette (60), écran de verrouillage (200),
   assistant (250), fenêtres modales (400+), bulles d'aide (900), messages (950).
   Une fenêtre modale DOIT couvrir l'assistant et le verrouillage : en dessous, ses boutons
   recevaient les clics de l'écran du dessus et la question semblait ne plus répondre. */
/* 10.12.0 — une fenêtre part du HAUT, pas du centre. Centrée, elle se recentrait à chaque fois
   qu'elle grandissait : choisir « Solde de tout compte » faisait remonter de 196 px la liste qu'on
   venait de manipuler, et le clic suivant tombait à côté (règle H-E1 : ce qui apparaît ne pousse
   rien). Ancrée en haut, une fenêtre ne s'étend que vers le bas. Feuille PARTAGÉE : les deux
   applications ont la même règle. */
.modal-bg { position: fixed; inset: 0; background: rgba(27,36,48,.38); backdrop-filter: blur(3px); display: flex; align-items: flex-start; justify-content: center; padding-block: var(--haut-fenetre) 3vh; z-index: 400; }
/* Fenêtres empilées : un seul voile, celui du dessus. Sinon deux voiles se cumulent et la fenêtre
   du dessous devient illisible alors qu'on a justement besoin de la voir. */
.modal-bg:not(:last-child) { background: none; backdrop-filter: none; }
.modal { background: var(--panel); border-radius: 18px; padding: 26px 28px; width: 580px; max-width: 94vw; max-height: calc(97vh - var(--haut-fenetre)); overflow-y: auto; box-shadow: 0 24px 70px rgba(20,40,60,.25); }
.modal h2 { margin: 0 0 18px; font-size: 18px; font-weight: 700; letter-spacing: -.2px; }
.modal .modal-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 20px; }

/* ---------- toast ---------- */
#toast { position: fixed; bottom: 22px; left: 50%; transform: translateX(-50%) translateY(6px); background: var(--text); color: #fff; padding: 11px 18px; border-radius: 12px; font-size: 13px; font-weight: 500; opacity: 0; transition: opacity .18s, transform .18s; pointer-events: none; z-index: 950; box-shadow: 0 10px 30px rgba(20,40,60,.25); }
#toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
#toast.error { background: var(--danger); }
/* Le bandeau qui porte un « Annuler » (7.12.0). Il doit recevoir les clics : le reste du temps
   `#toast` est en `pointer-events: none`, et un bouton posé dedans serait parfaitement visible et
   parfaitement inerte — le défaut le plus difficile à croire qu'on ait rencontré (voir 5.2.2). */
#toast.avec-bouton { pointer-events: auto; display: flex; align-items: center; gap: 16px; }
.toast-undo { background: transparent; border: 1px solid rgba(255,255,255,.45); color: #fff; font: inherit; font-weight: 700; padding: 4px 12px; border-radius: 8px; cursor: pointer; }
.toast-undo:hover { background: rgba(255,255,255,.14); }
body.dark .toast-undo { border-color: rgba(18,22,28,.35); color: #12161c; }
body.dark .toast-undo:hover { background: rgba(18,22,28,.1); }

.muted { color: var(--muted); }
.small { font-size: 12px; }
.mt { margin-top: 12px; }
.filters { display: flex; gap: 8px; align-items: center; margin-bottom: 16px; flex-wrap: wrap; }
/* La recherche d'une barre de filtres devait faire 300 px — sur le papier. La règle générale des
   champs porte quatre :not() et gagnait (spécificité 0,4,1 contre 0,3,1) : la recherche prenait
   toute la ligne et renvoyait les filtres, la bulle et le compteur sur une seconde rangée, au-dessus
   de chaque liste ; sur le Catalogue, une bulle « i » restait seule sous la recherche (test humain,
   10.12.0). Même famille que .help-search (7.27.0) : le HTML est juste, c'est la feuille qui décide.
   La recherche est souple, pour que son texte d'invite reste lisible. Elle ne touche QUE la
   largeur : plus spécifique que la règle du thème sombre, un fond posé ici mettrait du blanc sous
   un texte clair (le défaut de la 7.30.0). Et seulement la recherche posée DANS la barre : un champ
   date rangé dans une étiquette de filtre garde sa largeur à lui. (Depuis la 10.14.1 la règle
   commune ne pèse plus rien ; la chaîne reste pour ce qu'elle DIT : une case à cocher n'est pas
   une recherche à étirer.) */
.filters > input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=range]) { width: auto; flex: 1 1 260px; max-width: 440px; }
.filters select { width: auto; background: #fff; }
.logo-preview { max-height: 60px; max-width: 200px; display: block; margin-bottom: 8px; border: 1px solid var(--border); padding: 6px; border-radius: 10px; background: #fff; }
code { background: var(--ink-soft); padding: 2px 6px; border-radius: 6px; font-size: 12px; }
table.lines-edit select { padding-inline-end: 4px; min-width: 70px; }

/* ---------- mises à jour ---------- */
.update-pill { font: inherit; font-size: 12px; font-weight: 600; color: var(--primary); background: var(--primary-soft); border: none; border-radius: 999px; padding: 7px 12px; cursor: pointer; margin-bottom: 8px; text-align: start; }
.update-pill:hover { background: var(--primary-soft-2); }
/* Licence : la même pastille, mais orange quand il faut agir — fin d'essai, licence qui se termine,
   création bloquée. Une information qui demande une action ne doit pas ressembler à une bonne
   nouvelle. */
.update-pill.warn { color: var(--warning); background: var(--warning-soft); }
/* Et grise, à peine plus marquée que le numéro de version juste en dessous, pendant les trois
   premières semaines de l'essai (8.0.1) : il faut que ça se voie sans ouvrir les Paramètres, et il
   ne faut pas que ça crie tous les matins — un bandeau qu'on apprend à ignorer emmène avec lui
   ceux qui comptent. */
.update-pill.calme { color: var(--muted); background: var(--ink-soft); font-weight: 500; }
.update-pill.calme:hover { background: var(--border); }
.app-version { font-size: 11px; color: #b0b8c2; padding: 8px 12px 0; }
.notes-md { background: var(--bg); border-radius: 10px; padding: 10px 14px; margin: 8px 0 12px; font-size: 13px; }
.notes-md h3 { font-size: 12px; text-transform: uppercase; letter-spacing: .8px; color: var(--muted); margin: 10px 0 4px; }
.notes-md h3:first-child { margin-top: 0; }
.notes-md p { margin: 4px 0; }
.notes-md ul { margin: 4px 0 6px; padding-inline-start: 20px; }
.notes-md li { margin: 2px 0; }
.changelog { max-height: 60vh; overflow-y: auto; }
.changelog .notes-md { background: transparent; padding: 0; }
/* L'écran des mises à jour (src/renderer/majui.js, 23/09/2026), le même dans les deux
   applications, dessiné d'après le panneau « Mise à jour de logiciels » de macOS : l'application et
   sa version en tête, UN bloc d'état qui répond à « suis-je à jour ? », et des lignes de réglage
   avec leur valeur ou leur interrupteur à droite. Tout passe par les jetons de couleur : le
   panneau se retourne en sombre avec le reste. */
.maj { display: flex; flex-direction: column; gap: 16px; }
.maj-app { display: flex; align-items: center; gap: 14px; }
.maj-icone { flex: none; display: flex; }
.maj-icone .brand-mark { width: 52px; height: 52px; border-radius: 13px; font-size: 19px; margin: 0; }
.maj-icone .brand-mark svg { width: 26px; height: 26px; }
.maj-icone.grande .brand-mark { width: 60px; height: 60px; border-radius: 15px; }
.maj-id { flex: 1; min-width: 0; }
.maj-nom { font-size: 17px; font-weight: 700; }
.maj-ver { color: var(--muted); font-size: 13px; margin-top: 2px; }
.maj-etat { display: flex; align-items: flex-start; gap: 14px; padding: 16px 18px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--bg); }
.maj-pictos { flex: none; display: flex; color: var(--muted); }
.maj-pictos svg { width: 28px; height: 28px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.maj-ajour .maj-pictos { color: var(--success); }
.maj-dispo .maj-pictos, .maj-pret .maj-pictos { color: var(--primary); }
.maj-erreur .maj-pictos, .maj-erreur > .maj-txt > .maj-sous { color: var(--danger); }
.maj-pret { border-color: var(--primary); background: var(--primary-soft); }
.maj-txt { flex: 1; min-width: 0; }
.maj-titre { font-weight: 700; font-size: 15px; }
.maj-sous { color: var(--muted); font-size: 13px; line-height: 1.5; margin-top: 3px; }
.maj-sous.maj-note { color: var(--text); }
.maj-droite { flex: none; align-self: center; }
.maj-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.maj-etat .progress { max-width: 460px; margin-top: 10px; }
.maj-notes { margin-top: 10px; font-size: 13px; }
.maj-notes summary { cursor: pointer; color: var(--primary); font-weight: 600; width: max-content; }
.maj-notes .notes-md { max-height: 240px; overflow: auto; background: var(--panel); }
.maj-spin { flex: none; width: 22px; height: 22px; border-radius: 50%; border: 2.5px solid var(--border); border-top-color: var(--primary); animation: maj-tourne .8s linear infinite; }
@keyframes maj-tourne { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .maj-spin { animation-duration: 2.4s; } }
.maj-lignes { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
.maj-ligne { display: flex; align-items: center; gap: 16px; padding: 13px 16px; }
.maj-ligne + .maj-ligne { border-top: 1px solid var(--border); }
.maj-ligne-txt { flex: 1; min-width: 0; }
.maj-ligne-titre { font-weight: 600; font-size: 14px; }
.maj-valeur { flex: none; color: var(--muted); font-size: 13px; }
/* L'interrupteur des réglages : la case elle-même, redessinée (et non une case cachée sous un
   décor) — elle garde son clavier, son étiquette et ce que les tests cliquent. */
input.switch { appearance: none; -webkit-appearance: none; flex: none; position: relative; width: 40px; height: 24px; margin: 0; padding: 0; border: none; border-radius: 12px; background: var(--border); cursor: pointer; transition: background .15s; }
input.switch::before { content: ''; position: absolute; top: 2px; inset-inline-start: 2px; width: 20px; height: 20px; border-radius: 50%; background: #fff; box-shadow: 0 1px 3px rgba(0, 0, 0, .25); transition: inset-inline-start .15s; }
input.switch:checked { background: var(--primary); }
input.switch:checked::before { inset-inline-start: 18px; }
input.switch:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
/* La fenêtre « prête à installer », celle de Sparkle. */
.maj-fenetre-tete { display: flex; align-items: flex-start; gap: 16px; }
.maj-fenetre h2 { margin: 0 0 6px; font-size: 17px; }
.maj-fenetre-notes { margin-top: 16px; padding: 12px 14px; max-height: 280px; overflow: auto; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg); font-size: 13px; }
.maj-fenetre-notes .notes-md { background: transparent; padding: 0; margin: 6px 0 0; }
.progress { height: 8px; background: var(--ink-soft); border-radius: 999px; overflow: hidden; margin-top: 8px; }
.progress div { height: 100%; background: var(--primary); border-radius: 999px; transition: width .2s; }

.token-box { margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--border); }
.token-box .k-label { margin-bottom: 6px; }
.token-box input { max-width: 520px; font-family: "SF Mono", Menlo, Consolas, monospace; font-size: 12px; }

/* « Détails techniques » (7.26.0) : la cause exacte d'un échec de mise à jour, repliée.
   Elle ne s'adresse pas à l'utilisateur — elle s'adresse à celui qui dépanne — mais la jeter
   priverait le dépannage à distance de la seule information utile (règle 6.7.2). */
details.tech { margin: 8px 0 10px; }
details.tech > summary { cursor: pointer; font-size: 12px; color: var(--muted); user-select: none; }
details.tech > summary:hover { color: var(--text); }
details.tech > code {
  display: block; margin-top: 6px; padding: 8px 10px;
  background: var(--ink-soft); border: 1px solid var(--border); border-radius: 6px;
  font-family: "SF Mono", Menlo, Consolas, monospace; font-size: 11px; line-height: 1.5;
  color: var(--muted); word-break: break-all; white-space: pre-wrap;
}
body.dark details.tech > code { background: #1d232b; }

/* ---------- relances / contrats / bannière ---------- */
.nav-count { margin-inline-start: auto; background: var(--warning-soft); color: var(--warning); font-size: 11px; font-weight: 700; border-radius: 999px; padding: 1px 7px; }
/* Le cadenas d'un module fermé par l'offre (7.33.0) : la page reste, elle se lit, elle ne crée plus. */
nav a svg.nav-lock { width: 12px; height: 12px; margin-inline-start: 4px; opacity: .55; flex: none; }
.offre-banner code { font-size: 11px; }
.nav-count.info { background: var(--info-soft); color: var(--info); }

/* ---------- panneau « À faire » ---------- */
.panel.todo ul { list-style: none; margin: 0; padding: 0; }
.panel.todo li { display: flex; align-items: center; gap: 12px; padding: 11px 0; border-top: 1px solid var(--border); }
.panel.todo li:first-child { border-top: none; padding-top: 0; }
.panel.todo li:last-child { padding-bottom: 0; }
.td-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; background: var(--info); }
.lvl-danger .td-dot { background: var(--danger); }
.lvl-warn .td-dot { background: var(--warning); }
.td-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.td-txt strong { font-size: 13.5px; font-weight: 650; }
.td-txt .small { line-height: 1.4; }
.lvl-danger .td-txt strong { color: var(--danger); }
.todo-ok { background: var(--success-soft); color: var(--success); border-radius: var(--radius); padding: 14px 18px; margin-bottom: 20px; font-weight: 600; font-size: 13px; }
/* En-tête repliable : le titre devient un bouton, le chevron tourne, le compteur reste visible replié. */
.collapse-h {
  background: none; border: 0; padding: 0; margin: 0; font: inherit; color: inherit;
  cursor: pointer; display: inline-flex; align-items: center; gap: 8px;
}
.collapse-h:hover { color: var(--primary); }
.collapse-h:focus-visible { outline: 2px solid var(--primary); outline-offset: 3px; border-radius: 4px; }
.collapse-h .chev { font-size: 11px; color: var(--muted); transition: transform .15s; }
.panel.collapsed .collapse-h .chev { transform: rotate(-90deg); }
.collapse-h .count { background: var(--ink-soft); color: var(--muted); font-size: 11px; font-weight: 700; border-radius: 999px; padding: 1px 8px; }
.panel.todo .todo-sum { margin: 0; }
body.dark .collapse-h .count { background: #1f2731; }

/* ---------- historique d'un document ---------- */
.timeline { list-style: none; margin: 0; padding: 0 0 0 6px; }
.timeline li { position: relative; padding: 0 0 16px 22px; border-inline-start: 2px solid var(--border); }
.timeline li:last-child { border-inline-start-color: transparent; padding-bottom: 0; }
.timeline li::before { content: ""; position: absolute; inset-inline-start: -6px; top: 3px; width: 10px; height: 10px; border-radius: 50%; background: var(--border); border: 2px solid var(--panel); }
.timeline li.k-emis::before, .timeline li.k-paiement::before, .timeline li.k-facture::before { background: var(--primary); }
.timeline li.k-relance::before { background: var(--warning); }
.timeline li.k-avoir::before, .timeline li.k-annule::before { background: var(--danger); }
.timeline .tl-h { font-size: 13px; font-weight: 600; }
.timeline .tl-d { font-size: 11.5px; color: var(--muted); }
.timeline a { color: var(--primary); }
nav a.active .nav-count { background: #fff; }
.banner { display: flex; align-items: center; justify-content: space-between; gap: 12px; background: var(--warning-soft); color: var(--warning); border-radius: var(--radius); padding: 12px 18px; margin-bottom: 16px; font-weight: 600; }
.banner.info { background: var(--info-soft); color: var(--info); }
/* Le bandeau rouge : réservé à ce qui est irréparable. Dans l'app du cabinet, c'est l'absence de
   clé de secours — perdre le poste sans elle rend illisibles POUR TOUJOURS tous les paquets reçus. */
.banner.danger { background: var(--danger-soft); color: var(--danger); }
.banner .btn:not(.btn-primary):not(.btn-danger) { background: #fff; }
.level { display: inline-block; padding: 2px 8px; border-radius: 6px; font-size: 11px; font-weight: 700; background: var(--ink-soft); color: var(--muted); }
.level.l2 { background: var(--warning-soft); color: var(--warning); }
.level.l3 { background: var(--danger-soft); color: var(--danger); }
/* Une relance reportée se lit en retrait — mais pas son bouton : pâli, il paraissait éteint (vu au test
   humain, 10.14.1). Le geste reste entier ; seul ce qu'on lit recule. */
table.list tr.snoozed td:not(.row-actions):not(.actions) { opacity: .62; }
table.list tr.snoozed:hover td:not(.row-actions):not(.actions) { opacity: 1; }
table.mini { width: 100%; border-collapse: collapse; margin-top: 8px; }
table.mini th { text-align: start; font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .8px; padding: 4px 6px; }
table.mini td { padding: 3px 6px; vertical-align: middle; }
table.mini input, table.mini select { padding: 6px 8px; font-size: 13px; }
.section-head { display: flex; align-items: center; justify-content: space-between; margin: 26px 0 12px; }
.section-head h2 { margin: 0; font-size: 16px; font-weight: 700; }

/* ---------- palette de recherche (Cmd+K) ---------- */
#palette-root { position: fixed; inset: 0; background: rgba(27,36,48,.35); backdrop-filter: blur(3px); z-index: 60; display: flex; justify-content: center; align-items: flex-start; padding-top: var(--haut-fenetre); }
.palette { width: 640px; max-width: 94vw; background: var(--panel); border-radius: 16px; box-shadow: 0 24px 70px rgba(20,40,60,.3); overflow: hidden; }
.palette input { border: none; border-bottom: 1px solid var(--border); border-radius: 0; padding: 16px 18px; font-size: 16px; background: #fff; }
.palette input:focus { box-shadow: none; border-color: var(--border); }
.palette .results { max-height: 52vh; overflow-y: auto; padding: 6px; }
.palette .res { display: flex; align-items: center; gap: 12px; padding: 9px 12px; border-radius: 10px; cursor: pointer; }
.palette .res.sel, .palette .res:hover { background: var(--primary-soft); }
.palette .res .kind { font-size: 10px; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); width: 88px; flex-shrink: 0; font-weight: 700; white-space: nowrap; }
.palette .res .main { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.palette .res .sub { color: var(--muted); font-size: 12px; margin-inline-start: 6px; }
.palette .res .amt { color: var(--muted); font-variant-numeric: tabular-nums; font-size: 13px; }
.palette .hint { padding: 8px 14px; font-size: 11px; color: var(--muted); border-top: 1px solid var(--border); }

/* ---------- menu « Plus » de l'éditeur ---------- */
.more { position: relative; }
.more-list { position: absolute; inset-inline-end: 0; top: 42px; background: var(--panel); border: 1px solid var(--border); border-radius: 12px; box-shadow: 0 12px 40px rgba(20,40,60,.18); display: flex; flex-direction: column; min-width: 240px; padding: 6px; z-index: 30; }
.more-list button { font: inherit; font-size: 13px; text-align: start; background: transparent; border: none; padding: 9px 12px; border-radius: 8px; cursor: pointer; color: var(--text); }
.more-list button:hover { background: var(--ink-soft); }
.more-list button.danger { color: var(--danger); }
/* Une entrée de menu et sa bulle sur UNE ligne (10.12.0). La bulle vivait À L'INTÉRIEUR du bouton :
   un bouton dans un bouton n'existe pas en HTML, le navigateur fermait le premier au second, et la
   bulle tombait seule sur la ligne suivante du menu « Facturer ▾ ». */
.more-list .ml-ligne { display: flex; align-items: center; gap: 4px; }
.more-list .ml-ligne > button:not(.i) { flex: 1 1 auto; }
.more-list .ml-ligne > button.i { margin-inline-end: 8px; }
/* Les conversions rangées dans « Plus ▾ » (10.14.0) : un titre de section et un trait qui les
   séparent des gestes de la pièce elle-même. */
.more-list .ml-titre { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .6px; color: var(--muted); padding: 6px 12px 2px; }
.more-list .ml-sep { border-top: 1px solid var(--border); margin: 6px 4px; }
.section-head h2.small { font-size: 12px; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); }

/* ---------- tableau de bord : graphiques ---------- */
.dash-grid { display: grid; grid-template-columns: 2fr 1fr; gap: 18px; align-items: start; }
.chart { width: 100%; height: 220px; display: block; }
.chart .bar-inv { fill: var(--primary); opacity: .85; }
.chart .bar-col { fill: #2a6fd6; opacity: .55; }
.chart .axis { stroke: var(--border); stroke-width: 1; }
.chart .lbl { font-size: 10px; fill: var(--muted); }
.chart .grid { stroke: var(--border); stroke-dasharray: 2 3; }
.legend { display: flex; gap: 16px; font-size: 12px; color: var(--muted); margin-top: 6px; }
.legend i { display: inline-block; width: 10px; height: 10px; border-radius: 3px; margin-inline-end: 6px; vertical-align: -1px; }
.kpis { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 14px; }
.kpi { background: var(--bg); border-radius: 12px; padding: 12px 14px; }
.kpi .v { font-size: 20px; font-weight: 700; margin-top: 4px; font-variant-numeric: tabular-nums; }
.kpi .sub { font-size: 12px; color: var(--muted); }
/* Un classement se lit en COLONNES (10.12.0) : les noms alignés, les barres sur la même piste, les
   montants au bout. Le nom était plafonné à 42 % de la ligne pendant que le montant en réservait
   110 px : « Hôtel Dar El Marsa … » à côté d'une barre de 55 px et de 60 px vides — le nom est
   l'information, la barre n'est que son ordre de grandeur. La colonne du nom prend ce dont elle a
   besoin, jusqu'à 60 % ; la barre garde 56 px au moins. Et chaque ligne REPREND les colonnes de la
   liste (subgrid) : deux barres ne se comparent que si elles partagent la même piste, ce qu'une
   rangée flex par ligne ne garantissait pas (la piste changeait avec la longueur du nom). */
.rank { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: fit-content(60%) minmax(56px, 1fr) max-content; column-gap: 10px; }
.rank li { grid-column: 1 / -1; display: grid; grid-template-columns: subgrid; align-items: center; padding: 8px 0; border-bottom: 1px solid var(--border); font-size: 13px; }
.rank li:last-child { border-bottom: none; }
.rank .bar { height: 8px; background: var(--ink-soft); border-radius: 999px; overflow: hidden; }
.rank .bar i { display: block; height: 100%; background: var(--primary); border-radius: 999px; }
.rank .amt { font-variant-numeric: tabular-nums; color: var(--muted); text-align: end; }
.rank a.name { color: inherit; text-decoration: none; font-weight: 500; }
.rank a.name:hover { color: var(--primary); text-decoration: underline; }
/* Un libellé de prestation n'est pas cliquable : il occupe la même place qu'un nom de client. */
.rank .name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }
.rank.plain { grid-template-columns: minmax(0, 1fr) max-content; }   /* pas de barre : le nom prend la place */

/* ---------- Statistiques ---------- */
/* Écart avec la même période de l'an dernier, accolé au chiffre : vert si ça monte, rouge si ça baisse. */
.delta { font-size: 12px; font-weight: 700; letter-spacing: 0; white-space: nowrap; }
.delta.up { color: var(--success); }
.delta.down { color: var(--danger); }
.warn-text { color: var(--warning); }
.danger-text { color: var(--danger); }
.ok-text { color: var(--success); }
.err-text { color: var(--danger); }
/* `class="mono"` était posé sur l'empreinte du cabinet depuis la 6.2.0 et n'a JAMAIS été défini :
   elle s'affichait dans la police du texte, là où c'est exactement l'endroit qui a besoin d'une
   chasse fixe — une empreinte se lit caractère par caractère, et c'est ainsi qu'on distingue un 0
   d'un O et un 1 d'un l. Rien ne plantait, rien n'apparaissait nulle part (8.1.0). */
.mono { font-family: "SF Mono", Menlo, Consolas, monospace; font-variant-ligatures: none; letter-spacing: .5px; }
.inline > .grow { flex: 1 1 auto; min-width: 0; }
.sub-h { font-size: 12px; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); font-weight: 700; margin: 16px 0 8px; }
.panel > .sub-h:first-of-type { margin-top: 4px; }
.two-col { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; }
/* Jauge d'objectif : la barre est le réalisé, le trait vertical le rythme théorique du jour. */
.gauge { margin-top: 4px; }
.g-bar { position: relative; height: 14px; border-radius: 999px; background: var(--ink-soft); overflow: hidden; }
.g-bar > i { display: block; height: 100%; background: var(--primary); border-radius: 999px; transition: width .3s; }
.g-mark { position: absolute; top: -3px; bottom: -3px; width: 2px; background: var(--text); opacity: .55; border-radius: 2px; }
.g-legend { display: flex; justify-content: space-between; font-size: 13px; margin-top: 8px; }
/* Barres d'appoint dans un tableau (issue des devis) : mêmes proportions que celles du classement. */
.list .bar { display: block; height: 8px; background: var(--ink-soft); border-radius: 999px; overflow: hidden; }
.list .bar i { display: block; height: 100%; border-radius: 999px; background: var(--primary); }
.list .bar i.f-ok { background: var(--success); }
.list .bar i.f-bad { background: var(--danger); }
.list .bar i.f-warn { background: var(--warning); }
.list .bar i.f-neutral { background: #9aa7b4; }
tr.row-warn td { background: var(--warning-soft); }
/* L'exercice en cours dans un plan d'amortissement : repéré sans être alarmant. */
tr.row-now td { background: rgba(15, 157, 143, .08); }
.chart .bar-prev { fill: #9aa7b4; opacity: .55; }
/* ---------- courbe de trésorerie : c'est le passage sous zéro qui doit sauter aux yeux ---------- */
.chart.forecast { height: 210px; }
.fc-area { fill: var(--primary); opacity: .10; }
.fc-line { stroke: var(--primary); stroke-width: 2; stroke-linejoin: round; }
.fc-zero { stroke: var(--danger); stroke-width: 1; stroke-dasharray: 4 3; opacity: .7; }
.chart .bar-off { opacity: .28; }   /* mois hors de la période choisie : présents pour le contexte, en retrait */
/* Deux panneaux de poids égal, contrairement à .dash-grid (2fr 1fr) qui met un graphique en vedette.
   minmax(0, 1fr) : sans lui, un tableau large impose sa largeur et casse le partage en deux. */
.split { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; align-items: start; }
#s-kind { min-width: 152px; }
#s-n { min-width: 208px; }
@media (max-width: 1100px) { .two-col, .split { grid-template-columns: 1fr; } }

/* Bouton retour : présent mais discret, il ne doit pas concurrencer l'action principale de la page. */
.btn-back { background: none; border-color: var(--border); color: var(--muted); font-weight: 500; }
.btn-back:hover { background: var(--ink-soft); color: var(--text); }
body.dark .btn-back { border-color: #2b3540; }
/* Le retour a DEUX placements, et un seul des deux avait son espacement. Dans l'app entreprise il
   vit à l'intérieur d'une barre `.actions`, qui porte son propre `gap` : il est alors sur la même
   rangée que les autres gestes, et une marge basse le désalignerait. Dans le Cabinet il est posé
   SEUL, juste au-dessus du titre de la fiche — et il s'y collait au nom du client : zéro pixel,
   sur les vingt-huit écrans d'un dossier, dans les deux thèmes et aux deux largeurs. La règle vise
   donc le placement autonome (enfant direct de la page), jamais le bouton en soi. */
#view > .btn-back { margin-block-end: 14px; }
.stamp-preview { max-height: 70px; max-width: 220px; display: block; margin-bottom: 8px; border: 1px solid var(--border); padding: 6px; border-radius: 10px; background: #fff; }

/* ---------- thème sombre ---------- */
body.dark {
  --bg: #12161c; --panel: #1a2029; --border: #2a3340; --line: #3b4656; --text: #e6eaf0; --muted: #8c98a8;
  --primary: #1fb8a8; --primary-hover: #26cbba; --primary-soft: #14302e; --primary-soft-2: #1a403c; --ink-soft: #232b36;
  --danger: #ef6b64; --danger-soft: #3a2224; --success: #34c27f; --success-soft: #1a3328; --warning: #e39a4a; --warning-soft: #3a2c1c; --info: #6ea3f0; --info-soft: #1d2b44;
  --shadow: 0 1px 2px rgba(0,0,0,.3), 0 6px 20px rgba(0,0,0,.25);
  color-scheme: dark;
}
/* Le thème sombre des CHAMPS, et la chaîne de `:not()` n'est pas décorative.
   La règle commune (« input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=range]) »)
   pèse quatre sélecteurs d'attribut ; `body.dark input[type=text]` n'en pesait que deux. Elle
   PERDAIT donc, en silence : tous les champs de saisie de l'application gardaient leur fond clair
   (#fbfcfd) avec le texte clair du thème sombre (#e6eaf0), soit un contraste mesuré de **1,18** —
   du blanc sur du blanc. `select` et `textarea`, eux, n'étaient pas concernés : dans une LISTE de
   sélecteurs, chaque membre porte sa propre spécificité, et celle de `textarea` seul est minuscule.
   D'où un écran sombre où les zones de texte étaient lisibles et les champs d'une ligne, non.
   Mesuré dans l'application (getComputedStyle + rapport de luminance), pas déduit du fichier :
   c'est la troisième fois qu'une règle correcte perd contre une autre sans que rien ne le signale
   (le `th.r` des colonnes en 7.23.0, `.help-fil` en 7.27.0).
   10.14.1 — la règle commune ne pèse plus rien (`:where()`), mais celle-ci garde sa chaîne NUE,
   exprès : en sombre, un fond clair posé par un conteneur (une ligne, une palette, un en-tête)
   remettrait du blanc sous un texte clair. Le thème sombre des champs doit gagner contre eux. */
body.dark input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=range]),
body.dark select, body.dark textarea { background: #12161c; color: var(--text); border-color: var(--border); }
body.dark input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=range]):focus,
body.dark select:focus, body.dark textarea:focus { background: #0e1217; }
body.dark input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=range]):disabled,
body.dark select:disabled, body.dark textarea:disabled { background: #1f2630; color: #8c98a8; }
/* Le texte d'invite doit rester lisible sans se confondre avec une valeur saisie (le gris du thème
   sombre bat la règle commune ci-dessous par sa spécificité : `body.dark` en tête). */
body.dark input::placeholder, body.dark textarea::placeholder { color: #6f7c8c; }
/* 10.12.0 (U-18) — et il se lit comme un EXEMPLE, dans les deux thèmes : gris, en graisse normale.
   Un champ hérite du gras de son étiquette (`font: inherit`, et une étiquette de formulaire est à
   600) : « +216 … » ou « SKAN1.… » en gras se prenaient pour des valeurs déjà saisies, et on
   passait le champ sans le remplir. `opacity: 1` : Firefox atténue l'invite par défaut, et deux
   atténuations s'additionnent. */
input::placeholder, textarea::placeholder { font-weight: 400; color: var(--muted); opacity: 1; }
body.dark .btn { background: var(--ink-soft); color: var(--text); }
body.dark .btn:hover { background: #2c3644; }
body.dark .btn-primary { background: var(--primary); color: #0b1512; }
body.dark .btn-primary:hover { background: var(--primary-hover); }
body.dark .btn-danger { background: var(--danger-soft); color: var(--danger); }
body.dark .btn-ghost { background: transparent; }
body.dark table.list th { background: #171d25; }
body.dark table.list tr.clickable:hover td { background: #1f2731; }
body.dark table.list tr.total-row td { background: #171d25; }
body.dark .empty { border-color: var(--border); }
/* Le pied de la barre recopie la couleur des liens du menu (7.0.0) mais n'avait pas reçu sa jumelle
   sombre : « Tous les modules » et « Aide » restaient gris foncé sur fond foncé, contraste 2,1. */
body.dark nav a, body.dark .sidebar-foot .foot-link { color: #b6c0cc; }
body.dark nav a.active .nav-count { background: var(--panel); }
body.dark .filters input:not([type=checkbox]):not([type=radio]), body.dark .filters select { background: var(--panel); }
body.dark .preview iframe { background: #0e1217; }
body.dark .totals-box table td { color: #c9d2dd; }
/* En sombre, l'ombre d'une fenêtre ne se voit plus sur le fond assombri : son bord se devinait à
   peine (vu à la souris, 10.13.0). Un liseré dit où elle commence. */
body.dark .modal { background: var(--panel); border: 1px solid var(--line); }
body.dark .palette input { background: var(--panel); color: var(--text); }
body.dark .banner .btn:not(.btn-primary):not(.btn-danger),
body.dark .banner.info .btn:not(.btn-primary):not(.btn-danger) { background: var(--panel); color: var(--text); }
body.dark .logo-preview, body.dark .stamp-preview { background: #fff; }
body.dark #toast { background: #e6eaf0; color: #12161c; }
body.dark #toast.error { background: var(--danger); color: #fff; }
body.dark .app-version { color: #5c6875; }
body.dark code { background: var(--ink-soft); }
body.dark .kpi, body.dark .pay-grid > div, body.dark .notes-md { background: var(--bg); }

/* ---------- listes : tri, totaux, actions au survol ---------- */
th.sortable-h { cursor: pointer; user-select: none; }
th.sortable-h:hover { color: var(--text); background: var(--ink-soft); }
th.sortable-h:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
.sort-ar { margin-inline-start: 4px; color: var(--primary); }
/* Sans repère au repos, personne ne devine qu'un en-tête se clique : « ⇅ » discret, flèche pleine une fois trié. */
th.sortable-h:not(.sorted) .sort-ar { color: var(--muted); opacity: .45; }
th.sortable-h:hover .sort-ar { opacity: 1; }
table.list tfoot td { padding: 11px 16px; background: #fafbfc; border-top: 1px solid var(--border); font-size: 12px; color: var(--muted); font-weight: 600; }
table.list tfoot td.r { color: var(--text); font-variant-numeric: tabular-nums; }
/* Les boutons de ligne ne tiennent plus sur une seule ligne dès qu'une pièce en porte cinq (un devis
   en attente : PDF, Email, Accepté, Refusé, Dupliquer). `nowrap` les poussait alors hors de l'écran,
   et un bouton hors champ n'existe pas — c'est `npm run e2e:contraste` qui l'a mesuré. On les laisse
   passer à la ligne plutôt que disparaître ; la cellule reste au plus étroit tant qu'ils tiennent. */
td.row-actions { text-align: end; width: 1%; }
/* Les boutons de ligne restent visibles, en retrait, et reprennent leur pleine opacité au survol.
   Cachés jusqu'au survol (audit), ils étaient introuvables sur les listes de documents et de clients
   alors qu'ils étaient toujours affichés partout ailleurs. */
/* `flex-wrap: wrap` (7.18.0) devait éviter un débordement à 1280 px. Il a fabriqué pire : la
   cellule vaut `width: 1%`, donc le navigateur lui donne sa largeur MINIMALE — et la largeur
   minimale d'un conteneur qui peut passer à la ligne, c'est celle d'un seul bouton. Les cinq
   boutons se sont donc empilés verticalement sur chaque ligne, et les lignes ont triplé de hauteur.
   En `nowrap`, la largeur minimale redevient la somme des boutons : une seule ligne, comme avant.
   Le débordement, lui, se mesure — `npm run e2e:contraste` refuse tout bouton hors de l'écran. */
td.row-actions > span { display: inline-flex; flex-wrap: nowrap; justify-content: flex-end; gap: 4px; opacity: .7; transition: opacity .12s; }
tr:hover td.row-actions > span, tr:focus-within td.row-actions > span { opacity: 1; }
th.row-actions-h { width: 1%; }

/* Le menu d'actions d'une ligne (7.28.0). Le bouton occupe la largeur d'un bouton quel que soit le
   nombre d'actions : c'est ce qui met fin, définitivement, au budget de boutons d'une ligne.
   Il porte le MOT « Actions » depuis la 7.29.0 : « ⋮ » ne se lit que si on connaît déjà la
   convention, et c'était devenu la seule porte de la ligne — la porte d'une maison ne se signale
   pas par trois points. Le chevron dit qu'elle ouvre sur un choix. */
.row-menu-btn {
  display: inline-flex; align-items: center; gap: 5px; white-space: nowrap;
  background: var(--panel); border: 1px solid var(--border); border-radius: 8px;
  padding: 4px 9px; cursor: pointer; font: inherit; font-size: 12.5px; font-weight: 600;
  color: var(--muted); opacity: .9; transition: opacity .12s, background .12s, color .12s;
}
/* H3 (10.15.0) — un menu posé dans une BARRE (l'en-tête d'une fiche, la barre d'un livre) prend
   l'allure du bouton qu'il côtoie : gris et à 90 %, « Exporter ▾ » se lisait comme un bouton éteint
   à côté de « Réimporter… », et « Imprimer la fiche » comme un texte à côté de « Modifier la fiche ».
   Seul le menu d'une LIGNE reste discret au repos (la ligne survolée l'allume). */
.row-menu-btn.btn { color: var(--text); opacity: 1; padding: 9px 15px; font-size: 13px; border-radius: 10px; }
.row-menu-btn.btn.btn-sm { padding: 6px 11px; font-size: 12px; border-radius: 8px; }
.row-menu-btn svg { stroke: currentColor; fill: none; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
/* Le bouton visible que la ligne a le droit de garder, et le menu juste après : sans cette règle
   ils se touchent, parce que `td.actions .btn + .btn` ne voit pas `.row-menu-btn`. */
td.row-actions .btn + .row-menu-btn { margin-inline-start: 6px; }
.row-menu-btn .rm-chev { width: 13px; height: 13px; }
.row-menu-btn .rm-i { width: 15px; height: 15px; }
/* Une action UNIQUE devient un vrai bouton qui la nomme (pas un menu à un choix). Son icône est un
   vrai <svg> ; le `<span class="rm-i">` vide, lui, ne sert qu'à aligner les colonnes DANS le menu
   et n'a rien à faire ici — sans cette règle il laisserait un blanc de 15 px avant le libellé. */
.row-menu-btn span.rm-i { display: none; }
tr:hover .row-menu-btn, tr:focus-within .row-menu-btn, .row-menu-btn[aria-expanded="true"] {
  opacity: 1; background: var(--ink-soft); color: var(--text);
}
/* Couche 70 : au-dessus de la page et de ses tableaux, SOUS les fenêtres modales (400) — une
   question posée par une action doit rester devant le menu qui l'a déclenchée (règle 5.2.2). */
.row-menu {
  position: fixed; z-index: 70; min-width: 226px; max-width: 320px;
  background: var(--panel); border: 1px solid var(--border); border-radius: 12px;
  box-shadow: 0 12px 36px rgba(20, 40, 60, .18); padding: 6px;
  display: flex; flex-direction: column; gap: 1px;
}
/* L'icône est une COLONNE, pas un élément en ligne : les libellés s'alignent alors les uns sous les
   autres même quand une action n'en a pas (`ico()` pose un vide de la même largeur). Un menu dont
   les textes ne démarrent pas au même x se lit deux fois plus lentement. */
.row-menu button {
  display: grid; grid-template-columns: 18px 1fr; align-items: start; gap: 0 10px; width: 100%; text-align: start;
  font: inherit; background: none; border: 0; border-radius: 8px; padding: 8px 10px; cursor: pointer; color: var(--text);
}
.row-menu button:hover, .row-menu button:focus-visible { background: var(--ink-soft); outline: none; }
.row-menu button.danger .rm-l { color: var(--danger); }
.row-menu button.danger .rm-i { color: var(--danger); }
.row-menu .rm-i { width: 18px; height: 18px; display: block; margin-top: 1px; color: var(--muted); }
.row-menu svg.rm-i { stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.row-menu button:hover .rm-i, .row-menu button:focus-visible .rm-i { color: var(--primary); }
.row-menu .rm-t { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.row-menu .rm-l { font-size: 13.5px; font-weight: 600; }
.row-menu .rm-h { font-size: 11.5px; color: var(--muted); line-height: 1.35; }
.row-menu hr { border: 0; border-top: 1px solid var(--border); margin: 5px 4px; width: calc(100% - 8px); }
.badge.expiré { background: var(--warning-soft); color: var(--warning); }
/* statuts d'achat : libellés avec espace, donc classes dédiées */
.badge.b-due { background: var(--info-soft); color: var(--info); }
.badge.b-part, .badge.b-late { background: var(--warning-soft); color: var(--warning); }
.badge.b-late { background: var(--danger-soft); color: var(--danger); }
.badge.b-paid { background: var(--success-soft); color: var(--success); }
/* case à cocher centrée dans une ligne d'achat */
.lines-edit td.c { text-align: center; }
.buy-lines select { font-size: 13px; padding: 6px 8px; }
body.dark table.list tfoot td { background: #171d25; }
body.dark th.sortable-h:hover { background: #1f2731; }

/* ---------- liste déroulante avec recherche ---------- */
.combo { position: relative; width: 100%; }
.combo-btn {
  font: inherit; font-size: 14px; color: var(--text); text-align: start;
  padding: 9px 11px; border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: #fbfcfd; width: 100%; cursor: pointer;
  display: flex; align-items: center; gap: 8px; transition: border-color .12s, box-shadow .12s, background .12s;
}
.combo-btn:hover { border-color: #d5dbe3; }
.combo-btn[aria-expanded="true"], .combo-btn:focus-visible { outline: none; border-color: var(--primary); background: #fff; box-shadow: 0 0 0 3px rgba(15,157,143,.15); }
.combo-val { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-weight: 500; }
.combo-val.ph { color: var(--muted); font-weight: 400; }
/* 10.13.0 — le MÊME chevron que les listes (`select`) : le combo portait un triangle plein « ▾ »,
   et deux listes voisines dans un formulaire avaient chacune leur dessin (vu à la souris sur une
   facture : « Affaire » à côté de « Langue du document »). Le caractère reste dans le HTML pour les
   lecteurs d'écran qui le lisent ; seul son dessin change. */
.combo-caret {
  flex-shrink: 0; font-size: 0; width: 10px; height: 6px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%238a94a3' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat center / 10px 6px;
}
/* Toutes les cases à cocher et boutons radio prennent la couleur de l'application : seules les
   cases rangées dans `.check` l'avaient, et l'assistant montrait des cases BLEUES (le système) à
   un écran d'une case verte (vu à la souris). */
input[type=checkbox], input[type=radio] { accent-color: var(--primary); }
.combo.ro .combo-btn { background: var(--ink-soft); cursor: default; opacity: .75; }
.combo-pop {
  position: absolute; z-index: 60; inset-inline-start: 0; inset-inline-end: 0; top: calc(100% + 5px);
  background: var(--panel); border: 1px solid var(--border); border-radius: 12px;
  box-shadow: 0 16px 44px rgba(20,40,60,.18); padding: 7px; min-width: 260px;
}
.combo.up .combo-pop { top: auto; bottom: calc(100% + 5px); }
.combo-q { font-size: 13px; padding: 8px 10px; }
.combo-list { max-height: 260px; overflow-y: auto; margin-top: 6px; }
.combo-it {
  display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 8px;
  cursor: pointer; font-size: 13px;
}
.combo-it.sel { background: var(--primary-soft); }
.combo-it.cur .ci-main { color: var(--primary); }
.ci-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; font-weight: 600; }
.ci-sub { font-size: 11.5px; color: var(--muted); font-weight: 400; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ci-right { color: var(--muted); font-size: 12px; white-space: nowrap; font-variant-numeric: tabular-nums; }
.combo-empty { padding: 14px 10px; color: var(--muted); font-size: 13px; text-align: center; }
.combo-add {
  font: inherit; font-size: 12.5px; font-weight: 600; color: var(--primary); background: none; border: 0;
  border-top: 1px solid var(--border); width: 100%; text-align: start; padding: 9px 10px; margin-top: 5px; cursor: pointer;
}
.combo-add:hover { background: var(--primary-soft); border-radius: 0 0 8px 8px; }
/* La désignation qui cherche dans le catalogue (9.2.1) : le même langage que la liste déroulante,
   posé sous un champ texte libre. La cellule porte la position, la liste flotte en dessous. */
.sugg-host { position: relative; }
.sugg-pop {
  position: absolute; z-index: 60; inset-inline-start: 0; top: calc(100% + 4px);
  min-width: 320px; max-width: 540px; max-height: 300px; overflow-y: auto;
  background: var(--panel); border: 1px solid var(--border); border-radius: 12px;
  box-shadow: 0 16px 44px rgba(20,40,60,.18); padding: 7px;
}
.sugg-it { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 8px; cursor: pointer; font-size: 13px; }
.sugg-it.sel, .sugg-it:hover { background: var(--primary-soft); }
.sugg-add {
  font: inherit; font-size: 12.5px; font-weight: 600; color: var(--primary); background: none; border: 0;
  border-top: 1px solid var(--border); width: 100%; text-align: start; padding: 9px 10px; margin-top: 5px; cursor: pointer;
}
.sugg-add:hover { background: var(--primary-soft); border-radius: 0 0 8px 8px; }
/* Seule dans la liste (rien du catalogue ne correspond), la création n'a rien à séparer : le trait
   au-dessus d'elle séparait du vide (10.12.0). */
.sugg-add:first-child { border-top: 0; margin-top: 0; }
body.dark .combo-btn { background: var(--panel); }
body.dark .combo-btn[aria-expanded="true"] { background: #1b222c; }
body.dark .combo.ro .combo-btn { background: #1f2731; }

/* ---------- les listes du système, redessinées (10.13.0) ----------
   Un `<select>` ouvrait la liste du SYSTÈME (grise sous Windows, une bulle sous macOS) pendant que
   les clients ou le catalogue ouvraient celle de SkanFact. `listes.js` ouvre désormais la même liste
   partout ; ici, le champ fermé cesse de porter la flèche du système et prend le chevron des combos.
   Les trois `:not()` ne sont pas décoratifs : ils donnent au chevron (0,3,1), plus que toute règle
   qui pose un fond par le raccourci `background` (`.filters select`, `body.dark .filters select` à
   (0,2,2), `:focus`, `:disabled`) — un raccourci remet `background-image` à zéro, et un `select` seul
   perdait le chevron sur la moitié des listes. `e2e:listes` le mesure, en clair et en sombre.
   `background-position` n'a pas d'équivalent logique : `right` est l'exception nommée ici — le
   chevron suit la fin du champ en français ; une interface arabe le retournerait par `[dir=rtl]`. */
select:not([multiple]):not([size]):not([data-natif]) {
  appearance: none; -webkit-appearance: none; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%238a94a3' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 11px center; background-size: 10px 6px;
  padding-inline-end: 30px; text-overflow: ellipsis;
}
select:not([multiple]):not([size]):not([data-natif]):disabled { cursor: not-allowed; }
/* Dans une grille de saisie ou un pied de pagination, la place compte : le chevron se serre. */
:is(table, .pg-size, .buy-lines) select:not([multiple]):not([size]):not([data-natif]) {
  background-position: right 7px center; padding-inline-end: 22px;
}
/* La liste ouverte : le dessin de `.combo-pop`, posée sur la page entière (position fixe) parce
   qu'un `<select>` vit souvent dans un tableau qui défile ou une fenêtre qui coupe ce qui déborde. */
/* `border-box` : la liste prend la largeur du champ bord à bord — sans lui, son cadre et ses
   marges s'ajoutaient à la largeur mesurée et elle dépassait le champ de 14 px (vu à la souris). */
.lm-pop {
  position: fixed; z-index: 880; box-sizing: border-box;
  background: var(--panel); border: 1px solid var(--border); border-radius: 12px;
  box-shadow: 0 16px 44px rgba(20,40,60,.18); padding: 6px;
}
.lm-pop .lm-list { margin-top: 0; max-height: 300px; }
.lm-pop .lm-q + .lm-list { margin-top: 6px; }
.lm-pop .combo-it { padding: 7px 10px; font-size: 13.5px; }
.lm-pop .ci-main { font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: block; }
.lm-pop .combo-it.cur .ci-main { font-weight: 600; }
.lm-groupe { font-size: 12px; font-weight: 600; color: var(--muted); padding: 9px 10px 3px; }
.lm-groupe:first-child { padding-top: 4px; }
/* Une ligne éteinte se montre et ne se choisit pas, comme dans la liste native. */
.combo-it.off { opacity: .45; cursor: default; }
.combo-it.off.sel { background: none; }
body.dark .lm-pop { box-shadow: 0 16px 44px rgba(0,0,0,.45); }

/* ---------- champ date et calendrier ---------- */
.datefield { position: relative; display: flex; align-items: center; }
.datefield .d-txt { padding-inline-end: 38px; font-variant-numeric: tabular-nums; }
.d-btn {
  position: absolute; inset-inline-end: 4px; top: 50%; transform: translateY(-50%);
  width: 30px; height: 30px; border: 0; background: none; cursor: pointer; color: var(--muted);
  display: grid; place-items: center; border-radius: 8px;
}
.d-btn:hover { background: var(--ink-soft); color: var(--primary); }
.d-btn svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; }
.datefield.ro .d-btn { display: none; }
.cal-pop {
  position: absolute; z-index: 60; inset-inline-start: 0; top: calc(100% + 5px); width: 268px;
  background: var(--panel); border: 1px solid var(--border); border-radius: 12px;
  box-shadow: 0 16px 44px rgba(20,40,60,.18); padding: 10px;
}
.datefield.up .cal-pop { top: auto; bottom: calc(100% + 5px); }
.cal-head { display: flex; align-items: center; gap: 5px; margin-bottom: 8px; }
.cal-nav {
  font: inherit; font-size: 17px; line-height: 1; width: 28px; height: 28px; flex-shrink: 0;
  border: 0; background: none; color: var(--muted); cursor: pointer; border-radius: 8px;
}
.cal-nav:hover { background: var(--ink-soft); color: var(--text); }
/* Sélecteurs plus spécifiques que la règle générale `input[type=number] { width: 100% }`,
   sinon le champ Année mange toute la place et le nom du mois disparaît. */
.cal-head .cal-m { flex: 1 1 auto; min-width: 0; width: auto; padding: 5px 6px; font-size: 12.5px; }
.cal-head .cal-y { flex: 0 0 62px; width: 62px; padding: 5px 4px; font-size: 12.5px; text-align: center; }
.cal-dow { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; margin-bottom: 3px; }
.cal-dow span { text-align: center; font-size: 10px; text-transform: uppercase; letter-spacing: .4px; color: var(--muted); font-weight: 700; }
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.cal-d {
  font: inherit; font-size: 12.5px; height: 30px; border: 0; border-radius: 8px;
  background: none; color: var(--text); cursor: pointer; font-variant-numeric: tabular-nums;
}
.cal-d:hover { background: var(--ink-soft); }
.cal-d.out { color: #b8c0ca; }
.cal-d.today { box-shadow: inset 0 0 0 1px var(--primary); color: var(--primary); font-weight: 700; }
.cal-d.sel { background: var(--primary); color: #fff; font-weight: 700; box-shadow: none; }
.cal-foot { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 9px; padding-top: 9px; border-top: 1px solid var(--border); }
.cal-foot .btn { padding: 5px 9px; font-size: 11.5px; }
body.dark .cal-d.out { color: #5b6572; }

/* ---------- pagination ---------- */
.pager {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  margin: 12px 2px 0; font-size: 12px; color: var(--muted);
}
.pg-info { font-weight: 600; }
.pg-nav { display: flex; align-items: center; gap: 8px; margin-inline-start: auto; }
.pg-page { font-variant-numeric: tabular-nums; white-space: nowrap; }
.pg-size { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.pg-size select { width: auto; padding: 5px 8px; font-size: 12px; background: var(--panel); }
.pager .btn[disabled] { opacity: .4; cursor: default; }
/* Repère « filtres actifs » : une liste filtrée ne doit jamais passer pour une liste vide. */
.f-note { display: inline-flex; align-items: center; gap: 8px; margin-inline-start: auto; }
.filters .f-note { margin-inline-start: auto; }
/* Un compte posé à côté d'un champ ÉTIQUETÉ se lit à la hauteur de la case, pas du milieu du bloc
   étiquette + case : « 0 article compté sur 3 » flottait au niveau de l'étiquette (10.14.0). */
.filters > label + span.small { align-self: flex-end; padding-block-end: 11px; }

/* ---------- fiche client ---------- */
.kv { display: flex; flex-direction: column; gap: 9px; font-size: 13px; }
.kv > div { display: flex; gap: 12px; align-items: baseline; }
.kv > div > span:first-child { color: var(--muted); min-width: 110px; flex-shrink: 0; font-size: 12px; }
.kv > div > span:last-child { line-height: 1.5; }
/* 10.13.0 — la colonne des libellés s'aligne sur le PLUS LONG d'entre eux. Chaque rangée était sa
   propre ligne flex : « Ce qu'elle ne fait pas » (112 px, au-delà du minimum de 110) poussait SA
   valeur de 2 px, et la colonne des valeurs se voyait décalée (vu à la souris sur l'assistant du
   Cabinet). Une grille dont chaque rangée est une sous-grille décide la colonne une fois, pour toutes
   (règle 10.12.0 : aucune colonne décidée ligne par ligne). `.kv.two` garde sa propre grille. */
.kv:not(.two) { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 9px 12px; }
.kv:not(.two) > div { display: grid; grid-column: 1 / -1; grid-template-columns: subgrid; align-items: baseline; }
/* Le récapitulatif avant un geste irréversible (émettre une facture) : à qui, quand, combien — posé
   sur un fond qui le détache de la phrase qui suit (10.12.0). */
.kv.recap-emission { background: var(--bg); border-radius: 10px; padding: 12px 14px; margin: 6px 0 12px; }

/* Ce qui doit être lu avant d'agir : jamais une ligne grise de plus. Ces règles vivaient dans la
   feuille du Cabinet — et l'app entreprise posait `.warn-box` (la question du comptable, les
   questions restées sans réponse) et `.code-box` (la clé d'une licence) sur un texte NU : une classe
   que la feuille ne connaît pas ne se voit nulle part (10.12.0 ; `.mono` en 8.1.0, `th.r` en 7.23.0).
   Elles vivent ici, pour les deux applications. */
/* 10.14.0 — acheter sa licence depuis Paramètres → Licence : deux offres côte à côte, le prix
   lu sur le serveur, le bouton au pied de chaque carte (aligné d'une carte à l'autre). */
.lic-achat { margin-block: 14px 4px; padding: 12px 14px; border-radius: 10px; background: var(--bg); display: flex; flex-direction: column; gap: 10px; }
.lic-achat > p { margin: 0; }
.lic-achat .inline { flex-wrap: wrap; }
.lic-offres { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 10px; }
.lic-offre { display: flex; flex-direction: column; gap: 6px; padding: 12px; border: 1px solid var(--border); border-radius: 10px; background: var(--panel); }
.lic-offre > .btn { margin-block-start: auto; align-self: flex-start; }
.lic-prix { font-size: 17px; font-weight: 600; font-variant-numeric: tabular-nums; }
.warn-box {
  background: var(--warning-soft); border-inline-start: 3px solid var(--warning); border-radius: 8px;
  padding: 11px 14px; font-size: 13px; line-height: 1.55;
}
.warn-box.grave { background: var(--danger-soft); border-inline-start-color: var(--danger); }
/* Les deux soeurs de `.warn-box`, posées en 9.1.0 dans la feuille du Cabinet — elles y étaient
   UTILISÉES sans être définies, et une classe qui n'existe pas dans la feuille ne se voit nulle part.
   `ok-box` : ce qu'on peut affirmer. `info-box` : ce qu'on explique — un écart ATTENDU (une ligne en
   double dans un tableur) n'est pas un avertissement, et du orange sur une situation normale apprend
   à ignorer l'orange (8.0.1). Elles vivent ici depuis la 10.14.0, pour les deux applications. */
.ok-box {
  background: var(--success-soft); border-inline-start: 3px solid var(--success); border-radius: 8px;
  padding: 11px 14px; font-size: 13px; line-height: 1.55;
}
.info-box {
  background: var(--info-soft); border-inline-start: 3px solid var(--info); border-radius: 8px;
  padding: 11px 14px; font-size: 13px; line-height: 1.55;
}
/* Une clé se copie d'un bloc et n'a pas d'espace : sans `overflow-wrap`, elle sortait du cadre. */
.code-box {
  background: var(--bg); border: 1px solid var(--border); border-radius: 10px; padding: 12px 14px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; line-height: 1.7;
  white-space: pre-wrap; overflow-wrap: anywhere; margin: 0;
}
/* Une fenêtre qui porte des LIGNES de facture s'élargit (10.12.0) : dans les 580 px d'une fenêtre
   ordinaire, la désignation — la colonne qu'on lit — n'avait que 135 px à côté de quatre colonnes
   de chiffres. La leçon de la grille des lignes de l'éditeur, portée aux formulaires.
   Et une fenêtre qui porte une LISTE aussi : le relevé de compte d'un client — six colonnes, toutes
   sans retour à la ligne — cachait « Reste dû » et le total derrière un défilement de côté, dans
   les 580 px d'une fenêtre ordinaire (250 à 290 px cachés, trouvé par `e2e:entreprise-rendu`).
   Le total est la raison d'être d'un relevé.
   `:where()` garde à la règle la spécificité d'un simple `.modal` : sans lui, `:has(table.list)`
   pesait (0,2,1) et ÉCRASAIT les largeurs voulues du Cabinet (`.modal.cab-large`, 1 240 px, pour
   la grille du modèle de liasse) — la fenêtre retombait à 860 px et cachait 25 px de colonnes
   derrière un défilement de côté. La règle générale qui avale l'exception, une fois de plus.
   Sauf la liste des justificatifs d'un mouvement (10.14.1, S-04) : elle naît au premier fichier joint,
   et la fenêtre passait de 580 à 860 px sous le curseur — « Enregistrer » partait de 140 px (H-E1).
   Ses quatre colonnes courtes tiennent dans la fenêtre ordinaire. */
.modal:where(:has(table.mini, table.lines-edit, table.list:not(.pj))) { width: 860px; }
/* L'import depuis un tableur (10.14.0). La fenêtre a sa largeur DÈS l'ouverture : l'aperçu qui
   apparaît au collage l'aurait élargie sous le curseur, et « Importer » aurait bougé au moment où on
   le vise (H-E1, dans une fenêtre). Le texte collé garde ses colonnes (chasse fixe, pas de retour à
   la ligne) : c'est ce qui permet de reconnaître son tableur dans la case. */
.modal:where(:has(.import-tableau)) { width: 860px; }
.import-tableau textarea { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; line-height: 1.6; white-space: pre; overflow-x: auto; tab-size: 18; }
.imp-source { margin-block-start: 8px; }
.imp-bilan { margin-block: 16px 10px; line-height: 1.6; }
.imp-cadre { margin-block: 8px 6px; max-height: 320px; overflow-y: auto; }
.imp-table th { vertical-align: bottom; min-width: 150px; }
.imp-table th.imp-st-h { min-width: 0; }
.imp-table th select { width: 100%; }
/* Une liste de colonne se lit EN ENTIER : « Personne à co… » ou « Code postal, vi… » obligeait à
   l'ouvrir pour savoir ce que SkanFact avait reconnu (vu à la souris, sur une liste collée depuis
   LibreOffice). La colonne prend la largeur de la plus longue réponse possible. */
.imp-table th select { min-inline-size: max-content; }
.imp-table .imp-titre { font-weight: 400; text-transform: none; letter-spacing: 0; margin-block-start: 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 200px; }
.imp-table td { max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Une colonne sur « Ignorer » n'entre pas : elle se lit en retrait, comme ce qui ne compte pas. */
.imp-table td.imp-ignore { color: var(--muted); opacity: .55; }
.imp-refus, .imp-doublons { margin-block: 10px; }
.imp-notes-t { margin-block: 12px 0; }
.imp-autre { margin-block: 16px 10px; }
/* Le geste et sa réserve dans une rangée dont le `gap` décide des deux écarts (T-56) : posé dans la
   phrase, le bouton passait à la ligne collé sous elle. */
.imp-autre-geste { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin-block-start: 10px; }
.imp-refus ul, .imp-doublons ul, .imp-notes { margin-block: 6px; padding-inline-start: 20px; max-height: 150px; overflow-y: auto; }
.pp-import { align-self: flex-start; }
/* « Ta facture à ton image » (10.14.0). Les choix à gauche, la facture entière à droite : on regarde
   ce qu'on règle, sans aller ouvrir un devis. La fenêtre a sa largeur DÈS l'ouverture — l'aperçu qui
   se charge ne l'élargit pas sous le curseur (H-E1, dans une fenêtre). */
.modal:where(:has(.marque)) { width: 900px; }
.mq-grille { display: grid; grid-template-columns: minmax(0, 320px) minmax(0, 1fr); gap: 24px; align-items: start; margin-block-start: 14px; }
.mq-choix { display: flex; flex-direction: column; gap: 18px; }
.mq-bloc .inline { flex-wrap: wrap; }
.mq-t { display: flex; align-items: center; gap: 4px; font-size: 13px; font-weight: 600; color: var(--text); margin-block-end: 8px; }
/* L'image choisie se voit avant de se voir sur la facture : un cadre pointillé tant qu'elle manque,
   pour dire qu'une place l'attend. Sa hauteur est FIXE : l'image arrivée ne pousse rien sous le
   curseur (vu à la souris : 6 px à chaque image, et le bouton suivant descendait). */
.mq-image { display: grid; place-items: center; block-size: 72px; padding: 8px; margin-block-end: 8px; border: 1px dashed var(--border); border-radius: 10px; background: var(--bg); text-align: center; overflow: hidden; }
/* Une image posée se voit sur SON papier : blanc dans les deux thèmes, comme sur le PDF — un logo
   sombre sur le fond d'un thème sombre ne se lirait pas, et ce n'est pas ce que le client verra. */
.mq-image:has(img) { background: #fff; border-style: solid; }
.mq-image img { max-block-size: 52px; max-inline-size: 100%; object-fit: contain; }
.mq-nuancier { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
/* Une pastille est un bouton qui n'a que sa couleur : l'anneau dit laquelle est posée, la bordure de
   la couleur du panneau la détache de lui dans les deux thèmes. */
.mq-nuance { inline-size: 28px; block-size: 28px; padding: 0; border-radius: 50%; border: 2px solid var(--panel); background: var(--nuance); box-shadow: 0 0 0 1px var(--border); cursor: pointer; }
.mq-nuance:hover { box-shadow: 0 0 0 2px var(--muted); }
.mq-nuance[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--text); }
.mq-nuance:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.mq-autre { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }
/* La pastille « Autre… » n'a pas les 72 px d'un sélecteur de couleur de formulaire. */
.mq-autre input[type=color] { inline-size: 44px; block-size: 30px; padding: 2px; }
/* Une couleur trop claire se dit dans la ligne de SON titre, poussée au bout : elle n'ajoute aucune
   ligne, donc elle ne déplace rien (la remarque d'un RIB douteux, 10.12.0, pour une couleur). */
.mq-note { margin-inline-start: auto; font-size: 12px; font-weight: 600; color: var(--warning); white-space: nowrap; }
/* La page imprimée reste claire dans les deux thèmes, comme le PDF : c'est ce que le client reçoit. */
.mq-apercu-cadre iframe { display: block; inline-size: 100%; block-size: min(60vh, 680px); border: 1px solid var(--border); border-radius: 12px; background: #e9edf1; }
.mq-param { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; }
/* Le résumé des Paramètres : ce qui est posé, en trois colonnes — on le regarde ici, on le change
   dans la fenêtre qui le montre sur une facture. */
.mq-resume { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px 24px; align-items: start; }
.mq-mini { margin-block-end: 0; }
.mq-resume-c { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text); min-block-size: 30px; }
.mq-pastille { inline-size: 22px; block-size: 22px; border-radius: 50%; background: var(--nuance); box-shadow: 0 0 0 1px var(--border); flex: 0 0 auto; }
/* En sombre, une couleur foncée (Ardoise, le noir bleuté du texte) se fondait dans le fond : son
   anneau prend la couleur des textes discrets, et l'état « choisi » garde le sien (vu à l'écran). */
body.dark .mq-nuance, body.dark .mq-pastille { box-shadow: 0 0 0 1px var(--muted); }
body.dark .mq-nuance:hover { box-shadow: 0 0 0 2px var(--muted); }
body.dark .mq-nuance[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--text); }
.warn-link { color: var(--warning); font-weight: 600; text-decoration: none; }
.warn-link:hover { text-decoration: underline; }

/* ---------- bulles d'explication « i » ---------- */
button.i {
  font: inherit; font-size: 10px; font-weight: 800; line-height: 1; font-style: italic; font-family: Georgia, "Times New Roman", serif;
  width: 15px; height: 15px; flex: 0 0 15px; padding: 0; border: none; border-radius: 50%; cursor: pointer; vertical-align: 1px;
  background: var(--ink-soft); color: var(--muted); display: inline-grid; place-items: center; transition: background .12s, color .12s;
}
button.i:hover, button.i:focus-visible { background: var(--primary-soft-2); color: var(--primary); outline: none; }
.check button.i, h2 button.i, .lbl button.i, .k-label button.i { margin-inline-start: 2px; }
/* Une bulle est une ANNOTATION : elle doit rester collée au libellé qu'elle explique, et ces 2 px
   sont justes. Mais DEUX bulles côte à côte ne sont pas une annotation, ce sont deux explications
   différentes et deux cibles différentes — collées, elles se lisent comme un seul objet et on ne
   sait pas laquelle on clique. C'est le défaut de la 7.29.0 (deux boutons collés par un gabarit
   sans espace), à l'échelle de 15 px. */
button.i + button.i { margin-inline-start: 6px; }
/* Et pour la même raison : une bulle qui suit un BOUTON est une seconde cible collée à la première.
   Les 2 px de l'annotation valent face à un LIBELLÉ, qui ne se clique pas ; face à un bouton, le
   bord de l'un est le bord de l'autre et on ne sait pas lequel on vise. */
.btn + button.i { margin-inline-start: 6px; }
/* La bulle d'un bouton caché se cache avec lui (10.12.0). Sortie du bouton — un bouton dans un
   bouton n'existe pas en HTML —, celle de « Lire une photo… » restait seule à l'écran pendant que
   la lecture est en pause : deux « i » orphelins à côté de « Joindre un justificatif… ». */
.btn[hidden] + button.i { display: none; }
/* Le dernier mot et sa bulle ne se séparent pas (10.12.0, `collerBulles`). Une espace insécable ne
   les retenait pas : la bulle est un élément en ligne ATOMIQUE, et Chrome coupe avant lui même
   derrière une insécable. Et deux bulles voisines gardent leur écart : la première vit maintenant
   dans ce <span>, donc `button.i + button.i` ne la voit plus. */
.colle-bulle { white-space: nowrap; }
.colle-bulle + button.i { margin-inline-start: 6px; }
/* libellé + bulle sur une seule ligne dans un champ (.field est une colonne flex) */
.fl { display: flex; align-items: center; gap: 4px; }
/* Un lien qui dépend de la valeur du champ vit dans la LIGNE DU LIBELLÉ (10.12.0) : il apparaît et
   disparaît sans rien déplacer. Posé sous le champ, « Fiche du client » poussait tout le
   formulaire de 45 px au choix du client, et le clic suivant tombait dans le vide. Souligné et
   coloré au repos : un lien sans ces deux signes ne se reconnaît pas (9.4.2). */
.fl-ligne { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
/* Les justificatifs d'un mouvement libre (10.14.1, S-04) : un bloc sous le formulaire, séparé de la
   dernière rangée de champs comme une rangée de plus. */
.mv-pj { margin-block-start: 14px; }
/* Son titre a la voix des autres libellés de la fenêtre (12 px, gris, gras) : posé hors d'un `.field`,
   il sortait en texte courant, plus gros que « Date » et « Montant » juste au-dessus. */
.mv-pj > .field { margin-block-end: 5px; }
/* Le fichier joint qui a fait sortir une ligne de la recherche : une ligne sous le numéro, bornée —
   un nom de fichier long ne doit pas élargir la colonne (elle est en nowrap) au détriment des montants. */
.att-trouve { max-inline-size: 24ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 400; }
.lien-fl { font: inherit; font-size: 12px; font-weight: 600; background: none; border: none; padding: 0; color: var(--primary); cursor: pointer; text-decoration: underline; text-underline-offset: 2px; white-space: nowrap; }
.lien-fl:hover { text-decoration-thickness: 2px; }
.i-demo { display: inline-grid; place-items: center; width: 15px; height: 15px; border-radius: 50%; background: var(--primary-soft-2); color: var(--primary); font: italic 800 10px/1 Georgia, serif; vertical-align: 1px; }

#info-pop {
  position: fixed; z-index: 900; width: 340px; max-width: calc(100vw - 16px); background: var(--panel);
  border: 1px solid var(--border); border-radius: 12px; box-shadow: 0 16px 50px rgba(20,40,60,.22); overflow: hidden;
}
#info-pop .ip-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 10px 10px 10px 14px; font-weight: 700; font-size: 13px; background: var(--primary-soft); color: var(--primary); }
#info-pop .ip-close { font: inherit; font-size: 12px; background: transparent; border: none; color: inherit; cursor: pointer; opacity: .7; padding: 2px 4px; border-radius: 6px; }
#info-pop .ip-close:hover { opacity: 1; background: rgba(0,0,0,.06); }
#info-pop .ip-body { padding: 12px 14px 14px; font-size: 13px; line-height: 1.55; color: var(--text); font-weight: 400; }
#info-pop .ip-body b { font-weight: 650; }
#info-pop .ip-body em { color: var(--warning); font-style: normal; font-weight: 600; }

/* ---------- onglets ---------- */
.tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--border); margin-bottom: 20px; flex-wrap: wrap; }
.tabs button {
  font: inherit; font-size: 13px; font-weight: 600; color: var(--muted); background: transparent; border: none;
  border-bottom: 2px solid transparent; padding: 9px 14px; cursor: pointer; white-space: nowrap; margin-bottom: -1px;
}
.tabs button:hover { color: var(--text); }
.tabs button.active { color: var(--primary); border-bottom-color: var(--primary); }
/* compteur discret dans un onglet : il annonce ce qu'on va trouver sans crier comme une pastille d'alerte */
.tab-n { display: inline-block; min-width: 18px; padding: 1px 5px; margin-inline-start: 4px; border-radius: 999px;
  background: var(--ink-soft); color: var(--muted); font-size: 11px; font-weight: 700; vertical-align: 1px; }
.tabs button.active .tab-n { background: var(--primary-soft); color: var(--primary); }

/* ---------- barre « Enregistrer » flottante ---------- */
.save-bar {
  position: sticky; bottom: 0; z-index: 20; display: flex; align-items: center; gap: 10px; justify-content: flex-end;
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); padding: 12px 16px;
  margin-top: 18px; box-shadow: 0 -6px 24px rgba(20,40,60,.10);
}
.save-bar span { margin-inline-end: auto; font-size: 13px; font-weight: 600; color: var(--warning); }

/* ---------- zone sensible ---------- */
.danger-zone { border-color: #f0d5d2; }
.danger-zone h2 { color: var(--danger); }
.dz-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 12px 0; border-top: 1px solid var(--border); }
.dz-row:first-of-type { border-top: none; padding-top: 0; }
body.dark .danger-zone { border-color: #4a2a2a; }

/* ---------- éditeur : marqueur, aperçu, outils de ligne ---------- */
/* Le marqueur « modifications non enregistrées » vit HORS du flux, dans la marge sous l'en-tête
   (10.12.0, H-E19). Posé à côté du titre, il élargissait l'en-tête au premier geste : à 1440 px la
   barre d'actions passait sur une seconde rangée, et tout le formulaire descendait de 40 px sous le
   curseur — le clic suivant tombait à côté. C'est le point de la grille de saisie du Cabinet (H-2),
   jamais porté ici : ce qui APPARAÎT ne pousse rien. Ancré sur l'en-tête entier et non sur le
   titre : quand l'en-tête passe sur deux rangées, sous le titre, c'est la rangée des actions. */
.page-head { position: relative; }
.dirty-dot { position: absolute; inset-block-start: 100%; inset-inline-start: 0; margin-block-start: 2px;
  font-size: 11px; line-height: 14px; font-weight: 700; letter-spacing: 0; white-space: nowrap;
  color: var(--warning); background: var(--warning-soft); border-radius: 999px; padding: 2px 9px; }
.editor .preview { display: flex; flex-direction: column; gap: 8px; height: calc(100vh - 96px); }
.pv-head { display: flex; align-items: center; gap: 10px; }
.pv-pages { margin-inline-start: auto; font-size: 11px; color: var(--muted); background: var(--ink-soft); border-radius: 999px; padding: 2px 9px; font-weight: 600; }
.pv-pages.warn { background: var(--warning-soft); color: var(--warning); }
.preview iframe { flex: 1 1 auto; height: auto; min-height: 0; }
/* Fiche de contrat : l'aperçu se cale sur la hauteur du contenu, la liste des factures
   passe dessous sur toute la largeur — à sept colonnes elle ne tient pas dans une demi-page. */
.editor-short .preview { height: auto; min-height: 420px; max-height: calc(100vh - 140px); }
.editor.no-preview { grid-template-columns: 1fr; }
/* Saisie d'un achat : pas d'aperçu (le document existe déjà, c'est celui du fournisseur), donc pleine
   largeur — mais bornée, une ligne de formulaire de 1400 px ne se lit pas. */
.buy-editor > div { max-width: 1080px; }
/* ---------- déclaration de TVA : trois lignes et un résultat, lisibles d'un coup d'œil ---------- */
.vat-box { max-width: 620px; }
.vat-line { display: flex; justify-content: space-between; align-items: baseline; gap: 16px; padding: 10px 0; border-bottom: 1px solid var(--border); font-size: 14px; }
.vat-line .num { font-variant-numeric: tabular-nums; font-weight: 600; white-space: nowrap; }
.vat-line.minus { color: var(--muted); }
/* Sous-ligne d'un détail (« dont … ») : plus petite, décalée, sans trait de séparation. */
.vat-line.sub-line { padding: 2px 0 8px 18px; border-bottom: none; font-size: 13px; }
.vat-line.sub-line .num { font-weight: 500; }
.vat-line.total { border-bottom: none; border-top: 2px solid var(--text); margin-top: 4px; padding-top: 14px; font-size: 16px; font-weight: 700; }
.vat-line.total.due .num { color: var(--danger); font-size: 20px; }
.vat-line.total.ok .num { color: var(--success); font-size: 20px; }
tr.row-ok td { background: rgba(28, 143, 90, .05); }
.compact-stats .stat .val { font-size: 19px; }
.stat .val.ok { color: var(--success); }
.stat .val.due { color: var(--danger); }
/* Aperçu masqué : la colonne disparaît ENTIÈREMENT. Jusqu'à la 7.13.0 elle restait là, réduite à son
   seul bouton, qui se retrouvait donc à la fin du formulaire — trois écrans plus bas que là où on
   venait de cliquer. L'interrupteur vit maintenant dans la barre d'actions, en haut, et ne bouge
   plus (.pv-cmd). */
.editor.no-preview .preview { display: none; }
.pv-cmd { display: inline-flex; gap: 4px; align-items: center; padding-inline-end: 6px; margin-inline-end: 2px; border-inline-end: 1px solid var(--border); }
.pv-cmd .btn.on { background: var(--primary-soft); color: var(--primary); border-color: transparent; }

/* ---------- l'aperçu en grand (7.13.0) ----------
   Une colonne de 430 px montre une page A4 à 54 %, et à 35 % sur un portable : on distingue une mise
   en page, on ne lit ni un prix ni une mention légale. Sous la barre modale (400) : une question
   posée par-dessus doit rester devant. */
#pv-full { position: fixed; inset: 0; z-index: 300; background: rgba(20, 32, 44, .55); display: flex; flex-direction: column; }
.pv-full-bar { display: flex; align-items: center; gap: 12px; padding: 10px 16px; background: var(--panel); border-bottom: 1px solid var(--border); flex: 0 0 auto; flex-wrap: wrap; }
.pv-full-bar #pv-full-close { margin-inline-start: 0; }
.pv-zoom { display: flex; align-items: center; gap: 6px; margin-inline-start: auto; }
.pv-zoom .btn { min-width: 32px; justify-content: center; }
.pv-zoom #pv-zoom-val { min-width: 96px; text-align: center; font-variant-numeric: tabular-nums; }
.pv-full-body { flex: 1 1 auto; min-height: 0; padding: 14px; }
.pv-full-body iframe { width: 100%; height: 100%; border: none; border-radius: var(--radius); background: #e9edf1; }
/* les flèches du champ numérique mangent la colonne Qté sur un écran étroit */
table.lines-edit input[type=number]::-webkit-inner-spin-button,
table.lines-edit input[type=number]::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
table.lines-edit input.num { padding-inline-start: 6px; padding-inline-end: 6px; }
table.mini input[type=number]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
td.line-tools { white-space: nowrap; text-align: end; padding-top: 9px; }
td.line-tools .btn { padding: 4px 6px; min-width: 24px; justify-content: center; color: var(--muted); }
td.line-tools .btn:hover:not(:disabled) { color: var(--text); }
/* Monter · Descendre · Dupliquer · Supprimer : une grappe de pictogrammes, volontairement dense —
   c'est une seule famille de gestes sur une seule ligne. Mais sa densité venait de l'espace entre
   deux balises du gabarit (3,9 px), c'est-à-dire de rien : réécrire ces quatre boutons sur une
   seule ligne l'aurait ramenée à zéro sans que personne le veuille. On la DÉCIDE. */
td.line-tools .btn + .btn { margin-inline-start: 2px; }
.link-add { font: inherit; font-size: 12px; background: none; border: none; color: var(--muted); cursor: pointer; padding: 4px 0 0; text-decoration: underline; text-underline-offset: 2px; }
.link-add:hover { color: var(--primary); }

/* ---------- Aide ---------- */
.lead { font-size: 15px; color: var(--muted); max-width: 760px; margin: -6px 0 20px; line-height: 1.55; }
/* ---- l'Aide (refonte 7.27.0, sur celle de 7.23.0) ----
   Ce qui n'allait pas, mesuré sur captures :
   — sept cartes grises identiques qui n'annonçaient que leur NOMBRE d'articles, et 400 px de vide
     sous elles. Rien ne distinguait « Ton équipe » de « Encaisser » ;
   — ouvrir un thème redessinait la liste ENTIÈRE des sept thèmes juste en dessous, le thème ouvert
     compris, sans état actif ni moyen de refermer ;
   — le fil d'Ariane s'empilait VERTICALEMENT au milieu de la page : `.help-fil` était un <nav>, et
     la règle `nav { display:flex; flex-direction: column }` de la barre latérale l'emportait ;
   — « article suivant » se posait à l'extrémité droite de la page, 250 px à côté de la colonne de
     l'article qu'il prolonge.
   Maintenant : un plan coloré où les trente-deux articles se voient d'un coup, et un article qui
   tient dans une colonne, avec son sommaire à côté. */

/* Les sept couleurs des thèmes. Elles vivent ICI et jamais dans guide.js : une couleur écrite dans
   du JavaScript ne sait pas se retourner en mode sombre. Un test vérifie qu'aucun thème n'est
   oublié — sinon un huitième naîtrait gris au milieu de sept colorés. */
/* Le repli, posé sur `body` (spécificité 0,0,1) pour qu'une classe de thème le batte toujours,
   quel que soit l'ordre des règles. Sans lui, un article sans thème rendrait `3px solid` avec une
   couleur vide : la bordure disparaîtrait sans que rien ne le signale. */
body { --th: var(--primary); --th-bg: var(--primary-soft); }
.th-commencer { --th: #0c8478; --th-bg: #e4f4f2; }
.th-vendre    { --th: #2a6fd6; --th-bg: #e8f0fc; }
.th-encaisser { --th: #1c8f5a; --th-bg: #e3f6ec; }
.th-acheter   { --th: #b4620f; --th-bg: #fdf0e1; }
.th-declarer  { --th: #6b4ec9; --th-bg: #efeafc; }
.th-equipe    { --th: #b83f7a; --th-bg: #fceaf2; }
.th-piloter   { --th: #0e7c96; --th-bg: #e3f2f6; }
body.dark .th-commencer { --th: #37c9b8; --th-bg: #11332f; }
body.dark .th-vendre    { --th: #77aaf5; --th-bg: #182a42; }
body.dark .th-encaisser { --th: #47cc8c; --th-bg: #133224; }
body.dark .th-acheter   { --th: #e8a45c; --th-bg: #38291a; }
body.dark .th-declarer  { --th: #a992f0; --th-bg: #272142; }
body.dark .th-equipe    { --th: #ee85b6; --th-bg: #3a1f2e; }
body.dark .th-piloter   { --th: #4dbcd6; --th-bg: #12303a; }

/* La recherche est la porte d'entrée de l'aide : elle a la taille d'une porte. */
.help-search { position: relative; max-width: 640px; margin: 0 0 20px; }
/* 10.12.0 (U-30) — la loupe se centre sur le CHAMP, pas sur le bloc : le compte des résultats
   s'affiche sous le champ et agrandit le bloc, et la loupe descendait alors de 11 px sous la ligne
   du texte (mesuré dans l'application du comptable, puis retrouvé dans l'autre). */
.help-search .hs-champ { position: relative; display: block; }
.help-search .hs-loupe {
  position: absolute; inset-inline-start: 14px; top: 50%; transform: translateY(-50%); width: 17px; height: 17px;
  stroke: var(--muted); fill: none; stroke-width: 2; stroke-linecap: round; pointer-events: none;
}
/* `#aide-q` et pas `.help-search input` : jusqu'à la 10.14.1 la règle générale des champs portait
   quatre `:not(…)` nus (spécificité 0,4,1) et battait n'importe quelle classe. Sans l'identifiant,
   le `padding-left` n'était jamais appliqué — la loupe se posait SUR la première lettre du texte.
   Même famille que le `th.r` des colonnes en 7.23.0 : le HTML est juste, la feuille tranche. */
.help-search #aide-q, .help-search #guide-q { padding: 12px 14px 12px 41px; border-radius: 12px; }
.help-search .help-count { padding: 8px 2px 0; }

/* Le plan : sept pastilles colorées, qui mènent chacune à sa section. */
.help-rail { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 20px; }
.help-chip {
  display: inline-flex; align-items: center; gap: 8px; cursor: pointer; font: inherit;
  font-size: 13px; font-weight: 600; color: var(--th); background: var(--th-bg);
  border: 1px solid transparent; border-radius: 999px; padding: 7px 14px 7px 11px;
}
.help-chip:hover { border-color: var(--th); }
.help-chip svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.help-chip .hc-n { opacity: .7; font-weight: 600; }

/* Une section par thème, ses articles visibles : un plan montre le territoire, il ne le cache pas
   derrière un clic qui ne promet qu'un nombre. */
.help-secs { display: grid; gap: 16px; }
.help-sec { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; scroll-margin-top: 10px; }
.help-sec > header { display: flex; align-items: center; gap: 13px; padding: 15px 18px; background: var(--th-bg); }
.help-sec .hs-ico { width: 38px; height: 38px; border-radius: 11px; background: var(--th); color: #fff; display: grid; place-items: center; flex-shrink: 0; }
.help-sec .hs-ico svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
body.dark .help-sec .hs-ico { color: #0d1418; }
.help-sec .hs-t { min-width: 0; flex: 1 1 auto; }
.help-sec h2 { margin: 0; font-size: 15px; font-weight: 700; letter-spacing: -.1px; color: var(--text); text-transform: none; }
.help-sec .hs-s { margin: 2px 0 0; font-size: 12.5px; color: var(--muted); line-height: 1.45; }
.help-sec .hs-n { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .6px; color: var(--th); white-space: nowrap; flex-shrink: 0; }
/* Le repère posé par `pageFocus` : on vient d'arriver ici, et on le voit. */
.help-sec.flash { box-shadow: 0 0 0 2px var(--th); }

.help-arts { display: grid; grid-template-columns: repeat(auto-fill, minmax(248px, 1fr)); gap: 8px; padding: 14px 16px 16px; }
.help-art {
  display: flex; flex-direction: column; gap: 2px; text-align: start; cursor: pointer;
  background: transparent; border: 1px solid var(--border); border-inline-start: 3px solid var(--th);
  border-radius: 10px; padding: 10px 12px; transition: background .12s, border-color .12s;
}
.help-art:hover { background: var(--th-bg); border-color: var(--th); }
.help-art .ht { font-size: 13.5px; font-weight: 650; color: var(--text); }
.help-art .hs { font-size: 12px; color: var(--muted); line-height: 1.4; }
.help-art .hth { font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .5px; color: var(--th); margin-bottom: 1px; }
/* La pastille de l'article, utilisée par l'aide de l'app CABINET (qui n'a pas de thèmes : ce sont
   ses articles eux-mêmes qui portent une couleur). Posée ici, dans la feuille partagée, parce que
   c'est le même langage visuel — surtout pas un second jeu de règles dans cabinet.css, qui
   dériverait au premier ajustement (règle 6.8.0). */
.ha-ico {
  display: inline-grid; place-items: center; width: 22px; height: 22px; border-radius: 7px;
  background: var(--th); color: #fff; margin-inline-end: 8px; vertical-align: -4px; flex-shrink: 0;
}
.ha-ico svg { width: 14px; height: 14px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
body.dark .ha-ico { color: #0d1418; }
/* Huit articles au lieu de trente-deux : les cartes de l'aide du cabinet respirent davantage. */
.help-arts:has(.help-art.grande) { grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 10px; }
.help-art.grande { padding: 15px 16px 14px; gap: 4px; }
.help-art.grande .ht { font-size: 14.5px; display: flex; align-items: center; }
.help-art.grande .hs { font-size: 12.5px; }
.help-art mark { background: var(--th-bg); color: var(--text); font-weight: 650; border-radius: 3px; padding: 0 2px; }
/* Les résultats de recherche réutilisent la grille d'articles, mais hors d'une section : c'est
   elle qui leur donne leur panneau. */
.help-res { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); }

/* Le fil d'Ariane : d'où vient cet article, et comment remonter d'un cran. `flex-direction: row`
   est écrit noir sur blanc — c'est un <div> maintenant, mais la valeur par défaut d'un flex ne se
   devine pas quand une règle d'élément peut la renverser ailleurs. */
.help-fil { display: flex; flex-direction: row; align-items: center; gap: 7px; margin: 0 0 14px; color: var(--muted); flex-wrap: wrap; font-size: 13px; }
.help-fil button { background: none; border: 0; padding: 0; cursor: pointer; color: var(--primary); font-weight: 600; font-size: inherit; }
.help-fil button:hover { text-decoration: underline; }
.help-fil .sep { opacity: .5; }

/* L'article et son sommaire, côte à côte. Les deux colonnes sont bornées : « article suivant »
   vivait jusqu'ici à l'extrémité droite de la PAGE, très loin de la colonne qu'il prolonge. */
.help-layout { display: grid; grid-template-columns: minmax(0, 800px) minmax(0, 238px); gap: 26px; align-items: start; }
.help-layout.seul { grid-template-columns: minmax(0, 820px); }
.help-col { min-width: 0; }
.help-toc { position: sticky; top: 0; padding-top: 2px; }
.help-toc .tt { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .6px; color: var(--muted); padding: 0 0 8px 12px; }
.help-toc ol { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 1px; border-inline-start: 2px solid var(--border); }
.help-toc button {
  font: inherit; font-size: 12.5px; text-align: start; background: none; border: 0;
  box-shadow: -2px 0 0 transparent; color: var(--muted); cursor: pointer; padding: 6px 10px;
  line-height: 1.35; border-radius: 0 8px 8px 0; width: 100%;
}
.help-toc button:hover { color: var(--th); background: var(--th-bg); box-shadow: -2px 0 0 var(--th); }
@media (max-width: 1180px) {
  .help-layout { grid-template-columns: minmax(0, 1fr); }
  .help-toc { position: static; order: -1; margin-bottom: 12px; }
  .help-toc ol { flex-direction: row; flex-wrap: wrap; gap: 6px; border-inline-start: 0; }
  .help-toc button { width: auto; background: var(--th-bg); color: var(--th); border-radius: 999px; padding: 5px 12px; box-shadow: none; }
}
/* Le geste : on lit une explication pour FAIRE quelque chose. */
.help-geste { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: 26px 0 2px; padding-top: 18px; border-top: 1px solid var(--border); }
.help-suite { display: flex; justify-content: space-between; gap: 10px; margin-top: 14px; }
.help-body { padding: 26px 30px 24px; border-top: 3px solid var(--th); }
.help-kicker { display: flex; align-items: center; gap: 7px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .7px; color: var(--th); margin-bottom: 9px; }
.help-kicker svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.help-body h3 { scroll-margin-top: 12px; }
.help-body .help-h { font-size: 22px; font-weight: 700; letter-spacing: -.3px; color: var(--text); text-transform: none; margin: 0 0 4px; }
.help-body .help-sub { color: var(--muted); margin: 0 0 22px; font-size: 14px; }
.help-body h3 { font-size: 14px; font-weight: 700; margin: 22px 0 6px; color: var(--text); }
.help-body p { margin: 8px 0; line-height: 1.65; font-size: 14px; }
.help-body ul, .help-body ol { margin: 8px 0; padding-inline-start: 22px; line-height: 1.65; font-size: 14px; }
.help-body li { margin: 5px 0; }
.help-body b { font-weight: 650; }
.help-body em { color: var(--warning); font-style: normal; font-weight: 600; }
.help-foot { margin-top: 26px; padding-top: 14px; border-top: 1px solid var(--border); }
.gloss { margin: 10px 0; font-size: 14px; }
.gloss dt { font-weight: 650; margin-top: 12px; }
.gloss dd { margin: 2px 0 0; color: var(--muted); line-height: 1.55; }
table.keys { width: 100%; border-collapse: collapse; font-size: 14px; margin: 10px 0; }
table.keys th { text-align: start; font-weight: 500; padding: 7px 0; border-bottom: 1px solid var(--border); }
table.keys td { text-align: end; padding: 7px 0; border-bottom: 1px solid var(--border); white-space: nowrap; }
kbd { font: inherit; font-size: 12px; font-family: -apple-system, system-ui, sans-serif; background: var(--ink-soft); border: 1px solid var(--border); border-bottom-width: 2px; border-radius: 6px; padding: 2px 7px; margin-inline-start: 3px; }

/* ---------- petits écrans (1280 px : MacBook 13" en fenêtre) ---------- */
@media (max-width: 1340px) {
  .editor { grid-template-columns: 1fr 350px; }
  .dash-grid { grid-template-columns: 1fr; }
  .help-grid { grid-template-columns: 220px 1fr; }
  /* Les listes à huit colonnes débordaient de quelques dizaines de pixels et forçaient un
     défilement horizontal de toute la page. Des cellules plus serrées suffisent à les faire tenir. */
  table.list th, table.list td, table.list tfoot td { padding-inline-start: 10px; padding-inline-end: 10px; }
  table.list.compact th, table.list.compact td { padding-inline-start: 8px; padding-inline-end: 8px; }
  table.list th { letter-spacing: .2px; }
  td.row-actions > span { gap: 3px; }
}
@media (max-width: 1120px) {
  .editor { grid-template-columns: 1fr; }
  .editor .preview { position: static; height: 70vh; }
  .stats, .pay-grid { grid-template-columns: 1fr 1fr; }
  .help-grid { grid-template-columns: 1fr; }
  .help-nav { position: static; flex-direction: row; flex-wrap: wrap; }
  .help-nav .hs { display: none; }
}

/* ---------- assistant de première utilisation ---------- */
/* Ancré à la même hauteur que ses questions (voir `--haut-fenetre`) : centré, l'assistant se
   recentrait à chaque écran de hauteur différente, et « Continuer » changeait de place sous le doigt. */
#setup { position: fixed; inset: 0; z-index: 250; background: var(--bg); display: flex; align-items: flex-start; justify-content: center; padding: var(--haut-fenetre) 24px 24px; overflow-y: auto; }
.setup-card { width: 720px; max-width: 100%; background: var(--panel); border: 1px solid var(--border); border-radius: 20px; box-shadow: 0 24px 70px rgba(20,40,60,.16); overflow: hidden; }
.setup-head { display: flex; align-items: center; gap: 14px; padding: 24px 28px 18px; }
.setup-head .brand-mark { width: 40px; height: 40px; border-radius: 12px; font-size: 16px; flex-shrink: 0; }
.setup-t { font-size: 19px; font-weight: 700; letter-spacing: -.3px; }
.setup-s { font-size: 13px; color: var(--muted); margin-top: 2px; }
.setup-step { margin-inline-start: auto; font-size: 12px; color: var(--muted); font-weight: 600; white-space: nowrap; }
.setup-bar { height: 3px; background: var(--ink-soft); }
.setup-bar i { display: block; height: 100%; background: var(--primary); transition: width .25s; }
.setup-body { padding: 24px 28px; max-height: 58vh; overflow-y: auto; }
.setup-body p { margin: 10px 0; line-height: 1.6; font-size: 14px; }
.setup-body p:first-child { margin-top: 0; }
.setup-body em { color: var(--warning); font-style: normal; font-weight: 600; }
.setup-foot { display: flex; gap: 8px; justify-content: flex-end; padding: 16px 28px 22px; border-top: 1px solid var(--border); }
.setup-foot .btn-ghost { margin-inline-end: auto; color: var(--muted); }
/* La légende de l'étoile rejoint les boutons, à droite : « Passer… » garde seul la marge qui pousse. */
.setup-foot .oblig-note { margin-inline-end: 8px; }
/* La porte du premier lancement (10.14.0) : deux battants côte à côte, le recommandé en avant — les
   cartes sont celles de l'accueil (`.pp-choix`), pour que les deux portes se reconnaissent. Pas de
   défilement interne : sur un portable, c'est la fenêtre entière qui défile, jamais une boîte dans la boîte. */
.setup-card.porte .setup-body { max-height: none; }
.setup-porte { display: flex; flex-direction: column; gap: 16px; }
.setup-porte .sp-lead { margin: 0; font-size: 14.5px; color: color-mix(in srgb, var(--text) 82%, var(--muted)); }
.setup-porte .sp-choix { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
.setup-porte .sp-note p { margin: 0; font-size: 12.5px; line-height: 1.55; color: var(--muted); }
/* Les deux entrées de ceux qui ont déjà ce qu'il faut (10.14.0) : une rangée flex, jamais une phrase
   avec des boutons dedans — au passage à la ligne, un `gap` décide des deux écarts (T-56). */
.setup-porte .sp-autres { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; }
@media (max-width: 640px) { .setup-porte .sp-choix { grid-template-columns: minmax(0, 1fr); } }
.act-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.act { font: inherit; text-align: start; background: var(--panel); border: 1px solid var(--border); border-radius: 12px; padding: 12px 14px; cursor: pointer; display: flex; flex-direction: column; gap: 3px; transition: border-color .12s, background .12s; }
.act:hover { border-color: var(--primary); }
.act.sel { border-color: var(--primary); background: var(--primary-soft); }
.act-l { font-size: 13.5px; font-weight: 650; }
.act-s { font-size: 11.5px; color: var(--muted); }
.act.sel .act-l { color: var(--primary); }

/* ---------- écran de verrouillage ---------- */
#lock-screen { position: fixed; inset: 0; background: var(--bg); z-index: 200; display: flex; align-items: flex-start; justify-content: center; padding-top: var(--haut-fenetre); overflow-y: auto; }
.lock-card { width: 400px; max-width: 92vw; background: var(--panel); border: 1px solid var(--border); border-radius: 20px; padding: 32px 30px; box-shadow: 0 24px 70px rgba(20,40,60,.18); text-align: center; }
.lock-card .brand-mark { margin: 0 auto 14px; width: 44px; height: 44px; font-size: 18px; border-radius: 13px; }
.lock-card h2 { margin: 0 0 6px; font-size: 18px; }
.lock-card input { text-align: center; font-size: 16px; margin: 16px 0 12px; }
.lock-card .btn { width: 100%; justify-content: center; }
/* Deux boutons pleine largeur l'un sous l'autre se lisent comme UN bloc à deux étages : le
   second (« J'ai déjà un cabinet… ») touchait « Ouvrir » à zéro pixel. L'écart se pose ici, pour
   les deux applications, et non sur un identifiant. */
.lock-card .btn + .btn { margin-top: 10px; }
.lock-err { color: var(--danger); font-size: 13px; margin-bottom: 10px; }

/* ---------- Paramètres : le sommaire, la recherche, ce qui manque (7.30.0) ----------
   Trois ajouts qui répondent au même défaut : soixante réglages rangés dans des onglets dont on ne
   devine pas le contenu. Le sommaire montre ce que l'onglet ouvert contient, la recherche répond
   quand on ne sait pas dans quel onglet regarder, et le bandeau « il manque » dit à l'endroit où
   l'on remplit ce qui n'est pas rempli. */
/* La règle générale `.page-head .actions input` plafonne à 280 px : à cette largeur, le seul
   indice de ce qu'on peut taper était coupé au milieu d'un mot. */
.set-search input[type=search] { min-width: 320px; max-width: 400px; }
.set-somm { display: flex; flex-wrap: wrap; gap: 6px; margin: -8px 0 20px; }
.somm-chip {
  font: inherit; font-size: 12px; font-weight: 600; color: var(--muted);
  background: var(--panel); border: 1px solid var(--border); border-radius: 999px;
  padding: 5px 12px; cursor: pointer; transition: color .12s, border-color .12s, background .12s;
}
.somm-chip:hover { color: var(--primary); border-color: var(--primary); background: var(--primary-soft); }

.set-hits { display: grid; gap: 8px; }
.set-hit {
  font: inherit; text-align: start; background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 12px 16px; cursor: pointer;
  display: flex; flex-direction: column; gap: 3px; transition: border-color .12s;
}
.set-hit:hover { border-color: var(--primary); }
.set-hit-ou { font-size: 10.5px; text-transform: uppercase; letter-spacing: 1px; font-weight: 700; color: var(--muted); }
.set-hit-t { font-size: 14px; font-weight: 650; }
.set-hit-d { font-size: 12px; color: var(--muted); }
.set-hit mark { background: var(--primary-soft); color: var(--primary); border-radius: 3px; padding: 0 1px; }

/* « Il manque : matricule fiscal, RIB » — dit là où on le remplit, pas seulement dans « À faire ». */
.set-manque { background: var(--warning-soft); color: var(--warning); border-radius: var(--radius); padding: 12px 16px; font-size: 13px; }
.set-manque b { font-weight: 700; }
.upd-quand { margin-top: 10px; }

/* ---------- la rangée de chiffres (9.4.4) ----------
   Variante COMPACTE de `.stats`. Elle vit ici, avec le composant qu'elle décline, et pas dans la
   feuille du Cabinet : une variante rangée à part diverge du composant qu'elle prétend varier.

   Le défaut qu'elle corrige : quatre cartes de 180 px poussaient la liste des clients à 800 px sur
   un écran de 800 px de haut. Les mêmes quatre chiffres tiennent sur 72 px. */
.stats.rangee { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; margin-bottom: 14px; }
.stats.rangee .stat { padding: 11px 14px; display: block; text-align: start; }
.stats.rangee .pf-l { display: flex; align-items: baseline; gap: 7px; min-inline-size: 0; }
.stats.rangee .val { font-size: 19px; margin: 0; white-space: nowrap; }
.stats.rangee .lbl { text-transform: none; letter-spacing: 0; font-size: 12.5px; font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stats.rangee .sub { margin-top: 2px; display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Un chiffre qui NOMME un ensemble l'ouvre (7.15.0) — et il le dit par trois signes, au repos. */
.stats.rangee .stat.ouvre { cursor: pointer; font: inherit; color: inherit; width: 100%; }
.stats.rangee .stat.ouvre::after {
  content: '›'; position: absolute; inset-block-start: 9px; inset-inline-end: 12px;
  color: var(--muted); font-size: 15px; line-height: 1;
}
.stats.rangee .stat.ouvre:hover { border-color: var(--primary); }
.stats.rangee .stat.ouvre:hover::after { color: var(--primary); }
.stats.rangee .stat.ouvre:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

/* Un champ de recherche se reconnaît à sa loupe, et il n'a pas besoin de toute la largeur : 1 220 px
   pour chercher parmi cinq clients, c'est un champ étiré, pas un champ large. */
.champ-loupe { position: relative; display: inline-flex; flex: 1 1 260px; max-inline-size: 380px; }
/* Le texte commence après la loupe. Tant que la règle générale des champs pesait 0,4,1, ce décalage
   n'était jamais appliqué et la loupe se posait SUR la première lettre (le défaut de `.help-search`
   en 7.27.0) ; elle ne pèse plus rien depuis la 10.14.1. */
.champ-loupe input {
  padding-inline-start: 34px;
}
.champ-loupe::before {
  content: ''; position: absolute; inset-block-start: 50%; inset-inline-start: 12px;
  inline-size: 13px; block-size: 13px; margin-block-start: -7px; pointer-events: none; opacity: .5;
  background: currentColor;
  -webkit-mask: no-repeat center/13px url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='m20 20-3.6-3.6'/%3E%3C/svg%3E");
  mask: no-repeat center/13px url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='m20 20-3.6-3.6'/%3E%3C/svg%3E");
}

/* La légende d'un code couleur. Une pastille sans son mot n'est pas une information : elle n'est ni
   apprenable, ni accessible, ni lisible sur une impression. */
.legende { display: flex; flex-wrap: wrap; gap: 6px 18px; font-size: 12px; color: var(--muted); margin: 10px 0 4px; }
.legende .lg { display: inline-flex; align-items: center; }

/* La colonne d'actions d'une table LARGE reste sous la main (9.4.4). Une table de neuf colonnes
   déborde sur un portable : le menu de la ligne partait 17 px hors de l'écran et il fallait faire
   défiler de côté pour l'atteindre. Un geste qu'on doit chercher n'est pas un geste. */
/* 10.12.0 (U-02) — SEULE la colonne d'actions colle : son en-tête est vide. La règle visait
   « la dernière cellule d'en-tête » de TOUT tableau : sur un tableau dont la dernière colonne est une
   donnée (« À saisir » de la Production), l'en-tête collait seul au bord pendant que ses cellules
   défilaient, et il couvrait juillet et août. */
/* 10.12.0 — et SEULEMENT quand la colonne EST une colonne d'actions. « En-tête vide » ne le disait
   pas : la barre de marge de Marges › Analyse et le « Modifier » des Salariés ont un en-tête vide et
   des cellules ordinaires — leur en-tête restait collé seul, avec son ombre, sur un fond BLANC au
   milieu d'une rangée d'en-têtes gris. `:has()` lit la colonne elle-même ; et l'en-tête garde le
   fond de sa rangée — seule la cellule du corps a besoin d'un fond opaque pour cacher ce qui
   défile dessous, l'en-tête en a déjà un. */
.scroll-x table.list td.row-actions,
.scroll-x table.list:has(td.row-actions) thead th:last-child {
  position: sticky; inset-inline-end: 0;
  box-shadow: -8px 0 8px -8px rgba(20, 40, 60, .16);
}
.scroll-x table.list td.row-actions { background: var(--panel); }
.scroll-x table.list tr.clickable:hover td.row-actions { background: #f7fbfa; }
body.dark .scroll-x table.list tr.clickable:hover td.row-actions { background: #1f2731; }
body.dark .scroll-x table.list td.row-actions,
body.dark .scroll-x table.list:has(td.row-actions) thead th:last-child { box-shadow: -8px 0 8px -8px rgba(0, 0, 0, .5); }

/* ---------- 10.14.0 — la visite guidée (visite.js) ----------
   Trois couches : l'ombre SOUS les fenêtres (390 : une fenêtre ouverte reste lisible, c'est déjà
   elle qui assombrit la page), l'anneau et la bulle AU-DESSUS (891, 892 : on peut désigner un champ
   d'une fenêtre), sous la bulle « i » (900) et le message passager (950), qui se lisent par-dessus.
   L'ombre est un VOILE PERCÉ (10.14.1) : un dessin plein écran dont le masque découpe un trou pour
   la zone de l'étape, et un trou pour chaque bouton que la bulle NOMME — ils sont en pleine lumière,
   cliquables. L'ancienne ombre portée autour d'un seul cadre ne savait percer qu'un trou : un bouton
   cité hors de la zone restait dans le noir (Skander, 26/09/2026 : « pas qu'elle s'affiche en sombre
   en arrière-plan »). Rien de tout ça ne prend le clic, sauf la bulle : on n'est jamais enfermé.
   L'habillage (Skander, 24/09/2026 : « wow, beau, attirant ») : chaque chapitre porte la COULEUR de
   son domaine — les sept couleurs de l'Aide, `th-<nom>`, qui savent déjà se retourner en sombre —,
   le projecteur GLISSE d'une cible à la suivante, le texte entre du côté où l'on va, et la fin se
   fête. Tout ce qui bouge s'arrête pour qui a demandé moins d'animations. */
#visite-voile { position: fixed; inset: 0; width: 100vw; height: 100vh; z-index: 390; pointer-events: none; overflow: visible;
  animation: vb-voile .35s ease both; }
#visite-voile .vv-ombre { fill: rgba(10, 18, 28, .5); }
@keyframes vb-voile { from { opacity: 0; } }
/* Les boutons nommés hors de la zone : un anneau fin, sans pulsation — l'œil les trouve, ils ne
   crient pas. Sous la bulle (892), au niveau de l'anneau principal. */
#visite-nommes { position: fixed; inset-block-start: 0; inset-inline-start: 0; width: 0; height: 0; z-index: 891; pointer-events: none; }
#visite-nommes .visite-nomme { position: fixed; border-radius: 9px; pointer-events: none; border: 2px solid var(--th);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--th) 18%, transparent); }
#visite-anneau { position: fixed; z-index: 891; border-radius: 14px; pointer-events: none; border: 2px solid var(--th);
  box-shadow: 0 0 0 5px color-mix(in srgb, var(--th) 22%, transparent), 0 0 34px 2px color-mix(in srgb, var(--th) 36%, transparent); }
#visite-anneau.glisse { transition: top .45s cubic-bezier(.2, .8, .2, 1), inset .45s cubic-bezier(.2, .8, .2, 1),
  left .45s cubic-bezier(.2, .8, .2, 1), width .45s cubic-bezier(.2, .8, .2, 1), height .45s cubic-bezier(.2, .8, .2, 1); }
/* Le trou du voile glisse comme l'anneau : ses coordonnées sont des propriétés CSS du rectangle. */
#visite-voile.glisse .vv-trou { transition: x .45s cubic-bezier(.2, .8, .2, 1), y .45s cubic-bezier(.2, .8, .2, 1),
  width .45s cubic-bezier(.2, .8, .2, 1), height .45s cubic-bezier(.2, .8, .2, 1); }
#visite-anneau.pulse { animation: visite-pulse 1.8s cubic-bezier(.2, .8, .2, 1) infinite; }
@keyframes visite-pulse {
  0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--th) 55%, transparent), 0 0 26px 0 color-mix(in srgb, var(--th) 30%, transparent); }
  70% { box-shadow: 0 0 0 15px color-mix(in srgb, var(--th) 0%, transparent), 0 0 26px 0 color-mix(in srgb, var(--th) 30%, transparent); }
  100% { box-shadow: 0 0 0 0 transparent, 0 0 26px 0 color-mix(in srgb, var(--th) 30%, transparent); }
}
/* Le second anneau : le bouton dont parle la ligne survolée de la bulle — plus fin, en pointillé. */
#visite-point { position: fixed; z-index: 891; border-radius: 9px; pointer-events: none;
  border: 2px dashed var(--th); background: color-mix(in srgb, var(--th) 9%, transparent); }
/* Un nom cliqué dans la bulle : son bouton clignote un instant, pour qu'on le trouve. */
#visite-point.flash { border-style: solid; animation: vb-flash .45s ease-in-out 3; }
@keyframes vb-flash { 50% { background: color-mix(in srgb, var(--th) 32%, transparent); box-shadow: 0 0 0 6px color-mix(in srgb, var(--th) 25%, transparent); } }

.visite-bulle { position: fixed; z-index: 892; width: 388px; max-width: calc(100vw - 24px); box-sizing: border-box;
  background: var(--panel); color: var(--text); border: 1px solid color-mix(in srgb, var(--th) 24%, var(--border)); border-radius: 18px;
  box-shadow: 0 30px 70px -20px rgba(10, 18, 28, .5), 0 12px 28px -14px rgba(10, 18, 28, .3);
  font-size: 14px; line-height: 1.55; animation: vb-apparait .32s cubic-bezier(.2, .8, .2, 1) both; }
@keyframes vb-apparait { from { opacity: 0; transform: translateY(10px) scale(.97); } }
.visite-bulle.glisse { transition: top .42s cubic-bezier(.2, .8, .2, 1), left .42s cubic-bezier(.2, .8, .2, 1); }
.visite-bulle.liste { width: 448px; }
.visite-bulle.fin { width: 440px; }
/* Un GESTE attendu se reconnaît de loin : le cadre prend la couleur du domaine. */
.visite-bulle.faire { border-color: color-mix(in srgb, var(--th) 50%, var(--border)); }
/* La bulle EN RETRAIT (10.14.1) : pendant qu'on essaie ce qu'elle montre, ou qu'une liste est ouverte,
   elle se range dans un coin, en petit — ce qu'on vient d'ouvrir se voit en entier. */
.visite-bulle.mini { width: 330px; }
.visite-bulle.mini .vb-haut { padding: 11px 12px 10px 13px; }
.visite-bulle.mini .vb-ico { width: 30px; height: 30px; border-radius: 9px; }
.visite-bulle.mini .vb-ico svg { width: 16px; height: 16px; }
.visite-bulle.mini .vb-corps { padding: 11px 15px 0; font-size: 13.5px; }
.visite-bulle.mini .vb-pied { padding: 11px 15px 13px; }
.visite-bulle .vb-pointe { position: absolute; width: 14px; height: 14px; background: var(--panel);
  border: 1px solid color-mix(in srgb, var(--th) 24%, var(--border)); transform: rotate(45deg); border-radius: 3px; }
.visite-bulle .vb-pointe.teinte { background: var(--th-bg); }
.visite-bulle[data-cote="droite"] .vb-pointe { border-block-start-color: transparent; border-inline-end-color: transparent; }
.visite-bulle[data-cote="gauche"] .vb-pointe { border-block-end-color: transparent; border-inline-start-color: transparent; }
.visite-bulle[data-cote="dessous"] .vb-pointe { border-block-end-color: transparent; border-inline-end-color: transparent; }
.visite-bulle[data-cote="dessus"] .vb-pointe { border-block-start-color: transparent; border-inline-start-color: transparent; }

/* L'en-tête : le dessin du domaine, où l'on est, la pause — et la barre des chapitres. Son bord du
   haut est de la teinte PURE du domaine : c'est là que s'accroche la pointe quand la bulle est dessous. */
.visite-bulle .vb-haut { position: relative; padding: 14px 14px 13px 16px; border-radius: 17px 17px 0 0;
  background: linear-gradient(180deg, var(--th-bg) 0%, color-mix(in srgb, var(--th-bg) 55%, var(--panel)) 100%);
  border-block-end: 1px solid color-mix(in srgb, var(--th) 14%, transparent); }
.visite-bulle .vb-tete { display: flex; align-items: center; gap: 11px; }
.visite-bulle .vb-ico { flex: none; width: 36px; height: 36px; border-radius: 11px; display: grid; place-items: center;
  background: var(--th); color: #fff; box-shadow: 0 6px 14px -6px color-mix(in srgb, var(--th) 75%, transparent); }
.visite-bulle .vb-ico svg { width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.visite-bulle .vb-ou { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.visite-bulle .vb-sur { font-size: 10.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .8px;
  color: color-mix(in srgb, var(--th) 82%, var(--text)); font-variant-numeric: tabular-nums; }
/* Le nom de la visite tient sur DEUX lignes au plus : une bulle rétrécie à côté d'une grande fenêtre
   (`largeurPres`) coupait « Établir une attestati… » sur une seule. */
.visite-bulle .vb-lieu { font-size: 13.5px; font-weight: 700; color: var(--text); overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; text-wrap: balance; }
.visite-bulle .vb-fermer { flex: none; align-self: flex-start; border: 0; background: transparent; color: var(--muted);
  width: 28px; height: 28px; border-radius: 8px; display: grid; place-items: center; cursor: pointer; }
.visite-bulle .vb-fermer svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2.1; stroke-linecap: round; }
.visite-bulle .vb-fermer:hover { background: color-mix(in srgb, var(--th) 14%, transparent); color: var(--text); }
.visite-bulle .vb-progres { display: flex; gap: 4px; margin-block-start: 12px; }
.visite-bulle .vb-progres i { flex: 1; height: 5px; border-radius: 999px; position: relative; overflow: hidden;
  background: color-mix(in srgb, var(--th) 17%, transparent); }
.visite-bulle .vb-progres b { position: absolute; inset-block: 0; inset-inline-start: 0; width: var(--p); border-radius: inherit; background: var(--th); }
.visite-bulle .vb-progres i.ici b { animation: vb-remplit .6s cubic-bezier(.2, .8, .2, 1) both; }
@keyframes vb-remplit { from { width: var(--p0, var(--p)); } }
/* Une visite de plus de douze étapes : une seule jauge, un peu plus haute. */
.visite-bulle .vb-progres.seul i { height: 6px; }
.visite-bulle.nouveau-chapitre .vb-ico { animation: vb-pop .55s cubic-bezier(.2, 1.5, .4, 1) both; }
.visite-bulle.nouveau-chapitre .vb-ou { animation: vb-glisse-g .45s cubic-bezier(.2, .8, .2, 1) both; }
@keyframes vb-pop { from { transform: scale(.4); opacity: 0; } }

/* Le corps : le titre de l'étape, son texte, et ce qu'on attend de la personne. */
.visite-bulle .vb-corps { padding: 14px 18px 2px; }
.visite-bulle.entre .vb-corps { animation: vb-glisse-g .34s cubic-bezier(.2, .8, .2, 1) both; }
.visite-bulle.entre-arriere .vb-corps { animation: vb-glisse-d .34s cubic-bezier(.2, .8, .2, 1) both; }
@keyframes vb-glisse-g { from { opacity: 0; transform: translateX(14px); } }
@keyframes vb-glisse-d { from { opacity: 0; transform: translateX(-14px); } }
.visite-bulle h3 { margin: 0 0 6px; font-size: 17.5px; line-height: 1.3; font-weight: 750; letter-spacing: -.2px; text-wrap: balance; }
.visite-bulle .vb-texte { color: color-mix(in srgb, var(--text) 86%, var(--muted)); }
.visite-bulle .vb-texte p { margin: 0 0 8px; }
.visite-bulle .vb-texte p:last-child { margin-block-end: 0; }
.visite-bulle .vb-texte b { color: var(--text); }
.visite-bulle kbd { font-family: inherit; font-weight: 700; font-size: 11px; line-height: 1; min-width: 20px; text-align: center; display: inline-block;
  padding: 3px 6px; border-radius: 6px; border: 1px solid var(--line); border-block-end-width: 2px; background: var(--bg); color: var(--text); }
.visite-bulle .vb-afaire { display: flex; flex-direction: column; gap: 7px; margin-block-start: 12px; padding: 11px 13px; border-radius: 12px;
  background: var(--th-bg); border: 1px dashed color-mix(in srgb, var(--th) 45%, transparent); }
.visite-bulle .vb-atoi { display: inline-flex; align-items: center; gap: 6px; align-self: flex-start; font-size: 10.5px; font-weight: 800;
  text-transform: uppercase; letter-spacing: .7px; color: #fff; background: var(--th); border-radius: 999px; padding: 3px 11px 3px 8px; }
.visite-bulle .vb-atoi svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
  animation: vb-tape 1.4s ease-in-out infinite; transform-origin: 60% 80%; }
@keyframes vb-tape { 0%, 100% { transform: translateY(0); } 40% { transform: translateY(2px) scale(.92); } }
.visite-bulle .vb-action { font-size: 14px; }
/* Le geste déjà fait en entrant : on le dit, sans l'attente animée. */
.visite-bulle .vb-afaire.fait { border-style: solid; }
.visite-bulle .vb-afaire.fait .vb-atoi svg { animation: none; }
/* Une case qu'une étape à LIRE éclaire (10.14.1) : la consigne est une proposition, pas un geste
   attendu — le pavé est plus calme, sans l'attente animée, et « Suivant » reste là. */
.visite-bulle .vb-afaire.vb-libre { border-style: solid; background: transparent; }
.visite-bulle .vb-afaire.vb-libre .vb-atoi { color: var(--th); background: var(--th-bg); }
.visite-bulle .vb-afaire.vb-libre .vb-atoi svg { animation: none; }
.visite-bulle .vb-afaire.vb-libre .vb-action { font-size: 13.5px; }
/* Un nom de bouton cité dans la bulle est un LIEN vers ce bouton : le survoler l'éclaire, le
   cliquer l'amène à l'écran. Souligné en pointillé, comme un mot qu'on peut toucher. */
.visite-bulle .vb-nomme { color: var(--text); font-weight: 650; cursor: pointer; border-radius: 4px;
  text-decoration: underline dotted; text-decoration-thickness: 2px; text-underline-offset: 3px;
  text-decoration-color: color-mix(in srgb, var(--th) 70%, transparent); }
.visite-bulle .vb-nomme:hover, .visite-bulle .vb-nomme:focus-visible { background: var(--th-bg); outline: none;
  text-decoration-color: var(--th); }
/* On revient au geste qui ouvrait ce que l'étape montrait : la bulle dit pourquoi, une fois, au-dessus
   du texte de l'étape. */
.visite-bulle .vb-note { margin-block-end: 8px; padding: 6px 10px; border-radius: 8px; font-size: 12.5px; line-height: 1.45;
  color: var(--text); background: var(--th-bg); border-inline-start: 3px solid var(--th); }
/* « Tu peux cliquer ce qui est éclairé » : une fois par visite, en petit. */
.visite-bulle .vb-essai-note { display: flex; gap: 8px; align-items: flex-start; margin-block-start: 12px; font-size: 12.5px; line-height: 1.45; color: var(--muted); }
.visite-bulle .vb-essai-note svg { flex: none; width: 16px; height: 16px; margin-block-start: 1px; fill: none; stroke: color-mix(in srgb, var(--th) 85%, var(--text));
  stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.visite-bulle .vb-prochain { display: flex; align-items: center; gap: 7px; margin-block-start: 12px; font-size: 12.5px; color: var(--muted); }
.visite-bulle .vb-prochain::before { content: ''; width: 14px; height: 2px; border-radius: 2px; background: var(--th); opacity: .6; }
.visite-bulle .vb-prochain b { color: var(--text); font-weight: 650; }
/* L'étape « liste » : la zone éclairée, et CHACUN de ses contrôles nommé, numéroté et expliqué.
   Survoler une ligne pose le second anneau sur le bouton dont elle parle. La liste défile dans la
   bulle : une barre de dix boutons ne pousse pas le pied hors de l'écran. */
.visite-bulle .vb-liste { list-style: none; margin: 12px 0 0; padding: 6px 0 0; max-height: 40vh; overflow-y: auto;
  display: flex; flex-direction: column; gap: 2px; border-block-start: 1px solid var(--border); }
.visite-bulle .vb-item { display: flex; gap: 10px; align-items: flex-start; padding: 7px 8px; border-radius: 10px; cursor: default; }
.visite-bulle .vb-num { flex: none; width: 22px; height: 22px; border-radius: 50%; display: grid; place-items: center; margin-block-start: 1px;
  font-size: 11px; font-weight: 800; color: color-mix(in srgb, var(--th) 85%, var(--text)); background: var(--th-bg); font-variant-numeric: tabular-nums; }
.visite-bulle .vb-it { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.visite-bulle .vb-it b { font-size: 13.5px; }
.visite-bulle .vb-it-t { font-size: 12.5px; color: var(--muted); line-height: 1.45; }
.visite-bulle .vb-item:hover, .visite-bulle .vb-item:focus { background: var(--th-bg); outline: none; }
.visite-bulle .vb-item:hover .vb-num, .visite-bulle .vb-item:focus .vb-num { background: var(--th); color: #fff; }
.visite-bulle .vb-item:hover .vb-it-t, .visite-bulle .vb-item:focus .vb-it-t { color: var(--text); }
.visite-bulle .vb-prepare { color: var(--muted); }
.visite-bulle .vb-squelette { display: flex; flex-direction: column; gap: 8px; margin-block: 12px 16px; }
.visite-bulle .vb-squelette i { height: 10px; border-radius: 6px; background-size: 200% 100%; animation: vb-reflet 1.2s linear infinite;
  background-image: linear-gradient(90deg, var(--ink-soft) 0%, color-mix(in srgb, var(--th) 16%, var(--ink-soft)) 50%, var(--ink-soft) 100%); }
.visite-bulle .vb-squelette i:nth-child(2) { width: 82%; }
.visite-bulle .vb-squelette i:nth-child(3) { width: 58%; }
@keyframes vb-reflet { to { background-position: -200% 0; } }

/* Le pied : précédent, le compte, suivant — toujours à la même place. */
.visite-bulle .vb-pied { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 14px 18px 16px; flex-wrap: wrap; }
.visite-bulle .vb-pied button { white-space: nowrap; }
/* Un bouton qui nomme une étape (« Revenir à « Déclarer un collaborateur » ») peut être plus long que
   la place qui reste à côté du lien : il passe à la ligne, calé à droite, au lieu de sortir de la
   bulle (vu à la souris, 10.14.1). Plus large que la bulle entière, son texte se replie. */
.visite-bulle .vb-pied .vb-suiv { margin-inline-start: auto; max-width: 100%; }
.visite-bulle .vb-pied .vb-suiv[data-v="regeste"] { white-space: normal; text-align: start; }
/* Un espaceur : sans « Précédent », il garde le compteur au milieu et « Suivant » au bout. */
.visite-bulle .vb-vide { flex: none; inline-size: 0; }
.visite-bulle .vb-compte { font-size: 12px; font-weight: 700; color: var(--muted); font-variant-numeric: tabular-nums; letter-spacing: .3px; }
.visite-bulle .vb-suiv { display: inline-flex; align-items: center; gap: 7px; border: 0; border-radius: 10px; padding: 9px 16px;
  font-family: inherit; font-weight: 650; font-size: 13.5px; line-height: 1.2; cursor: pointer; background: var(--th); color: #fff;
  box-shadow: 0 8px 18px -8px color-mix(in srgb, var(--th) 80%, transparent); transition: transform .12s, filter .12s, box-shadow .12s; }
.visite-bulle .vb-suiv:hover { filter: brightness(1.08); transform: translateY(-1px); }
.visite-bulle .vb-suiv:active { transform: translateY(0); }
.visite-bulle .vb-suiv svg, .visite-bulle .vb-prec svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.visite-bulle .vb-suiv:disabled { background: var(--ink-soft); color: var(--muted); box-shadow: none; cursor: default; filter: none; transform: none; }
.visite-bulle .vb-prec { display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--line); background: var(--panel); color: var(--text);
  border-radius: 10px; padding: 8px 12px; font-family: inherit; font-weight: 550; font-size: 13px; line-height: 1.2; cursor: pointer; }
.visite-bulle .vb-prec:hover { border-color: color-mix(in srgb, var(--th) 50%, var(--line)); background: var(--th-bg); }
.visite-bulle .vb-lien { border: 0; background: transparent; color: var(--muted); padding: 4px 2px; font-family: inherit; font-weight: 550; font-size: 12.5px;
  line-height: 1.2; cursor: pointer; text-decoration: underline; text-underline-offset: 3px; text-decoration-color: color-mix(in srgb, var(--muted) 55%, transparent); }
.visite-bulle .vb-lien:hover { color: var(--text); }
.visite-bulle .vb-attente { display: inline-flex; align-items: center; gap: 8px; font-size: 12.5px; font-weight: 650; color: color-mix(in srgb, var(--th) 85%, var(--text)); }
.visite-bulle .vb-points-attente { display: inline-flex; gap: 3px; }
.visite-bulle .vb-points-attente i { width: 6px; height: 6px; border-radius: 50%; background: var(--th); animation: vb-point 1.2s ease-in-out infinite; }
.visite-bulle .vb-points-attente i:nth-child(2) { animation-delay: .15s; }
.visite-bulle .vb-points-attente i:nth-child(3) { animation-delay: .3s; }
@keyframes vb-point { 0%, 80%, 100% { opacity: .25; transform: translateY(0); } 40% { opacity: 1; transform: translateY(-3px); } }
/* « Passer au chapitre suivant » vit sur sa propre ligne, sous les deux flèches, plutôt que de les
   serrer ; l'astuce du clavier, une seule fois par visite, sous tout le reste. */
.visite-bulle .vb-pied2 { display: flex; justify-content: flex-end; padding: 0 18px 12px; margin-block-start: -8px; }
.visite-bulle .vb-astuce { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; padding: 9px 18px 12px;
  border-block-start: 1px solid var(--border); font-size: 11.5px; color: var(--muted); }
.visite-bulle button:focus-visible { outline: 2px solid var(--th); outline-offset: 2px; }
/* Perdus de vue : l'en-tête passe à l'orange, le dessin à l'avertissement. */
.visite-bulle.perdu { --th: var(--warning); --th-bg: var(--warning-soft); }

/* La fin : une médaille qui se dessine, le parcours, et la suite. */
.visite-bulle.fin .vb-haut { display: flex; align-items: center; gap: 13px; padding: 18px 14px 15px 18px; }
.visite-bulle .vb-medaille { flex: none; width: 52px; height: 52px; }
.visite-bulle .vb-medaille svg { display: block; width: 52px; height: 52px; }
/* Le but n'est pas atteint : pas de coche, un « i » calme — on dit ce qui manque, on ne gronde pas. */
.visite-bulle .vb-medaille.info { display: grid; place-items: center; border-radius: 50%; background: var(--th); color: #fff; }
.visite-bulle .vb-medaille.info svg { width: 28px; height: 28px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.visite-bulle .vb-m-rond { fill: var(--th); transform-origin: 26px 26px; animation: vb-pop .5s cubic-bezier(.2, 1.4, .4, 1) both; }
.visite-bulle .vb-m-coche { fill: none; stroke: #fff; stroke-width: 4; stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 36; stroke-dashoffset: 36; animation: vb-trace .45s .28s ease-out forwards; }
@keyframes vb-trace { to { stroke-dashoffset: 0; } }
.visite-bulle .vb-fin-ou { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.visite-bulle.fin h3 { font-size: 20px; }
.visite-bulle .vb-parcours { margin-block-start: 14px; padding: 12px 14px; border-radius: 12px; background: var(--bg); border: 1px solid var(--border);
  display: flex; flex-direction: column; gap: 7px; }
.visite-bulle .vb-parcours-t { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; font-size: 13px; }
.visite-bulle .vb-parcours-t span { font-weight: 800; color: color-mix(in srgb, var(--th) 85%, var(--text)); font-variant-numeric: tabular-nums; }
.visite-bulle .vb-parcours-s { font-size: 12px; color: var(--muted); }
.visite-bulle .vb-jauge { display: block; height: 7px; border-radius: 999px; background: color-mix(in srgb, var(--th) 15%, transparent); overflow: hidden; }
.visite-bulle .vb-jauge i { display: block; height: 100%; width: var(--p); border-radius: inherit; animation: vb-jauge .9s .3s cubic-bezier(.2, .8, .2, 1) both;
  background: linear-gradient(90deg, var(--th), color-mix(in srgb, var(--th) 65%, var(--panel))); }
@keyframes vb-jauge { from { width: 0; } }
.visite-bulle .vb-actions { margin-block-start: 14px; display: flex; flex-direction: column; gap: 7px; align-items: stretch; }
.visite-bulle .vb-actions .vb-suiv { justify-content: center; padding: 11px 16px; font-size: 14px; }
.visite-bulle .vb-suites { margin-block-start: 14px; display: flex; flex-direction: column; gap: 7px; }
.visite-bulle .vb-label { font-size: 10.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .7px; color: var(--muted); }
.visite-bulle .vb-suite { display: flex; align-items: center; gap: 11px; width: 100%; text-align: start; border: 1px solid var(--border);
  background: var(--panel); color: var(--text); border-radius: 12px; padding: 9px 11px; font-family: inherit; font-size: 13px; line-height: 1.35;
  cursor: pointer; transition: border-color .12s, background .12s, transform .12s; }
.visite-bulle .vb-suite:hover { border-color: var(--th); background: var(--th-bg); transform: translateY(-1px); }
.visite-bulle .vb-suite.simple { padding: 10px 12px; }
.visite-bulle .vb-suite-ico { flex: none; width: 30px; height: 30px; border-radius: 9px; display: grid; place-items: center; background: var(--th-bg);
  color: color-mix(in srgb, var(--th) 85%, var(--text)); }
.visite-bulle .vb-suite-ico svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.visite-bulle .vb-suite-t { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.visite-bulle .vb-suite-t span { font-size: 12px; color: var(--muted); }
.visite-bulle .vb-duree { flex: none; font-size: 11px; font-weight: 700; color: var(--muted); background: var(--ink-soft); border-radius: 999px; padding: 2px 8px; white-space: nowrap; }
/* Les confettis : une gerbe d'une seconde, qui ne prend aucun clic. Les sept couleurs des domaines. */
#visite-fete { position: fixed; z-index: 893; width: 0; height: 0; pointer-events: none; }
#visite-fete i { position: absolute; inset-inline-start: -4px; top: -6px; width: 8px; height: 12px; border-radius: 2px; opacity: 0;
  animation: vb-confetti var(--t) cubic-bezier(.15, .7, .3, 1) forwards; }
#visite-fete i:nth-child(7n+1) { background: #2a6fd6; }
#visite-fete i:nth-child(7n+2) { background: #1c8f5a; width: 7px; height: 7px; border-radius: 50%; }
#visite-fete i:nth-child(7n+3) { background: #b4620f; }
#visite-fete i:nth-child(7n+4) { background: #6b4ec9; height: 9px; }
#visite-fete i:nth-child(7n+5) { background: #b83f7a; width: 7px; height: 7px; border-radius: 50%; }
#visite-fete i:nth-child(7n+6) { background: #0e7c96; }
#visite-fete i:nth-child(7n+7) { background: #f2b61c; height: 9px; }
@keyframes vb-confetti {
  0% { opacity: 1; transform: translate(0, 0) rotate(0); }
  55% { opacity: 1; transform: translate(var(--x), var(--y)) rotate(calc(var(--r) * .6)); }
  100% { opacity: 0; transform: translate(calc(var(--x) * 1.15), calc(var(--y) + 170px)) rotate(var(--r)); }
}
body.dark .visite-bulle { box-shadow: 0 30px 70px -20px rgba(0, 0, 0, .75), 0 12px 28px -14px rgba(0, 0, 0, .5); }
body.dark #visite-voile .vv-ombre { fill: rgba(0, 0, 0, .6); }
/* En sombre, les teintes des domaines sont CLAIRES : le texte posé dessus passe au foncé. */
body.dark .visite-bulle .vb-ico, body.dark .visite-bulle .vb-suiv, body.dark .visite-bulle .vb-atoi,
body.dark .visite-bulle .vb-item:hover .vb-num, body.dark .visite-bulle .vb-item:focus .vb-num, body.dark .visite-bulle .vb-medaille.info { color: #0d1418; }
body.dark .visite-bulle .vb-m-coche { stroke: #0d1418; }
@media (prefers-reduced-motion: reduce) {
  #visite-voile, #visite-anneau.pulse, #visite-point.flash, .visite-bulle, .visite-bulle .vb-corps, .visite-bulle .vb-ico, .visite-bulle .vb-ou, .visite-bulle .vb-atoi svg,
  .visite-bulle .vb-points-attente i, .visite-bulle .vb-squelette i, .visite-bulle .vb-m-rond, .visite-bulle .vb-progres b, .visite-bulle .vb-jauge i { animation: none !important; }
  .visite-bulle .vb-m-coche { animation: none !important; stroke-dashoffset: 0; }
  #visite-voile.glisse .vv-trou, #visite-anneau.glisse, .visite-bulle.glisse, .visite-bulle .vb-suite, .visite-bulle .vb-suiv { transition: none; }
  #visite-fete { display: none; }
}
@media print { #visite-voile, #visite-nommes, #visite-anneau, #visite-point, .visite-bulle, #visite-fete { display: none !important; } }

/* La ligne « Première fois sur cette page ? » : calme (ni orange ni vert), sous l'en-tête, à la
   couleur du domaine de la page. Elle ne porte pas le bouton principal de la page — U-11 : un seul
   vert, l'étape suivante du métier. */
.guide-band { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: -4px 0 16px; padding: 10px 12px 10px 10px;
  border: 1px solid color-mix(in srgb, var(--th) 22%, var(--border)); border-radius: 12px; color: var(--text); font-size: 13px;
  background: linear-gradient(90deg, var(--th-bg), var(--panel) 70%); }
.guide-band .gb-ico { flex: none; width: 32px; height: 32px; border-radius: 10px; display: grid; place-items: center; background: var(--th); color: #fff; }
.guide-band .gb-ico svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; display: block; }
body.dark .guide-band .gb-ico { color: #0d1418; }
.guide-band .gb-txt { flex: 1 1 320px; min-width: 0; }
.guide-band .gb-go svg, .g-prochain .btn svg, .g-ligne .g-l-ico svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
/* Sur l'Aide, la même ligne calme propose de se faire montrer plutôt que de lire. */
.aide-guide { margin: 0 0 16px; max-width: 980px; }

/* « Guide-moi » (10.14.1, S-03) : dans l'en-tête de CHAQUE page, à la même place, dans les deux
   applications. Il se reconnaît au repos (une teinte et une bordure, jamais le vert plein : U-11 ne
   laisse qu'un bouton principal, l'étape suivante du métier), et sa boussole dit qu'il oriente. */
.btn.guide-moi { background: var(--primary-soft); color: var(--primary); border-color: color-mix(in srgb, var(--primary) 32%, transparent); }
.btn.guide-moi:hover, .btn.guide-moi[aria-expanded="true"] { background: var(--primary-soft-2); }
body.dark .btn.guide-moi { background: var(--primary-soft); color: var(--primary); }
body.dark .btn.guide-moi:hover, body.dark .btn.guide-moi[aria-expanded="true"] { background: var(--primary-soft-2); }
.btn.guide-moi svg { width: 15px; height: 15px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }

/* Son menu : la visite de la page, ce qu'on peut faire ICI, les autres onglets, puis l'article. Plus
   large qu'un menu de ligne (les phrases disent ce que fait chaque geste), et il DÉFILE quand la liste
   dépasse l'écran — les Paramètres en portent une douzaine. */
.row-menu.guide-menu { min-width: 340px; max-width: 400px; max-height: min(78vh, 620px); overflow-y: auto; overscroll-behavior: contain; }
.row-menu .rm-titre { padding: 8px 10px 3px; font-size: 12px; font-weight: 700; color: var(--muted); letter-spacing: .1px; }
.row-menu .rm-titre:not(:first-child) { margin-top: 4px; padding-top: 10px; border-top: 1px solid var(--border); }
.row-menu button:has(.rm-fin) { grid-template-columns: 18px 1fr auto; }
.row-menu .rm-fin { display: flex; flex-direction: column; align-items: flex-end; gap: 3px; padding-top: 1px; }
.row-menu .rm-note { font-size: 11px; color: var(--muted); white-space: nowrap; font-variant-numeric: tabular-nums; }
.row-menu .rm-fait { font-size: 10.5px; font-weight: 700; color: var(--success); background: var(--success-soft); border-radius: 999px; padding: 1px 7px; white-space: nowrap; }
/* Ce qui ne peut pas encore se faire reste cliquable (le clic dit ce qui manque et propose ce qui le
   fabrique) ; il se lit en second plan, jamais éteint en silence. */
.row-menu button.off .rm-l { color: var(--muted); }
.row-menu button.off .rm-i { opacity: .6; }

/* « Première fois sur cette page ? » (10.14.1, S-03) : accrochée à « Guide-moi », par-dessus la page
   — elle ne pousse plus l'écran de travail (la bande d'avant faisait commencer la grille de saisie
   90 px plus bas) et apprend où la visite se retrouve. Couche 55 : sous la palette (60), les menus
   (70) et les fenêtres (400). À la couleur du domaine de la page, sans vert (U-11). */
.guide-appel { position: fixed; z-index: 55; width: 330px; max-width: calc(100vw - 24px); padding: 14px 16px 12px;
  background: linear-gradient(180deg, var(--th-bg) 0%, var(--panel) 62%); color: var(--text);
  border: 1px solid color-mix(in srgb, var(--th) 30%, var(--border)); border-radius: 14px;
  box-shadow: 0 16px 40px rgba(20, 40, 60, .2); font-size: 13px; animation: ga-entre .18s ease-out; }
@keyframes ga-entre { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
.guide-appel .ga-fleche { position: absolute; top: -7px; inset-inline-start: calc(var(--ga-fleche, 40px) - 6px); width: 12px; height: 12px;
  background: var(--th-bg); border-top: 1px solid color-mix(in srgb, var(--th) 30%, var(--border));
  border-inline-start: 1px solid color-mix(in srgb, var(--th) 30%, var(--border)); transform: rotate(45deg); }
.guide-appel .ga-tete { display: flex; align-items: center; gap: 10px; }
.guide-appel .ga-tete b { font-size: 14px; font-weight: 750; }
.guide-appel .ga-ico { flex: none; width: 30px; height: 30px; border-radius: 9px; display: grid; place-items: center; background: var(--th); color: #fff; }
.guide-appel .ga-ico svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; display: block; }
body.dark .guide-appel .ga-ico { color: #0d1418; }
.guide-appel .ga-txt { margin: 9px 0 12px; line-height: 1.5; color: color-mix(in srgb, var(--text) 86%, var(--muted)); }
.guide-appel .ga-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.guide-appel .ga-go svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.guide-appel .ga-note { margin: 10px 0 0; font-size: 11.5px; color: var(--muted); line-height: 1.45; }
@media (prefers-reduced-motion: reduce) { .guide-appel { animation: none; } }
@media print { .guide-moi, .guide-appel { display: none !important; } }

/* L'accueil de la toute première fois (10.14.0) : une PORTE à deux battants — découvrir sur
   l'exemple, ou démarrer guidé dans sa vraie entreprise. Un fond teinté et une illustration, pas une
   alerte : c'est une invitation. Il porte LE bouton vert de l'accueil tant qu'il est là (U-11). */
.pp-accueil { position: relative; overflow: hidden; display: flex; flex-direction: column; align-items: flex-start; gap: 16px;
  margin: -2px 0 18px; padding: 24px 26px 18px; border-radius: 18px; border: 1px solid var(--primary-soft-2);
  background: radial-gradient(640px 260px at 100% 0%, color-mix(in srgb, var(--primary) 16%, transparent), transparent 70%),
    linear-gradient(135deg, var(--primary-soft) 0%, var(--panel) 72%); }
.pp-accueil .pp-acc-intro { position: relative; z-index: 1; max-width: 62ch; display: flex; flex-direction: column; gap: 6px; }
.pp-accueil .pp-acc-sur { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .9px; color: var(--primary); }
.pp-accueil h2 { margin: 0; font-size: 23px; line-height: 1.2; font-weight: 800; letter-spacing: -.4px; color: var(--text); text-transform: none; text-wrap: balance; }
.pp-accueil .pp-acc-intro p { margin: 0; color: color-mix(in srgb, var(--text) 80%, var(--muted)); }
.pp-accueil .pp-acc-choix { position: relative; z-index: 1; display: grid; grid-template-columns: repeat(2, minmax(0, 330px)); gap: 14px; }
.pp-choix { position: relative; display: flex; flex-direction: column; align-items: flex-start; gap: 7px; padding: 18px 18px 16px;
  background: var(--panel); border: 1px solid var(--border); border-radius: 15px; box-shadow: 0 10px 30px -18px rgba(15, 24, 34, .35); }
.pp-choix.reco { border-color: color-mix(in srgb, var(--primary) 55%, var(--border)); box-shadow: 0 0 0 3px var(--primary-soft), 0 14px 34px -18px rgba(15, 157, 143, .45); }
.pp-choix .pp-choix-badge { position: absolute; top: 14px; inset-inline-end: 14px; font-size: 10.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .6px;
  color: var(--primary); background: var(--primary-soft); border-radius: 999px; padding: 3px 9px; }
.pp-choix .pp-choix-ico { width: 42px; height: 42px; border-radius: 13px; display: grid; place-items: center; margin-block-end: 4px;
  background: linear-gradient(135deg, var(--primary), color-mix(in srgb, var(--primary) 60%, #2a6fd6)); color: #fff; box-shadow: 0 8px 18px -8px rgba(15, 157, 143, .7); }
.pp-choix:not(.reco) .pp-choix-ico { background: var(--ink-soft); color: var(--text); box-shadow: none; }
.pp-choix .pp-choix-ico svg { width: 21px; height: 21px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.pp-choix h3 { margin: 0; font-size: 15.5px; font-weight: 750; letter-spacing: -.2px; }
.pp-choix p { margin: 0; font-size: 13px; color: var(--muted); line-height: 1.5; }
.pp-choix .pp-choix-meta { list-style: none; margin: 2px 0 6px; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.pp-choix .pp-choix-meta li { font-size: 11.5px; font-weight: 700; color: var(--muted); background: var(--ink-soft); border-radius: 999px; padding: 2px 9px; }
.pp-choix .btn { margin-block-start: auto; }
.pp-accueil #pp-plus-tard { position: relative; z-index: 1; }
.pp-accueil .pp-acc-deco { position: absolute; z-index: 0; inset-inline-end: 22px; top: 18px; width: 250px; height: 176px; pointer-events: none; }
.pp-acc-deco .d-carte { fill: var(--panel); stroke: var(--border); stroke-width: 1.5; filter: drop-shadow(0 10px 16px rgba(15, 24, 34, .12)); }
.pp-acc-deco .d-titre { fill: var(--primary); }
.pp-acc-deco .d-pale { fill: var(--primary-soft-2); }
.pp-acc-deco .d-txt { fill: var(--ink-soft); }
.pp-acc-deco .d-total { fill: var(--primary-soft); }
.pp-acc-deco .d-barre { fill: none; stroke: #2a6fd6; stroke-width: 8; stroke-linecap: round; opacity: .75; }
.pp-acc-deco .d-ligne { fill: none; stroke: var(--primary); stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; }
.pp-acc-deco .d-ok { fill: var(--success); }
.pp-acc-deco .d-coche { fill: none; stroke: #fff; stroke-width: 4.5; stroke-linecap: round; stroke-linejoin: round; }
.pp-acc-deco .d-eclat { fill: #f2b61c; opacity: .9; }
.pp-acc-deco .d-eclat.petit { fill: var(--primary); opacity: .6; }
/* L'illustration ne passe jamais SOUS le texte ni sous les choix : sous 1180 px de large, elle s'efface. */
@media (max-width: 1180px) { .pp-accueil .pp-acc-deco { display: none; } }
@media (max-width: 760px) { .pp-accueil .pp-acc-choix { grid-template-columns: minmax(0, 1fr); } }

/* « Me guider » à côté d'une étape de « Tes premiers pas » : un lien discret, pas un second bouton. */
.pp-guide { border: 0; background: transparent; color: var(--primary); font-family: inherit; font-size: 12.5px; font-weight: 600;
  cursor: pointer; padding: 4px 2px; text-decoration: underline; text-underline-offset: 2px; white-space: nowrap; }
.pp-guide:hover { color: var(--primary-hover); }
.pp-guide svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2; vertical-align: -2px; margin-inline-end: 4px; }
.pp-go { display: flex; align-items: center; gap: 10px; justify-content: flex-end; flex-wrap: wrap; }

/* La page « Me guider » : un centre d'apprentissage. */
.g-hero { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 18px 34px; align-items: center; margin-block-end: 18px; padding: 24px 26px;
  border-radius: 18px; border: 1px solid var(--primary-soft-2);
  background: radial-gradient(520px 240px at 100% 0%, color-mix(in srgb, var(--primary) 14%, transparent), transparent 70%),
    linear-gradient(135deg, var(--primary-soft) 0%, var(--panel) 70%); }
.g-hero .g-hero-txt { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.g-hero .g-sur { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .9px; color: var(--primary); }
.g-hero h2 { margin: 0; font-size: 22px; line-height: 1.2; font-weight: 800; letter-spacing: -.4px; color: var(--text); text-transform: none; text-wrap: balance; }
.g-hero .g-hero-txt > p { margin: 0; max-width: 64ch; color: color-mix(in srgb, var(--text) 80%, var(--muted)); }
.g-prochain { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-block-start: 8px; padding: 13px 14px 13px 16px;
  background: var(--panel); border: 1px solid var(--border); border-radius: 14px; box-shadow: 0 10px 26px -18px rgba(15, 24, 34, .4); max-width: 640px; }
.g-prochain .g-pr-t { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.g-prochain .g-pr-etiq { font-size: 10.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .7px; color: var(--primary); }
.g-prochain .g-pr-t b { font-size: 15px; }
.g-hero .g-hero-mesure { display: flex; align-items: center; gap: 22px; }
.g-anneau { position: relative; width: 132px; height: 132px; flex: none; }
.g-anneau svg { width: 132px; height: 132px; transform: rotate(-90deg); }
.g-anneau .g-an-fond { fill: none; stroke: color-mix(in srgb, var(--primary) 16%, transparent); stroke-width: 11; }
.g-anneau .g-an-plein { fill: none; stroke: var(--primary); stroke-width: 11; stroke-linecap: round; stroke-dasharray: var(--circ); stroke-dashoffset: var(--off);
  animation: g-anneau 1.1s .1s cubic-bezier(.2, .8, .2, 1) both; }
@keyframes g-anneau { from { stroke-dashoffset: var(--circ); } }
.g-anneau .g-an-t { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; }
.g-anneau .g-an-t b { font-size: 26px; font-weight: 800; letter-spacing: -.5px; font-variant-numeric: tabular-nums; line-height: 1.05; }
.g-anneau .g-an-t small, .g-chiffres small, .g-th-n small { font-size: .6em; font-weight: 700; color: var(--muted); }
.g-anneau .g-an-t span { font-size: 11px; font-weight: 700; color: var(--muted); max-width: 90px; line-height: 1.25; margin-block-start: 2px; }
.g-chiffres { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; min-width: 170px; }
.g-chiffres li { display: flex; flex-direction: column; gap: 0; padding-inline-start: 11px; border-inline-start: 3px solid var(--primary-soft-2); }
.g-chiffres b { font-size: 17px; font-weight: 800; font-variant-numeric: tabular-nums; }
.g-chiffres span { font-size: 12px; color: var(--muted); }
@media (max-width: 1100px) { .g-hero { grid-template-columns: minmax(0, 1fr); } }

/* Ton essai : ce qu'il a déjà construit, et la promesse — calme tant qu'il reste du temps. */
.g-essai { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; padding: 16px 20px; border-color: var(--primary-soft-2); }
.g-essai .g-essai-ico { flex: none; width: 42px; height: 42px; border-radius: 13px; display: grid; place-items: center; background: var(--primary-soft); color: var(--primary); }
.g-essai .g-essai-ico svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.g-essai .g-essai-txt { flex: 1 1 380px; min-width: 0; display: flex; flex-direction: column; gap: 5px; }
.g-essai h2 { margin: 0; font-size: 15.5px; font-weight: 750; color: var(--text); text-transform: none; letter-spacing: -.1px; }
.g-essai .g-essai-sur { margin: 0; font-size: 12.5px; color: var(--muted); }
.g-essai .g-essai-faits { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.g-essai .g-essai-faits li { font-size: 12.5px; font-weight: 700; color: var(--text); background: var(--ink-soft); border-radius: 999px; padding: 3px 10px; }
.g-essai p.small { margin: 2px 0 0; color: var(--muted); }
.g-essai.proche { border-color: color-mix(in srgb, var(--warning) 45%, var(--border)); }
.g-essai.proche .g-essai-ico { background: var(--warning-soft); color: var(--warning); }

/* Tes réussites : des médailles DÉDUITES des données, jamais cochées à la main. */
.g-reussites { padding-block-end: 18px; }
.g-reu-tete { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin-block-end: 12px; }
.g-reu-tete h2 { margin: 0; }
.g-reu-n { font-size: 12px; font-weight: 700; color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.g-reu-liste { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
/* Neuf réussites : trois colonnes que les cartes remplissent — aucune ne reste seule sur sa rangée. */
@media (max-width: 1000px) { .g-reu-liste { grid-template-columns: minmax(0, 1fr); } }
.g-reu { display: flex; align-items: flex-start; gap: 10px; padding: 10px 12px; border-radius: 12px; border: 1px dashed var(--line); background: transparent; }
.g-reu .g-reu-ico { flex: none; width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center; background: var(--ink-soft); color: var(--muted); }
.g-reu .g-reu-ico svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.g-reu .g-reu-t { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.g-reu .g-reu-t b { font-size: 13px; color: var(--muted); }
.g-reu .g-reu-t span { font-size: 12px; color: var(--muted); line-height: 1.4; }
.g-reu.obtenue { border-style: solid; border-color: color-mix(in srgb, var(--success) 35%, var(--border)); background: color-mix(in srgb, var(--success-soft) 70%, var(--panel)); }
.g-reu.obtenue .g-reu-ico { background: var(--success); color: #fff; box-shadow: 0 6px 14px -6px color-mix(in srgb, var(--success) 70%, transparent); }
.g-reu.obtenue .g-reu-t b { color: var(--text); }
body.dark .g-reu.obtenue .g-reu-ico { color: #0d1418; }

/* Les grands départs : des cartes aux couleurs de leur domaine. */
/* auto-FIT : trois cartes sur une rangée de quatre pistes laissaient une colonne vide à droite (vu à
   l'écran, 10.14.0) — les pistes vides se replient, et les cartes remplissent la rangée. */
.g-cartes { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 14px; margin-block-end: 20px; }
.g-carte { display: flex; flex-direction: column; gap: 7px; align-items: flex-start; background: var(--panel); border: 1px solid var(--border);
  border-radius: 15px; padding: 16px; box-shadow: var(--shadow); position: relative; overflow: hidden; transition: transform .14s, box-shadow .14s, border-color .14s; }
.g-carte::before { content: ''; position: absolute; inset: 0 0 auto 0; height: 4px; background: var(--th); }
.g-carte:hover { transform: translateY(-2px); border-color: color-mix(in srgb, var(--th) 40%, var(--border)); box-shadow: 0 16px 32px -20px rgba(15, 24, 34, .45); }
.g-carte .g-carte-t { display: flex; justify-content: space-between; align-items: center; gap: 10px; width: 100%; }
.g-carte .g-carte-ico { width: 38px; height: 38px; border-radius: 12px; display: grid; place-items: center; background: var(--th-bg); color: var(--th); }
.g-carte .g-carte-ico svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.g-carte .g-carte-titre { font-size: 14.5px; }
.g-carte p { margin: 0; color: var(--muted); }
.g-carte .btn { margin-block-start: auto; }
.g-carte.faite .g-carte-ico { background: var(--success-soft); color: var(--success); }
.g-duree { flex: none; font-size: 11px; font-weight: 700; color: var(--muted); background: var(--ink-soft); border-radius: 999px; padding: 2px 9px; white-space: nowrap; }
.g-cherche { margin-block-end: 18px; }
/* Chaque domaine : son en-tête coloré, sa progression, ses visites. Deux par rangée quand la place
   le permet ; une grille décide sur la place du CADRE (10.12.0). */
.g-themes { display: grid; grid-template-columns: repeat(auto-fill, minmax(460px, 1fr)); gap: 16px; margin-block-end: 18px; align-items: start; }
.g-theme { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow); overflow: hidden; }
.g-theme .g-th-tete { display: flex; align-items: center; gap: 12px; padding: 14px 16px; background: var(--th-bg); }
.g-theme .g-th-ico { flex: none; width: 36px; height: 36px; border-radius: 11px; display: grid; place-items: center; background: var(--th); color: #fff; }
.g-theme .g-th-ico svg { width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
body.dark .g-theme .g-th-ico { color: #0d1418; }
.g-theme .g-th-t { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.g-theme .g-th-t h2 { margin: 0; font-size: 15px; font-weight: 750; color: var(--text); text-transform: none; letter-spacing: -.1px; }
.g-theme .g-th-t span { font-size: 12.5px; color: var(--muted); }
.g-theme .g-th-n { font-size: 17px; font-weight: 800; color: color-mix(in srgb, var(--th) 85%, var(--text)); font-variant-numeric: tabular-nums; white-space: nowrap; }
.g-theme .g-th-barre { display: block; height: 3px; background: color-mix(in srgb, var(--th) 15%, transparent); }
.g-theme .g-th-barre i { display: block; height: 100%; background: var(--th); }
.g-liste { list-style: none; margin: 0; padding: 4px 16px 8px; }
.g-ligne { display: flex; align-items: center; gap: 12px; padding: 11px 0; border-block-start: 1px solid var(--border); }
.g-ligne:first-child { border-block-start: 0; }
.g-ligne .g-l-ico { flex: none; width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center; background: var(--th-bg);
  color: color-mix(in srgb, var(--th) 85%, var(--text)); }
.g-ligne.faite .g-l-ico { background: var(--success); color: #fff; }
body.dark .g-ligne.faite .g-l-ico { color: #0d1418; }
.g-ligne .g-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.g-etat { font-size: 11.5px; font-weight: 700; padding: 2px 9px; border-radius: 999px; white-space: nowrap; }
.g-etat.fait { background: var(--success-soft); color: var(--success); }
.g-etat.encours { background: var(--info-soft); color: var(--info); }
.g-pages-sec p.small { margin: -6px 0 12px; }
.g-pages { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; }
.g-page { display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--line); border-inline-start: 3px solid var(--th); background: var(--panel);
  color: var(--text); border-radius: 999px; padding: 6px 13px 6px 11px; font-family: inherit; font-size: 13px; cursor: pointer; transition: background .12s, border-color .12s; }
.g-page:hover { border-color: var(--th); background: var(--th-bg); }
.g-page svg { width: 14px; height: 14px; fill: none; stroke: var(--success); stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.g-page.fait { background: color-mix(in srgb, var(--success-soft) 60%, var(--panel)); }
.g-proposer { margin-block: 6px 24px; }
/* Une visite qui n'a encore rien à montrer dit pourquoi (9.4.5), sous son titre. */
.g-manque { color: var(--muted); font-style: italic; }
.g-page:disabled { opacity: .55; cursor: not-allowed; }
.g-page:disabled:hover { border-color: var(--line); background: var(--panel); }
@media (prefers-reduced-motion: reduce) {
  .g-anneau .g-an-plein { animation: none; }
  .g-carte, .g-page { transition: none; }
  .g-carte:hover { transform: none; }
}

/* 10.14.0 — un matricule fiscal peut se couper après ses « / » (`mfCoupable`) : c'est ce qui fait
   tenir la liste des clients à 1280 px. Mais sur un écran large il y a la place, et « 1213217T/A/ »
   au-dessus de « M/000 » double la hauteur de chaque ligne pour rien. Au-dessus du seuil des listes
   larges, il reste sur UNE ligne. Mesuré : Chrome coupe à un <wbr> MÊME sous `nowrap` — c'est le
   <wbr> qu'on retire du rendu. */
@media (min-width: 1340px) { td.mf wbr { display: none; } }

/* 10.14.1 (S-06) — une page LENTE se voit charger. Le voile couvre la zone de travail (sa place est
   mesurée en JavaScript, la barre latérale reste lisible) et n'apparaît qu'au bout d'un quart de
   seconde : une page rapide est dessinée avant qu'il se montre. Son apparition et sa roue sont des
   animations d'opacité et de rotation, que le compositeur joue même pendant que le calcul occupe le
   fil principal — c'est tout ce qui permet de le voir. Partagé par les deux applications. */
#chargement-page { position: fixed; z-index: 55; display: flex; align-items: center; justify-content: center; gap: 12px;
  background: color-mix(in srgb, var(--bg) 78%, transparent); color: var(--text); font-size: 15px; font-weight: 600;
  opacity: 0; animation: cp-apparait .18s ease-out .25s forwards; pointer-events: none; }
#chargement-page .cp-roue { width: 24px; height: 24px; border-radius: 50%; border: 3px solid var(--border); border-top-color: var(--primary);
  animation: maj-tourne .8s linear infinite; }
@keyframes cp-apparait { to { opacity: 1; } }

/* 10.14.1 (S-06) — une page qui change SE VOIT changer : son contenu monte de quelques pixels en
   apparaissant. `backwards` et non `both` : une transformation laissée en place ferait du contenu le
   bloc de référence de tout ce qui y est posé en `position: fixed`. Seulement quand la PAGE change. */
#view.entree > * { animation: page-entree .2s ease-out backwards; }
@keyframes page-entree { from { opacity: 0; transform: translateY(8px); } }
@media (prefers-reduced-motion: reduce) {
  #view.entree > * { animation: none; }
  #chargement-page { animation-delay: .25s; animation-duration: 0s; }
  #chargement-page .cp-roue { animation-duration: 2.4s; }
}

/* 10.14.1 (S-06) — les nouveautés d'une version, au premier lancement : une carte au coin de l'écran,
   pas une fenêtre (elle ne bloque rien, on peut travailler à côté), sous les questions (400) et au-dessus
   de la page. Partagée par les deux applications. */
#nouveautes { position: fixed; inset-block-end: 22px; inset-inline-end: 22px; z-index: 380; width: min(400px, calc(100vw - 44px));
  background: var(--panel); color: var(--text); border: 1px solid var(--border); border-radius: 14px;
  box-shadow: 0 18px 48px rgba(20, 40, 60, .22); padding: 18px 18px 14px; animation: nouv-entre .28s ease-out; }
#nouveautes.sort { opacity: 0; transform: translateY(8px); transition: opacity .18s, transform .18s; }
#nouveautes .nouv-tete { display: flex; gap: 12px; align-items: flex-start; margin-block-end: 10px; }
#nouveautes .nouv-ico { flex: none; width: 32px; height: 32px; border-radius: 10px; display: grid; place-items: center;
  background: var(--primary-soft); color: var(--primary); font-size: 16px; }
#nouveautes .nouv-titre { font-size: 15px; font-weight: 700; }
#nouveautes .nouv-sous { font-size: 12.5px; color: var(--muted); margin-block-start: 2px; }
#nouveautes .nouv-liste { margin: 0; padding-inline-start: 20px; display: grid; gap: 7px; font-size: 13.5px; line-height: 1.45;
  max-height: 46vh; overflow-y: auto; }
#nouveautes .nouv-actions { display: flex; justify-content: flex-end; margin-block-start: 14px; }
@keyframes nouv-entre { from { opacity: 0; transform: translateY(12px); } }
@media (prefers-reduced-motion: reduce) { #nouveautes, #nouveautes.sort { animation: none; transition: none; } }

/* La facture électronique (10.15.0) : ce qui manque avant le fichier El Fatoora, chaque point avec
   le bouton qui ouvre la fiche à corriger, sur la même ligne ; puis les deux gestes qui restent. */
.teif-manques { list-style: none; padding: 0; margin: 12px 0; display: flex; flex-direction: column; gap: 10px; }
.teif-manques li { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.teif-manques li > span { flex: 1 1 auto; }
.teif-manques li > .btn { flex: 0 0 auto; }
.teif-suite { margin: 10px 0; padding-inline-start: 22px; display: flex; flex-direction: column; gap: 6px; }

/* ---------- La caisse (10.15.0) ----------
   Deux colonnes : ce qu'on vend à gauche (le champ de scan en tête, qui garde le curseur), le ticket
   en cours à droite, le total en gros — c'est lui que le client regarde. Les boutons d'articles ont
   une taille de doigt : un comptoir s'utilise aussi à l'écran tactile. */
.caisse { display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(340px, 1fr); gap: 18px; align-items: start; }
@media (max-width: 1100px) { .caisse { grid-template-columns: 1fr; } }
.cs-scan input { font-size: 17px; padding-block: 12px; }
.cs-articles { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; margin-top: 14px; }
.cs-art { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; min-height: 84px; padding: 11px 12px; text-align: start;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-sm); color: var(--text); cursor: pointer; font: inherit; }
.cs-art:hover { border-color: var(--primary); background: var(--primary-soft); }
.cs-art:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.cs-art-nom { font-weight: 600; line-height: 1.25; overflow-wrap: anywhere; }
.cs-art-prix { font-variant-numeric: tabular-nums; color: var(--primary); font-weight: 600; margin-top: auto; }
/* Un article sans prix le dit en orange : la couleur de la tuile (0,1,0) l'emporterait sur .warn-text posée plus haut. */
.cs-art-prix.warn-text { color: var(--warning); }
.cs-art-stock { font-size: 12px; color: var(--muted); }
.cs-plus { grid-column: 1 / -1; }
.cs-lignes input.num { width: 72px; }
/* La zone des lignes a une hauteur FIXE : sans elle, chaque article scanné repoussait les boutons de
   paiement d'une ligne (45 px au premier), et le clic visé sur « Carte » tombait à côté (H-E1). Au-delà
   de trois lignes, la zone défile ; les totaux et « Encaisser » ne bougent plus. */
.cs-zone { position: relative; height: 170px; overflow-y: auto; scrollbar-gutter: stable; border-radius: 10px; }
.cs-lignes th { white-space: nowrap; }
.cs-zone table.list { margin: 0; }
.cs-vide { padding: 14px 0; height: 100%; box-sizing: border-box; margin: 0; display: grid; place-items: center; text-align: center;
  border: 1px dashed var(--border); border-radius: 10px; }
.cs-totaux { display: grid; gap: 4px; margin: 14px 0 12px; font-variant-numeric: tabular-nums; }
.cs-totaux > div { display: flex; justify-content: space-between; gap: 12px; }
.cs-total { font-size: 24px; font-weight: 700; padding-top: 8px; border-top: 1px solid var(--border); }
.cs-modes { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 12px; }
.cs-mode { font: inherit; padding: 9px 16px; border-radius: 999px; border: 1px solid var(--line); background: var(--panel); color: var(--text); cursor: pointer; }
.cs-mode.on { background: var(--primary); border-color: var(--primary); color: #fff; font-weight: 600; }
.cs-recu { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 14px; align-items: end; }
/* Le reçu ne sert qu'aux espèces, mais sa PLACE reste : passer à « Carte » faisait remonter tout le
   bas du ticket de 66 px, et « Encaisser » avec lui, sous le curseur (H-E1). */
.cs-recu { min-height: 66px; }
.cs-sans-recu { grid-column: 1 / -1; align-self: center; margin: 0; }
.cs-rendre { display: flex; flex-direction: column; align-items: flex-end; padding-bottom: 6px; }
.cs-rendre strong { font-size: 20px; font-variant-numeric: tabular-nums; }
.cs-client { margin-top: 10px; }
.cs-motif { min-height: 2lh; margin: 10px 0 6px; }
.cs-go { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.cs-encaisser { font-size: 16px; padding: 12px 22px; }
.cs-dernier { margin-top: 14px; padding-top: 12px; border-top: 1px dashed var(--line); display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.cs-apercu { width: 330px; height: 460px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: #fff; display: block; margin: 0 auto 6px; }
/* Le ticket reste sous les yeux pendant qu'on fait défiler les articles. */
.cs-droite { position: sticky; top: 0; }
@media (max-width: 1100px) { .cs-droite { position: static; } }

/* ---------- Le calculateur de prix (10.15.0, H6) ---------- */
/* Le résultat change à chaque frappe, et d'une seule ligne (« tape un coefficient ») à un tableau
   de lignes : sa hauteur est FIXE, sinon « Appliquer ce prix » descendrait sous le curseur (H-E1). */
.pc-resultat { height: 190px; overflow-y: auto; margin-block-start: 10px; padding: 12px 14px;
  border: 1px solid var(--border); border-radius: 10px; background: var(--bg); box-sizing: border-box; }
.pc-resultat > p { margin: 0; }
.pc-resultat table.list { margin: 0; }
.pc-grille { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px 16px; font-variant-numeric: tabular-nums; }
.pc-grille > div { display: flex; flex-direction: column; gap: 2px; }
.pc-prix { font-size: 20px; }
/* Le champ dit son unité à côté de lui (9.4.8) : ×, %, ou le prix TTC dans sa devise. */
.avec-unite { display: flex; align-items: center; gap: 8px; }
.avec-unite > input { flex: 1 1 auto; min-width: 0; }
.unite-champ { flex: 0 0 auto; min-width: 6.5em; color: var(--muted); font-size: 13px; }
.prix-rang { display: flex; align-items: flex-start; gap: 12px; }
.prix-rang > .annonce-stable { flex: 1 1 auto; min-width: 0; }
.prix-rang > .btn { flex: 0 0 auto; }
/* L'attestation d'exonération de retenue (10.15.0, H7) : un titre qui porte son état sur sa ligne,
   puis les trois champs. L'état vit À CÔTÉ du titre : sous les champs, il pousserait le bouton. */
.exo-rs { display: flex; flex-direction: column; gap: 8px; padding: 12px 14px; border: 1px solid var(--line); border-radius: 10px; }
.exo-titre { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 12px; font-weight: 600; }
.exo-titre > .small { font-weight: 400; }
.exo-rs .grid-3 { gap: 10px 14px; }
.modal.pc-modal { width: min(760px, calc(100vw - 32px)); }
