/* ============================================================
   CaptainContext — thème "carte marine"
   Papier froid, encre marine, accent sarcelle, mono = journal de bord
   ============================================================ */

:root {
  --paper: #eef1ee;
  --card: #ffffff;
  --ink: #182b3a;
  --ink-2: #53636f;
  /* V8.57 — `--ink-3` ÉTAIT SOUS LE SEUIL D'ACCESSIBILITÉ, ET DEPUIS TOUJOURS.
     #8795a0 sur blanc donne 3,07:1 ; WCAG AA demande 4,5:1 pour du texte
     courant. Ce jeton habille les en-têtes de tableau, les textes d'aide, les
     dates, les libellés secondaires — c'est-à-dire beaucoup de choses qu'on
     lit vraiment. Le défaut n'a rien à voir avec le thème sombre ; c'est le
     banc de contraste écrit pour LUI (tests/v8.57_theme.mjs) qui l'a trouvé,
     en mesurant aussi le thème clair. On assombrit d'un cran : 4,95:1 sur le
     blanc des cartes, 4,61:1 sur les surfaces enfoncées. La nuance reste
     nettement plus pâle que `--ink-2`, donc la hiérarchie visuelle ne bouge
     pas — seule la lisibilité monte. */
  --ink-3: #64727d;
  --line: #d4dcd9;
  --line-soft: #e4e9e7;
  --accent: #0f7583;
  --accent-fonce: #0b5a65;
  --accent-pale: #e2eff0;
  --danger: #a03d2c;
  --danger-pale: #f6e8e4;
  --ok: #3d7a46;

  --st-ouvert: #8a6a16;
  --st-actif: #0f7583;
  --st-resolu: #3d7a46;
  --st-abandonne: #8a4b3b;
  --st-remplace: #6b6f76;

  /* ============================================================
     V8.57 — LES JETONS QUI MANQUAIENT POUR QUE LE THÈME SOMBRE TIENNE.

     Le thème sombre existait depuis la V8.33 (app2.css) et ne redéfinissait
     que des JETONS — c'était la bonne méthode. Elle supposait seulement que
     les règles en utilisent. Or cette feuille en contenait DEUX CENT
     QUATRE-VINGT-CINQ couleurs écrites en dur, invisibles tant qu'on reste en
     clair, et qui ne suivaient rien.

     ⚠ ET UNE INVERSION, QUI EXPLIQUE LE PIRE DÉFAUT VISIBLE. `.topbar` posait
       `background: var(--ink)` — l'ENCRE comme fond, ce qui est juste tant que
       l'encre est sombre. En thème sombre, `--ink` devient CLAIR (#e7edf0) :
       le bandeau devenait donc blanc, seule surface éclatante de l'écran, et
       le nom du produit — écrit en blanc — y disparaissait. Un jeton nommé
       d'après ce qu'il EST (« l'encre ») employé pour ce qu'il VAUT (« du
       foncé ») : la confusion se paie exactement au moment où la palette
       s'inverse. D'où `--topbar` et `--topbar-txt`, nommés par leur rôle.

     Les noms sont ceux qu'app2.css emploie déjà pour la v2 — on n'invente pas
     un second vocabulaire, on complète celui-ci pour que la base le parle
     aussi. Valeurs identiques au thème clair : rien ne change à l'écran
     aujourd'hui, tout devient inversable demain.
     ============================================================ */
  --sunk: #f6f7f6;          /* survol, en-tête de tableau, surface enfoncée */
  --sunk-2: #eff1ef;        /* pastille, bouton fantôme au survol */
  --side-bg: #182b3a;       /* la navigation (foncée en v1, claire en v2) */
  --input-bg: #ffffff;      /* le fond des champs de saisie */
  --topbar: #182b3a;        /* le bandeau — un RÔLE, pas « l'encre » */
  --topbar-txt: #e9eef0;
  --topbar-ligne: #3b5062;  /* les bordures DANS le bandeau */
  --topbar-attenue: #90a3b0;/* les placeholders et icônes du bandeau */
  --topbar-vif: #7fc6ce;    /* le survol et la 2ᵉ moitié du mot-marque */
  --warn: #8a6a16;
  --warn-pale: #f7f0dd;
  --ok-pale: #e6efe7;
  --live: #2f9e5c;          /* présence : quelqu'un est là maintenant */
  /* ⚠ `--accent-plein` N'EST PAS UN DOUBLON DE `--accent`, ET C'EST LE THÈME
     SOMBRE QUI L'A RÉVÉLÉ. `--accent` sert à DEUX choses opposées : peindre du
     texte et des filets sur le papier (il doit alors être FONCÉ pour se
     détacher), et remplir des boutons pleins sous du texte blanc (il doit
     alors être assez foncé pour que le blanc s'y lise). En thème clair, une
     seule valeur satisfait les deux. En sombre, l'accent doit s'éclaircir pour
     rester lisible sur fond marine — et le blanc posé dessus tombe alors à
     2,9:1, sous le seuil d'accessibilité. Deux emplois, deux jetons : les
     boutons pleins prennent `--accent-plein`, identique à `--accent` en clair
     et volontairement plus profond en sombre. */
  --accent-plein: #0f7583;
  --sur-accent: #ffffff;    /* le texte posé SUR --accent-plein */

  /* ⚠ V9.21 (TEST-56) — LE VERROU DE SORTIE, ET SA COULEUR À LUI.
     La doctrine chromatique du produit (voir plus bas, § pastilles) dit :
     turquoise = chez nous, ambre = chez quelqu'un d'autre, violet = restreint
     à des personnes. Ce verrou-ci n'est aucun des trois — il ne parle pas de
     personnes mais de PORTES, et il ne restreint rien à l'intérieur de la
     maison. D'où une quatrième teinte, une brique sourde : assez proche de
     l'ambre pour dire « attention, ça concerne l'extérieur », assez distincte
     pour qu'on ne confonde jamais « partagé avec une autre organisation »
     avec « ne sort pas d'ici ». */
  --hors-ia: #a4523a;
  --hors-ia-pale: #fbf0ec;
  --prive: #6b4f9e;         /* le privé — violet, V5.6 */
  --prive-encre: #fff;      /* V9.18 — l'encre SUR --prive : 6,5:1 ici, declaree par theme */
  /* ⚠ V9.18 — `--prive-pale` EXISTAIT DEJA, deux lignes plus bas. La premiere
     ecriture de ce lot en a redeclare une seconde, aussitot ecrasee par
     l'ancienne : une ligne sans effet, et un futur reglage qui n'aurait rien
     change. On garde la valeur d'origine, qui convient. */
  --prive-pale: #f2eefa;
  --ombre: 0 1px 2px rgba(24, 43, 58, .05);
  --ombre-flot: 0 8px 28px rgba(24, 43, 58, .14);

  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;

  --r: 6px;
  --sidebar-w: 292px;

  /* ⚠ `color-scheme` N'EST PAS DÉCORATIF. Il dit au NAVIGATEUR quel thème est
     en vigueur, et c'est lui — pas nos règles — qui repeint ce que la CSS ne
     touche pas : le menu déroulant natif d'un `<select>` ouvert, les
     ascenseurs, le curseur de saisie, l'autofill, les cases à cocher, le fond
     par défaut d'un `<input>`. Sans lui, un thème sombre garde des listes
     déroulantes blanches et des ascenseurs clairs, et aucune quantité de CSS
     n'y change quoi que ce soit. La valeur sombre est posée avec les autres
     jetons du thème (app2.css). */
  color-scheme: light;
}

* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; }
body {
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.45;
  color: var(--ink);
  background: var(--paper);
  /* lignes de sonde très discrètes, façon carte marine */
  background-image: linear-gradient(transparent 31px, rgba(24, 43, 58, 0.025) 32px);
  background-size: 100% 32px;
}
a { color: var(--accent-fonce); text-decoration: none; }
a:hover { text-decoration: underline; }
button { font: inherit; cursor: pointer; }
/* ⚠ UN CHAMP SANS `background` N'EST PAS TRANSPARENT : LE NAVIGATEUR LUI EN
   POSE UN BLANC. C'était l'un des défauts les plus visibles du thème sombre —
   champs éclatants, texte pâle dedans, illisible — et il ne venait d'aucune
   règle de cette feuille, mais de leur absence : on ne peut pas corriger ce
   qu'on n'a jamais écrit. On habille donc ici TOUS les champs, une fois.

   ⚠ ON RESTE À LA SPÉCIFICITÉ D'UN SÉLECTEUR D'ÉLÉMENT (pas de `:not()`, qui
     la ferait monter) : des dizaines de règles plus bas reposent sur le fait
     que celle-ci cède, à commencer par les champs transparents du tableur et
     par `input:disabled` (lecture seule). La ligne d'après suffit à rendre aux
     cases à cocher et aux boutons radio leur fond natif — eux sont dessinés
     par le navigateur (`color-scheme`) et un fond imposé écrase l'état coché. */
/* V8.57 — LE FOND, LA COULEUR *ET* LA TEINTE DU TRAIT.
   Sans `background`, un champ garde le blanc que le navigateur lui donne :
   c'était l'un des trois défauts visibles du thème sombre (champ blanc, texte
   pâle dedans, illisible). Et sans `border-color`, il garde le trait par
   DÉFAUT du navigateur — que `color-scheme: dark` éclaircit, ce qui cerne
   chaque champ d'un liseré blanc sur fond marine. On ne touche ni à l'épaisseur
   ni au style du trait : seulement à sa teinte, pour ne pas déplacer d'un pixel
   les centaines de champs qui posent déjà leur propre bordure. */
input, select, textarea {
  font: inherit; color: var(--ink); background: var(--input-bg); border-color: var(--line);
}
input[type="checkbox"], input[type="radio"] { background: none; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
@media (prefers-reduced-motion: reduce) { * { transition: none !important; animation: none !important; } }

/* ---------- Coquille ---------- */
/* ============================================================
   ⚠ V11.5 — `#app` EST UNE COLONNE, PLUS UNE RANGÉE.
   La rangée était la cause d'un défaut visible : tout élément ajouté à `#app`
   qui n'était ni la barre latérale ni le contenu — les bandeaux de licence, de
   maintenance, d'aperçu, d'invitation — devenait une COLONNE DE PLUS à côté
   d'eux. Le bandeau « paiement en échec » occupait 856 px de large sur toute
   la hauteur, poussait la barre au milieu de l'écran, et son texte disparaissait
   sous la topbar fixe : l'application semblait cassée au moment précis où on
   demandait à quelqu'un de régulariser.

   La rangée existe toujours — elle est descendue d'un cran, dans `.app-corps`.
   ============================================================ */
#app { display: flex; flex-direction: column; height: 100vh; }

/* Les bandeaux, en pleine largeur, sous la topbar fixe.
   ⚠ LA MARGE VAUT AUSSI QUAND C'EST VIDE, et c'est ce qui rend le correctif
     sans calcul : en flex les marges ne fusionnent pas, un `.app-chrome` sans
     bandeau occupe donc quand même la hauteur de la topbar et `.app-corps`
     démarre au bon endroit. Zéro bandeau ou trois, sur une ligne ou sur deux :
     rien à mesurer.
   ⚠ `var(--topbar-h, 52px)` ET NON `52px` EN DUR. La topbar mesure 52 px en v1
     et 50 px sous la surcouche v2, qui pose `--topbar-h:50px` sur <html> (cf.
     css/app2.css § 16 bis). Écrire le nombre ici recréerait exactement la bande
     de 2 px que la V8.34 avait dû corriger — et la recréerait dans le seul
     endroit qui, désormais, commande la position de TOUT le reste. La variable
     n'existant pas en v1, le repli rend la mesure de la v1. */
.app-chrome { flex: none; margin-top: var(--topbar-h, 52px); }

/* La rangée d'autrefois. `min-height: 0` est OBLIGATOIRE : sans lui, un enfant
   flex refuse de descendre sous la taille de son contenu, `.contenu` cesse
   d'être borné, et le défilement de la V10.3 repart sur la fenêtre entière. */
.app-corps { display: flex; flex: 1; min-height: 0; }

.topbar {
  position: fixed; top: 0; left: 0; right: 0; height: 52px; z-index: 40;
  display: flex; align-items: center; gap: 14px; padding: 0 16px;
  background: var(--topbar); color: var(--topbar-txt);
  border-bottom: 3px solid var(--accent);
}
/* V8.54 — LE MOT-MARQUE N'EST PLUS EN MONOSPACE.
   `var(--mono)` est une PILE, pas une police : elle rend SF Mono sur macOS,
   Consolas sur Windows, Liberation Mono sur Linux. Trois dessins, trois
   chasses, trois largeurs pour le même mot — et celui qu'on voit le plus
   souvent, Consolas, donne au nom du produit l'allure d'un chemin de fichier.
   Un mot-marque doit avoir UNE forme, la même partout.

   La pile sans est déjà celle de toute l'interface, et ses polices (Segoe UI,
   SF, Roboto) sont dessinées pour être lues en gras à petite taille. On
   resserre l'interlettrage — un mot-marque se soude, il ne s'espace pas — et
   on monte d'un point pour compenser la chasse plus étroite du sans.

   ⚠ LA MONO NE BOUGE PAS AILLEURS. Codes de publication, identifiants,
     chemins, formules : là elle dit « ceci se recopie caractère par
     caractère », et c'est son emploi exact. Ce n'était simplement pas ce que
     disait le nom du produit.

   V8.55 — L'INTERLETTRAGE REVIENT À ZÉRO. Non, ce n'étaient pas des films :
   -0.015em à 16 px fait bien un quart de pixel de moins entre chaque lettre,
   et « relieo » est le pire mot possible pour ça — l·i·e sont trois traits
   verticaux consécutifs, et les resserrer les fait lire comme un seul signe.
   Le resserrement est une règle de mot-marque en CAPITALES ou en très grand ;
   à 16 px en bas-de-casse, sur fond sombre (où le texte clair « bave » déjà
   optiquement vers l'extérieur), il ne gagne rien et coûte la lisibilité.
   Reste le corps à 16 px, qui était l'essentiel du gain. */
.marque {
  font-family: var(--sans); font-size: 16px; font-weight: 700;
  /* V8.57 — CE BLANC RESTE UN BLANC. Le reste du bandeau est passé aux jetons
     `--topbar*`, mais le mot-marque est du TEXTE POSÉ SUR UNE SURFACE SOMBRE
     dans les deux thèmes : lui donner un jeton de texte le ferait virer au
     foncé le jour où la palette s'inverse — invisible. Même raison partout
     ailleurs dans cette feuille pour un `#fff` sur accent, danger ou bandeau.
     Il est aussi volontairement plus vif que `--topbar-txt` : le nom du
     produit passe devant les libellés qui l'entourent. */
  color: #fff; letter-spacing: 0; white-space: nowrap;
}
/* V7.1 (RELI-430) — le nom du site est SAISI par les gens, il peut être long :
   on borne l'AFFICHAGE (ellipse) plutôt que la saisie — le bandeau ne doit
   jamais céder sous un nom, quel qu'il soit. ~18 caractères visibles ici,
   moins encore à l'écran étroit (media query plus bas). */
.topbar .marque { max-width: 230px; overflow: hidden; text-overflow: ellipsis; }
.marque a { color: inherit; }
.marque .cap { color: var(--topbar-vif); }
.topbar .recherche-globale { flex: 1; max-width: 460px; position: relative; }
.topbar .recherche-globale input {
  width: 100%; padding: 6px 10px 6px 30px; border-radius: var(--r);
  /* V6.2 — `#223càd` (valeur corrompue, ignoree par le parseur) supprimee. */
  border: 1px solid var(--topbar-ligne);
  /* V8.57 — le champ de recherche est un CREUX dans le bandeau : il doit
     s'assombrir avec lui. Aucun jeton ne dit « creux du bandeau », et on n'en
     crée pas un pour un seul emploi : on teinte le fond du bandeau d'un peu de
     sa propre encre claire, ce qui redonne exactement le décrochage d'avant
     dans un thème comme dans l'autre. */
  background: color-mix(in srgb, var(--topbar) 82%, var(--topbar-txt));
  color: var(--topbar-txt); font-size: 13px;
}
.topbar .recherche-globale input::placeholder { color: var(--topbar-attenue); }
.topbar .recherche-globale::before {
  content: "◎"; position: absolute; left: 9px; top: 5px; color: var(--topbar-attenue); font-size: 13px;
}
.topbar .espace { flex: 1; }
.topbar .btn-quitter { background: none; border: 1px solid var(--topbar-ligne); color: var(--topbar-attenue); border-radius: var(--r); padding: 5px 10px; font-size: 12.5px; }
.topbar .btn-quitter:hover { border-color: var(--topbar-vif); color: var(--topbar-txt); }
/* V7.1 (RELI-430) — boutons réservés à l'écran étroit : la loupe (recherche en
   surcouche) et le menu qui recueille compte, Équipe, Admin, thème, Quitter.
   Invisibles au grand écran — le bandeau y est exactement celui d'avant.
   V8.57 — le second porte désormais la VIGNETTE du compte et non un « ⋮ » :
   voir ccBtnMenuMobile() (js/app.js) pour le pourquoi. Il perd donc son
   remplissage horizontal, qui écraserait la vignette. */
.btn-rech-mobile, .btn-menu-mobile {
  display: none; background: none; border: 1px solid var(--topbar-ligne); color: var(--topbar-attenue);
  border-radius: var(--r); padding: 4px 9px; font-size: 15px; line-height: 1.3;
  align-items: center; justify-content: center; /* V7.1b — centre la loupe SVG */
}
.btn-menu-mobile { padding: 3px; border-radius: 50%; }
/* ⚠ La vignette est ronde et pleine : le cadre du bouton ferait un double
   contour. Il n'apparaît qu'au survol et au focus, où il sert vraiment. */
.btn-menu-mobile:has(.cc-avatar) { border-color: transparent; }
.btn-menu-mobile:hover, .btn-menu-mobile:focus-visible { border-color: var(--topbar-vif); }
.btn-rech-mobile:hover, .btn-menu-mobile:hover,
.topbar.rech-ouverte .btn-rech-mobile { border-color: var(--topbar-vif); color: var(--topbar-txt); }

/* ---------- Barre latérale : l'arbre ---------- */
.sidebar {
  width: var(--sidebar-w); min-width: var(--sidebar-w);
  /* V11.5 — la hauteur vient du `flex:1` de `.app-corps`, plus d'un calc() en
     dur qui ignorait la présence d'un bandeau. */
  height: auto; min-height: 0;
  background: var(--card); border-right: 1px solid var(--line);
  display: flex; flex-direction: column;
}
.sidebar-tete {
  padding: 10px 12px; border-bottom: 1px solid var(--line-soft);
  display: flex; align-items: center; justify-content: space-between;
  /* V9.6 — `gap` + `wrap` : la pastille de quota (« Il reste 2 sur 5 ») se
     pose à côté du bouton « + racine » dans une barre de 292 px. Sans repli,
     elle comprimait l'eyebrow « CARTE DES CONNAISSANCES » jusqu'à le tronquer.
     Sans pastille — c'est-à-dire la plupart du temps — rien ne change : deux
     enfants qui tiennent sur une ligne se comportent exactement comme avant. */
  flex-wrap: wrap; gap: 6px;
}
.sidebar-tete .eyebrow { margin: 0; }
.eyebrow {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--ink-3);
}
.arbre { flex: 1; overflow: auto; padding: 6px 6px 40px; }

.noeud-ligne {
  display: flex; align-items: center; gap: 4px;
  padding: 3px 6px 3px 2px; border-radius: 5px;
  cursor: pointer; user-select: none; position: relative;
  border: 1px solid transparent;
}
.noeud-ligne:hover { background: var(--line-soft); }
.noeud-ligne.actif { background: var(--accent-pale); border-color: var(--line); }
.noeud-ligne.actif .noeud-nom { color: var(--accent-fonce); font-weight: 600; }
/* V2.4 — flèches déplier/replier agrandies (retour d'usage : cible de clic
   trop petite) : zone 26×26 px, glyphe 14 px, survol plus visible. */
.noeud-ligne .pli {
  width: 26px; height: 26px; line-height: 24px; text-align: center;
  color: var(--ink-3); font-size: 14px; border: none; background: none; padding: 0;
  flex: none; border-radius: 5px; cursor: pointer;
}
.noeud-ligne .pli:hover { background: var(--line); color: var(--ink); }
.noeud-ligne .pli.vide { visibility: hidden; }
.noeud-nom { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13.5px; }
.noeud-badge {
  font-family: var(--mono); font-size: 10px; color: var(--ink-3);
  background: var(--line-soft); border-radius: 8px; padding: 0 6px; flex: none;
}
.arbre ul { list-style: none; margin: 0; padding: 0 0 0 14px; border-left: 1px solid var(--line-soft); }
.arbre > ul { border-left: none; padding-left: 0; }

/* zones de drop */
.noeud-ligne.drop-into { background: var(--accent-pale); border: 1px dashed var(--accent); }
.noeud-ligne.drop-avant::before, .noeud-ligne.drop-apres::after {
  content: ""; position: absolute; left: 0; right: 0; height: 2px; background: var(--accent);
}
.noeud-ligne.drop-avant::before { top: -1px; }
.noeud-ligne.drop-apres::after { bottom: -1px; }
.noeud-ligne.en-deplacement { opacity: 0.4; }
.drop-racine {
  margin: 10px 8px; padding: 8px; border: 1px dashed var(--line);
  border-radius: var(--r); text-align: center; color: var(--ink-3); font-size: 12px;
  display: none;
}
.drop-racine.visible { display: block; }
.drop-racine.drop-into { border-color: var(--accent); color: var(--accent-fonce); background: var(--accent-pale); }

.sidebar-pied { padding: 10px 12px; border-top: 1px solid var(--line-soft); }

/* ---------- Contenu principal ---------- */
.contenu {
  /* V11.5 — même chose : la hauteur est celle que `.app-corps` lui laisse.
     `min-height: 0` garde `overflow:auto` opérant — c'est LUI le port de
     défilement de la V10.3, et il doit rester borné. */
  flex: 1; height: auto; min-height: 0;
  overflow: auto; padding: 22px 28px 80px;
}
.cap-chemin {
  font-family: var(--mono); font-size: 12px; color: var(--ink-2); margin-bottom: 6px;
}
.cap-chemin a { color: var(--ink-2); }
.cap-chemin a:hover { color: var(--accent-fonce); }
.cap-chemin .sep { color: var(--ink-3); padding: 0 4px; }

h1.titre-page {
  margin: 0 0 4px; font-size: 24px; font-weight: 700; letter-spacing: -0.01em;
  /* V6.2 (RELI-322) — le titre porte desormais des actions a sa droite
     (« Suivre »). Sans flex, le champ en width:100% les repoussait a la ligne.
     .titre-avec-icone posait deja exactement cette regle de son cote.
     PAS de `gap` ici : il ecraserait celui de .titre-avec-icone (h1.titre-page
     est plus specifique), et les libelles portent deja leur espace litteral. */
  display: flex; align-items: center;
}
h1.titre-page input {
  font: inherit; border: none; background: transparent; width: 100%;
  border-bottom: 1px dashed transparent; padding: 0; min-width: 0;
}
/* Les actions de titre restent a droite et ne se laissent pas comprimer. */
.titre-actions { flex: none; display: inline-flex; align-items: center; gap: 6px; margin-left: auto; }
/* V6.2 — jeton d'une valeur selectionnee que l'editeur ne sait pas nommer
   (compte proprietaire, collection privee d'un collegue). Volontairement terne
   et en chiffres : il dit « il y a quelque chose ici que je ne peux pas
   afficher », pas « voici une valeur normale ». */
.type-pill.inconnu { background: var(--line-soft); color: var(--ink-3); font-family: var(--mono); font-size: 11px; }
.titre-actions .btn-mini { font-size: 12px; font-weight: 500; letter-spacing: 0; }
@media (max-width: 700px) { .btn-suivre-tete .btn-suivre-txt { display: none; } }
h1.titre-page input:hover { border-bottom-color: var(--line); }
h1.titre-page input:focus { border-bottom-color: var(--accent); outline: none; }
.type-pill {
  display: inline-block; font-family: var(--mono); font-size: 10.5px;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--accent-fonce);
  background: var(--accent-pale); padding: 2px 8px; border-radius: 9px; vertical-align: middle;
}

.carte {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r);
  padding: 14px 16px; margin-bottom: 16px;
}
.carte h3 { margin: 0 0 8px; font-size: 13px; }
.carte h3 .eyebrow { display: block; }

/* V8.15.1 — TROIS COLONNES EN DUR, POUR TROIS CARTES QUI N'EXISTENT PLUS.
   Contexte, Vocabulaire et Ontologie se partageaient cette grille. Depuis que
   les deux dernières sont devenues une vue et une couche du graphe, il ne reste
   qu'une carte — qui continuait d'occuper un tiers de la largeur, avec deux
   colonnes vides à côté. `auto-fit` règle le cas sans rien décider d'avance :
   une carte prend toute la largeur, deux se partagent, trois se rangent en
   trois — et le jour où l'on en rajoute une, personne n'aura à y penser. */
.grille-contexte { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr)); gap: 12px; }
.zone-editable textarea {
  width: 100%; min-height: 84px; resize: vertical; border: 1px solid var(--line-soft);
  border-radius: var(--r); padding: 8px; font-size: 13px; background: var(--input-bg);
}
.zone-editable textarea:focus { border-color: var(--accent); outline: none; background: var(--input-bg); }

/* ---------- Onglets ---------- */
.onglets { display: flex; gap: 2px; border-bottom: 2px solid var(--line); margin: 18px 0 14px; }
.onglet {
  border: none; background: none; padding: 7px 14px; font-size: 13.5px;
  color: var(--ink-2); border-bottom: 2px solid transparent; margin-bottom: -2px;
}
.onglet:hover { color: var(--ink); }
.onglet.actif { color: var(--accent-fonce); font-weight: 600; border-bottom-color: var(--accent); }

/* ---------- Boutons ---------- */
.btn {
  border: 1px solid var(--line); background: var(--card); color: var(--ink);
  border-radius: var(--r); padding: 6px 12px; font-size: 13px;
}
.btn:hover { border-color: var(--ink-3); }
.btn-primaire { background: var(--accent-plein); border-color: var(--accent-plein); color: var(--sur-accent); }
.btn-primaire:hover { background: var(--accent-fonce); border-color: var(--accent-fonce); }
.btn-danger { color: var(--danger); border-color: var(--danger-pale); }
.btn-danger:hover { background: var(--danger-pale); }
.btn-mini { padding: 2px 8px; font-size: 12px; border-radius: 4px; }
.btn-lien { border: none; background: none; color: var(--accent-fonce); padding: 0; font-size: 12.5px; }
.btn-lien:hover { text-decoration: underline; }
/* V4.7 — boutons d'action icône : zone de clic confortable + retour au survol
   (les icônes seules restent, mais on ne « chasse » plus un glyphe de 12 px) */
.btn-lien.btn-mini { font-size: 14px; padding: 3px 6px; border-radius: 5px; line-height: 1.2; }
.btn-lien.btn-mini:hover { background: var(--accent-pale); text-decoration: none; }
/* V4.7 — icônes SVG (Lucide) : alignées sur le texte, couleur héritée */
.cc-icone { vertical-align: -2px; }
button .cc-icone, a .cc-icone { pointer-events: none; }

/* V4.9 — type checklist : jauge X/Y (orange < 50 %, jaune 50-99 %, vert 100 %) */
.jauge-checklist {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12px; font-weight: 700; cursor: pointer; white-space: nowrap;
}
.jauge-checklist .barre {
  width: 46px; height: 6px; border-radius: 3px; background: var(--line-soft, rgba(0,0,0,.12)); overflow: hidden;
}
.jauge-checklist .barre i { display: block; height: 100%; border-radius: 3px; }
.jauge-checklist .mention-attente { font-size: 13px; cursor: help; }

/* V4.12 — tableur : sélection de plage + couleurs de cellules */
.bloc-tableur td.sel { outline: 2px solid var(--accent); outline-offset: -2px; background: var(--accent-pale); }
.bloc-tableur td input { background: transparent; font-weight: inherit; }
.bloc-tableur th { cursor: pointer; user-select: none; position: relative; }
/* V4.12b — glisser-sélectionner, redimensionner les colonnes */
.bloc-tableur { table-layout: fixed; }
.bloc-tableur td { overflow: hidden; }
.bloc-tableur.drag-sel, .bloc-tableur.drag-sel input { user-select: none; cursor: cell; }
.bloc-tableur th .redim {
  position: absolute; top: 0; right: -3px; width: 7px; height: 100%;
  cursor: col-resize; z-index: 2;
}
.bloc-tableur th .redim:hover { background: var(--accent); opacity: .5; }
/* V4.13 — présence : pastilles flottantes + bloc/cellule occupés */
#cc-presence { position: fixed; top: 62px; right: 14px; z-index: 850; display: flex; flex-direction: column; gap: 5px; align-items: flex-end; pointer-events: none; }
.presence-pill {
  display: inline-flex; align-items: center; gap: 6px; pointer-events: auto;
  background: var(--card, #fff); border: 1px solid var(--line); border-radius: 14px;
  padding: 3px 10px; font-size: 12px; box-shadow: 0 2px 8px rgba(0, 0, 0, .08);
}
.presence-point { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.presence-detail { color: var(--ink-3); font-size: 11px; }
.bloc-ligne.bloc-occupe { position: relative; outline: 2px dashed rgba(245, 158, 11, .55); outline-offset: 2px; border-radius: 4px; }
.bloc-ligne.bloc-occupe::after {
  content: '✏ ' attr(data-occupant); position: absolute; top: -9px; right: 4px;
  font-size: 10px; line-height: 1.5; background: #f59e0b; color: #fff;
  border-radius: 8px; padding: 0 6px; pointer-events: none; z-index: 5;
}
td.cellule-occupee { position: relative; outline: 2px dashed #f59e0b; outline-offset: -2px; }
/* ⚠ V11.11 (retour de banc) — LE BADGE VIT DANS LA CELLULE, PLUS AU-DESSUS.
   `top:-9px` le faisait flotter hors de la cellule : sur la première ligne du
   tableau il passait SOUS l'en-tête figé et l'enveloppe défilante le coupait —
   « la bulle d'info est coupée, on voit rien ». Un onglet collé au coin
   intérieur ne peut être coupé par rien, et l'ellipse borne les noms longs. */
td.cellule-occupee::after {
  content: '✏ ' attr(data-occupant); position: absolute; top: 0; right: 0;
  max-width: calc(100% - 6px); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 10px; line-height: 1.7; background: #f59e0b; color: #fff;
  border-radius: 0 0 0 8px; padding: 0 6px; pointer-events: none; z-index: 5;
}
/* V11.11 — la présence croisée marque aussi les champs de la PAGE FICHE
   (titre, description courte, lignes de champ) : mêmes couleurs, encadrement
   vers l'extérieur — ces éléments ont des marges, contrairement aux <td>. */
:not(td).cellule-occupee { position: relative; outline: 2px dashed rgba(245, 158, 11, .55); outline-offset: 2px; border-radius: 4px; }
:not(td).cellule-occupee::after {
  content: '✏ ' attr(data-occupant); position: absolute; top: -9px; right: 4px;
  max-width: calc(100% - 8px); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 10px; line-height: 1.5; background: #f59e0b; color: #fff;
  border-radius: 8px; padding: 0 6px; pointer-events: none; z-index: 5;
}
/* V11.11 (retour de banc) — CONSULTER N'EST PAS MODIFIER. L'œil, un trait
   pointillé plus doux : on sait qu'elle est LÀ, pas qu'elle écrit ICI.
   Ne sert plus qu'aux CARTES (kanban) : une carte est la fiche entière,
   l'encadrement y est le bon grain. */
.cellule-occupee.presence-consulte { outline-style: dotted; outline-color: rgba(245, 158, 11, .5); }
.cellule-occupee.presence-consulte::after { content: '👁 ' attr(data-occupant); background: #d97706; }
/* ⚠ V11.11 (3e passe de banc) — SUR LE TABLEAU, QUELQU'UN « SUR LA FICHE »
   N'ENCADRE AUCUNE CELLULE. L'encadrement du titre — même pointillé — disait
   « elle édite le titre » : « elle apparaît sur le titre alors qu'elle n'est
   pas sur le titre ». La LIGNE se teinte, et une pastille 👁 se pose au bord
   droit de la colonne titre, centrée verticalement — DANS la cellule, donc
   jamais coupée par l'en-tête figé, et bornée par une ellipse. */
tr.ligne-consultee > td { background: rgba(217, 119, 6, .05); }
td.cellule-consultee { position: relative; }
td.cellule-consultee::after {
  content: '👁 ' attr(data-consultant); position: absolute; top: 50%; right: 4px;
  transform: translateY(-50%);
  max-width: 55%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 10px; line-height: 1.7; background: #d97706; color: #fff;
  border-radius: 8px; padding: 0 6px; pointer-events: none; z-index: 5;
}
/* V11.11 — « EN TRAIN D'ÉCRIRE » : le ⋯ des messageries. Le trait passe en
   PLEIN (occupé = pointillé, en frappe = plein) et le badge pulse FRANCHEMENT
   — le premier réglage (opacité .35, 1,1 s) demandait de se concentrer pour
   le voir, c'est un contresens pour un signal. Deux signaux redondants
   exprès : la couleur seule ne suffit pas à tout le monde. L'encadrement
   lui-même ne clignote pas : il marque une place, pas un événement. */
.presence-frappe { outline-style: solid !important; outline-color: #f59e0b !important; }
.presence-frappe::after { animation: cc-frappe .8s ease-in-out infinite; }
.presence-points { display: inline-block; margin-left: 4px; font-weight: 700; letter-spacing: 2px;
  font-size: 15px; line-height: 1; color: #b45309; animation: cc-frappe .8s ease-in-out infinite; }
.presence-pill-frappe { border: 1px solid #f59e0b; background: rgba(245, 158, 11, .1); }
.presence-pill-frappe .presence-detail { color: #b45309; font-weight: 600; }
@keyframes cc-frappe { 0%, 100% { opacity: 1; } 50% { opacity: .15; } }
@media (prefers-reduced-motion: reduce) {
  .presence-frappe::after, .presence-points { animation: none; }
}

/* ============================================================
   V9.38 (RELI-75/78) — LA CELLULE QUI VIENT DE CHANGER TOUTE SEULE.

   Depuis ce lot, la modification d'un collègue ne reconstruit plus l'écran :
   elle repeint la cellule concernée, sur place. ⚠ ET UNE VALEUR QUI CHANGE EN
   SILENCE REMPLACERAIT UN DÉFAUT PAR UN AUTRE — le tableau ne sauterait plus,
   et une note passerait de 3 à 8 sous les yeux de quelqu'un sans qu'il puisse
   dire si elle avait toujours été là. Le liseré dit « ceci vient de bouger ».

   Même vocabulaire visuel que « cellule occupée » juste au-dessus, mais l'autre
   couleur et l'autre durée : l'orange dit « quelqu'un écrit ICI, maintenant »
   et reste tant que c'est vrai ; le vert dit « c'est écrit, c'est arrivé » et
   s'efface. Les confondre aurait fait croire à une présence là où il n'y a
   qu'une trace.

   `box-shadow: inset` et non `outline` : le tableau est en `border-collapse:
   collapse`, où un outline se fait manger par les bordures voisines — c'est la
   leçon de la V9.34 sur l'en-tête figé, et elle vaut ici aussi.
   ============================================================ */
/* ⚠ V11.11 — LE SÉLECTEUR PERD SON `td`, ET C'EST UN CORRECTIF, PAS UN
   ÉLARGISSEMENT DE CONFORT. `ccLigneChampSignaler` (js/vues.js, V10.1) pose
   cette classe sur les LIGNES DE CHAMP de la page fiche — des <div> — depuis
   que la fiche sait se repeindre. Le sélecteur, écrit pour le tableau, ne les
   a jamais atteints : le liseré vert promis par la V10.1 n'a jamais été
   visible sur la page fiche. La V11.11 y ajoute la description courte et les
   blocs du document ; sans ce correctif, ses valeurs changeraient en silence —
   exactement ce que le commentaire au-dessus interdit. */
.cellule-repeinte {
  animation: cc-repeinte 1.6s ease-out;
  position: relative;
}
/* ⚠ V11.11 (retour de banc) — LE VERT DIT SON NOM. « Des fois un cadre vert
   sans nom, on voit rien » : le liseré signalait qu'une valeur venait de
   changer sans dire ni quoi ni pourquoi — à côté du badge orange nominatif,
   il passait pour un défaut. Il porte désormais sa propre étiquette
   (« ↻ <par qui / modifié> », posée par le JS dans data-repeinte), qui
   s'efface avec lui. ⚠ SAUF si la cellule est aussi OCCUPÉE : le badge
   orange nominatif suffit, deux étiquettes se chevaucheraient. */
.cellule-repeinte:not(.cellule-occupee)::after {
  content: '↻ ' attr(data-repeinte); position: absolute; top: 0; right: 0;
  max-width: calc(100% - 6px); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 10px; line-height: 1.7; background: var(--ok, #16a34a); color: #fff;
  border-radius: 0 0 0 8px; padding: 0 6px; pointer-events: none; z-index: 5;
  animation: cc-repeinte-badge 1.6s ease-out forwards;
}
@keyframes cc-repeinte-badge { 0% { opacity: 1; } 70% { opacity: 1; } 100% { opacity: 0; } }
@keyframes cc-repeinte {
  0%   { box-shadow: inset 0 0 0 2px var(--ok, #16a34a); background: color-mix(in srgb, var(--ok, #16a34a) 14%, transparent); }
  70%  { box-shadow: inset 0 0 0 2px var(--ok, #16a34a); background: color-mix(in srgb, var(--ok, #16a34a) 8%, transparent); }
  100% { box-shadow: inset 0 0 0 0 transparent; background: transparent; }
}
/* ⚠ ET ON N'ANIME PAS QUELQU'UN QUI A DEMANDÉ QU'ON N'ANIME PAS. Le liseré doit
   rester VISIBLE — c'est son travail — mais il ne clignote pas : il s'affiche
   et il disparaît, sans transition. */
@media (prefers-reduced-motion: reduce) {
  .cellule-repeinte { animation: none; box-shadow: inset 0 0 0 2px var(--ok, #16a34a); }
}

/* V4.12h — cellules référencées par la formule en cours d'édition */
/* ⚠ CES CINQ COULEURS SONT UNE DONNÉE, PAS UN THÈME. Elles numérotent les
   références d'une formule et doivent rester cinq teintes distinctes et
   reconnaissables dans les deux thèmes — les passer aux jetons les ramènerait
   à trois nuances de la même sarcelle. Idem l'orange de « cellule occupée »
   juste au-dessus. On n'y touche pas. */
.bloc-tableur td.ref-0 { box-shadow: inset 0 0 0 2px #3b82f6; }
.bloc-tableur td.ref-1 { box-shadow: inset 0 0 0 2px #ef4444; }
.bloc-tableur td.ref-2 { box-shadow: inset 0 0 0 2px #22c55e; }
.bloc-tableur td.ref-3 { box-shadow: inset 0 0 0 2px #a855f7; }
.bloc-tableur td.ref-4 { box-shadow: inset 0 0 0 2px #f59e0b; }

/* V4.10 — bloc « cases à cocher » */
.bloc-cases-ligne { display: flex; align-items: center; gap: 8px; padding: 1px 0; }
.bloc-cases-ligne input[type="checkbox"] { flex: none; }
/* V11.14 (retour de banc) — le ✕ au survol : retirer une ligne PLEINE à la
   souris, sans la vider caractère par caractère. Invisible au repos (une
   liste de vingt lignes hérissée de vingt croix serait du bruit), révélé au
   survol de SA ligne — et au focus clavier, pour rester atteignable. */
.bloc-cases-suppr { flex: none; opacity: 0; transition: opacity .12s; color: var(--ink-3); padding: 0 4px; }
.bloc-cases-ligne:hover .bloc-cases-suppr,
.bloc-cases-suppr:focus-visible { opacity: 1; }
.bloc-cases-suppr:hover { color: #a03d2c; }
.bloc-cases-texte { flex: 1; border: none; background: none; font: inherit; padding: 2px 0; outline: none; }
.bloc-cases-ligne.fait .bloc-cases-texte { text-decoration: line-through; color: var(--ink-3); }
/* V9.25 (RELI-36) — LA VUE D'UNE LIGNE DE CHECKLIST PORTANT UNE MENTION.
   Elle emprunte `.bloc-cases-texte` (police, marges, barré des lignes faites)
   pour que l'échange avec l'<input> ne déplace rien. Trois ajouts seulement :
   · une hauteur minimale, sans quoi une ligne vide serait haute de zéro ;
   · `pre-wrap`, parce qu'une vue PEUT aller à la ligne là où un <input>
     défile — on lit plus souvent qu'on n'édite, et un texte long tronqué à
     droite est illisible ;
   · un curseur de texte : elle se comporte comme un champ, elle doit en avoir
     l'air. */
.bloc-cases-vue { min-height: 1.4em; white-space: pre-wrap; word-break: break-word; cursor: text; }
.bloc-cases-vue:focus { outline: none; }
.bloc-cases-ligne.fait .bloc-cases-vue .cc-ref { text-decoration: line-through; }

/* V4.10 — planning « semaine type » (champ horaires) */
.semaine-type { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; margin-top: 10px; }
.semaine-entete { font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-3); padding: 4px 2px; text-align: center; border-bottom: 1px solid var(--line); margin-bottom: 4px; }
.semaine-col { min-height: 60px; }
.semaine-chip {
  display: block; width: 100%; text-align: left; margin: 3px 0; padding: 4px 6px;
  border: 1px solid var(--line); border-radius: 6px; background: var(--card);
  font-size: 11.5px; cursor: pointer; line-height: 1.35;
}
.semaine-chip:hover { border-color: var(--accent); background: var(--accent-pale); }
.semaine-chip strong { color: var(--accent-fonce); }

/* V4.9 — type horaires : chips de jours dans l'éditeur */
.jour-chip {
  border: 1px solid var(--line); background: var(--card); border-radius: 10px;
  padding: 2px 7px; font-size: 11px; cursor: pointer; text-transform: capitalize;
}
.jour-chip.actif { background: var(--accent-pale); border-color: var(--accent); color: var(--accent-fonce); font-weight: 600; }

/* ---------- Tableau (vue base) ---------- */
.barre-outils { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
.chip-filtre {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--accent-pale); color: var(--accent-fonce);
  border: 1px solid var(--line); border-radius: 12px; padding: 2px 8px; font-size: 12px;
}
.chip-filtre button { border: none; background: none; color: inherit; padding: 0; font-size: 13px; line-height: 1; }

.enveloppe-tableau { overflow-x: auto; background: var(--card); border: 1px solid var(--line); border-radius: var(--r); }
table.base { border-collapse: collapse; width: 100%; min-width: 640px; }
table.base th, table.base td {
  border-bottom: 1px solid var(--line-soft); border-right: 1px solid var(--line-soft);
  padding: 6px 10px; text-align: left; font-size: 13px; vertical-align: top;
}
table.base th {
  background: var(--sunk); font-family: var(--mono); font-size: 11px;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-2);
  cursor: pointer; user-select: none; white-space: nowrap; position: sticky; top: 0;
}

/* ============================================================
   V8.30 — EN-TÊTE DE TABLEAU FIGÉ AU DÉFILEMENT
   ------------------------------------------------------------
   ⚠ LE `position: sticky` CI-DESSUS EXISTE DEPUIS LONGTEMPS ET NE SERVAIT À
   RIEN. Il faut comprendre pourquoi avant de lire le correctif, sinon celui-ci
   a l'air arbitraire.

   `position: sticky` se cale sur le PLUS PROCHE ANCÊTRE QUI DÉFILE. Ici, cet
   ancêtre n'est pas `.contenu` (le vrai port de défilement de la page) mais
   `.enveloppe-tableau` : elle porte `overflow-x: auto`, et la spécification
   CSS dit qu'un `overflow` non-`visible` sur UN axe force l'autre à `auto`.
   L'enveloppe est donc, techniquement, une boîte qui défile — sauf qu'elle
   n'a aucune hauteur maximale, donc elle grandit avec le tableau et ne défile
   jamais verticalement. L'en-tête restait « collé » en haut d'une boîte qui ne
   bougeait pas : parfaitement immobile, et parfaitement inutile.

   Le correctif ne touche donc pas au `th` : il donne à l'enveloppe la hauteur
   qui lui manquait, ce qui en fait le port de défilement qu'elle prétendait
   déjà être.
   ============================================================ */
.enveloppe-tableau.entete-fige {
  /* La hauteur disponible sous la barre du haut (52 px), le fil d'Ariane et
     la barre d'outils de la vue, moins la marge basse de `.contenu`. Le
     `min-height` protège les petits écrans : mieux vaut déborder un peu que
     réduire le tableau à deux lignes et demie. */
  /* V9.5.2 — LE 210 px N'EST PLUS QU'UN FILET DE SÉCURITÉ. C'était la valeur
     de travail : une somme SUPPOSÉE (bandeau + fil d'Ariane + barre d'outils)
     qu'aucune mesure ne venait confirmer. Elle était trop grande dans le cas
     courant — ~130 px sont réellement occupés au-dessus du tableau — et le
     tableau se privait de la différence, soit une bonne ligne et demie.
     `ccAjusterTableauFige()` (js/app.js) pose maintenant la vraie valeur sur
     l'élément, mesurée après chaque rendu et à chaque redimensionnement. Le
     repli ci-dessous ne sert plus que si le script ne tourne pas. */
  max-height: calc(100vh - var(--entete-fige-marge, 210px));
  max-height: calc(100dvh - var(--entete-fige-marge, 210px)); /* dvh : hauteur réelle, cf. § 2 de app2.css */
  overflow: auto;
}

/* Un tableau à en-tête figé OCCUPE DÉJÀ TOUTE LA HAUTEUR : la réserve du bas
   de `.contenu` (96 px en v2, 80 en v1) devient sous lui une bande vide, et un
   défilement de page qui ne mène qu'à elle. C'est exactement ce que le retour
   de test appelait « on perd des centimètres ».
   ⚠ La classe est posée par le script UNIQUEMENT quand un tel tableau est à
   l'écran. Partout ailleurs — une fiche, un formulaire, un tableau dont
   l'en-tête n'est pas figé et qui coule dans la page — la réserve garde tout
   son sens et ne bouge pas d'un pixel. */
body.cc-tableau-plein .contenu { padding-bottom: 16px; }
/* Le plancher ne vaut que sur un écran assez haut pour se le permettre : posé
   sans condition, il réservait 240 px de vide sous une collection de deux
   fiches, cadre et bordure compris. */
@media (min-height: 700px) {
  .enveloppe-tableau.entete-fige { min-height: 240px; }
}
.enveloppe-tableau.entete-fige table.base th {
  /* ⚠ `border-collapse: collapse` (déclaré sur table.base) FAIT DÉFILER LES
     BORDURES AVEC LE CORPS : elles appartiennent alors à la table, pas aux
     cellules, et l'en-tête figé se retrouve sans trait sous lui dès la
     première ligne dépassée. On les redessine en ombre interne, qui, elle,
     est peinte avec la cellule. */
  /* V8.57 — les deux replis divergeaient du `:root` (#dbe4e6 / #e8eeef pour
     #d4dcd9 / #e4e9e7) : on les remet d'aplomb, un repli faux ne se voit que
     le jour où il sert. */
  box-shadow: inset 0 -1px 0 var(--line, #d4dcd9), inset -1px 0 0 var(--line-soft, #e4e9e7);
  /* Au-dessus des cellules qui passent dessous — et des contrôles qu'elles
     contiennent (interrupteurs, champs de saisie en cours d'édition). */
  z-index: 3;
}
.enveloppe-tableau.entete-fige table.base th.th-poignee,
.enveloppe-tableau.entete-fige table.base th:first-child { z-index: 4; }

/* ============================================================
   V9.34 (RELI-63) — L'EN-TÊTE FIGÉ MARCHE AUSSI SUR TÉLÉPHONE.
   ------------------------------------------------------------
   ⚠ CE BLOC ANNULE UNE DÉCISION DE LA V8.30, qui écrivait : « pas d'en-tête
   figé sous 800 px, quel que soit le réglage — sur un téléphone la hauteur
   d'écran est la ressource rare ». L'argument était juste et la conclusion
   fausse, parce qu'elle regardait une seule des deux dimensions.

   Ce qui manque sur un téléphone, ce n'est pas de la hauteur : c'est de la
   LARGEUR. Un tableau à huit colonnes y défile horizontalement sur trois
   écrans, et sans en-tête ni titre figés, la colonne qu'on lit n'a plus ni nom
   ni ligne — on regarde « 12/03/2026 » sans savoir ni de quelle date il
   s'agit, ni de quelle fiche. Le retour de test le dit sans détour : « sur
   mobile ça rendrait le truc inutilisable ». Économiser trente pixels de haut
   pour rendre le tableau illisible n'était pas le bon échange.

   La double barre de défilement reste un vrai inconvénient. Il est traité par
   ce qui l'entoure — `ccAjusterTableauFige()` (js/app.js) MESURE la place
   disponible plutôt que de la supposer, et `100dvh` suit la barre d'adresse
   qui se rétracte — et non plus en supprimant la fonction.
   ============================================================ */
@media (max-width: 800px) {
  /* Le plancher de 240 px n'a plus de sens ici : sur un écran de 600 px de
     haut dont 130 sont pris au-dessus, l'imposer reviendrait à réserver de la
     place à un tableau de deux lignes. On laisse la mesure décider. */
  .enveloppe-tableau.entete-fige { min-height: 0; }
}

/* ============================================================
   V9.34 (RELI-63) — LA COLONNE TITRE FIGÉE
   ------------------------------------------------------------
   ⚠ ELLE NE COÛTE RIEN QUAND ELLE NE SERT PAS. `position: sticky; left: 0`
   n'a d'effet que s'il y a un défilement horizontal : sur une collection dont
   le tableau tient dans la fenêtre, ces règles sont strictement invisibles.
   C'est ce qui permet de l'activer par défaut sans rien changer à ce que les
   gens voient aujourd'hui.

   ⚠ ET C'EST LA LIGNE QUI PORTE LA COULEUR, PAS LA CELLULE. Une cellule figée
   doit être OPAQUE, sinon le tableau défile en transparence dessous. Lui poser
   un fond en dur aurait mangé le survol de ligne (v1) et le fond de la ligne
   en création. On déplace donc la couleur sur le `tr`, et la cellule figée
   l'hérite — ce qui est exactement la doctrine que l'interface v2 avait déjà
   écrite pour elle-même : « une seule couche de couleur : la ligne. Jamais la
   cellule. » Les deux interfaces se retrouvent ici sans qu'on ait eu à écrire
   deux fois les règles.

   ⚠ LES BORDURES, ELLES, NE SUIVENT PAS. `border-collapse: collapse`
   (table.base) fait appartenir les bordures à la TABLE, pas aux cellules :
   une cellule figée se retrouve donc sans trait à sa droite dès qu'on défile.
   Même remède que pour l'en-tête figé de la V8.30 — une ombre interne, qui,
   elle, est peinte avec la cellule.
   ============================================================ */
.enveloppe-tableau.col-titre-figee table.base tbody tr { background: var(--card); }
.enveloppe-tableau.col-titre-figee table.base tbody tr:hover { background: var(--sunk); }
.enveloppe-tableau.col-titre-figee table.base tbody tr.ligne-nouvelle { background: var(--sunk); }
.enveloppe-tableau.col-titre-figee table.base tbody tr.saisie-privee { background: var(--prive-pale, rgba(107, 79, 158, .08)); }
.enveloppe-tableau.col-titre-figee table.base th.th-titre,
.enveloppe-tableau.col-titre-figee table.base td.cellule-titre,
.enveloppe-tableau.col-titre-figee table.base th.th-poignee,
.enveloppe-tableau.col-titre-figee table.base td.td-poignee {
  position: sticky; left: 0; z-index: 2;
  box-shadow: inset -1px 0 0 var(--line-soft, #e4e9e7);
}
/* La poignée ne porte pas de trait : c'est le titre, juste après elle, qui
   marque le bord de la zone figée. Deux traits collés feraient un liseré. */
.enveloppe-tableau.col-titre-figee table.base th.th-poignee,
.enveloppe-tableau.col-titre-figee table.base td.td-poignee { box-shadow: none; }
/* Les cellules de corps héritent de la couleur de leur ligne (cf. ci-dessus).
   L'en-tête, lui, a déjà son propre fond opaque (`--sunk`) : on n'y touche pas. */
.enveloppe-tableau.col-titre-figee table.base td.cellule-titre,
.enveloppe-tableau.col-titre-figee table.base td.td-poignee { background: inherit; }
/* ⚠ L'AFFORDANCE D'ÉDITION SURVIT. Sans cette ligne, la règle d'héritage
   ci-dessus (plus spécifique) écrasait `td.cellule-editable:hover` et la
   colonne titre devenait la seule à ne plus se teinter sous le curseur —
   c'est-à-dire la seule à ne plus dire qu'elle est modifiable. */
.enveloppe-tableau.col-titre-figee table.base tbody tr td.cellule-titre.cellule-editable:hover {
  background: var(--accent-pale);
}
/* En ordre personnalisé, une colonne-poignée de 28 px (cf. le colgroup de
   js/vues.js) précède le titre : la zone figée commence donc plus loin. */
.enveloppe-tableau.col-titre-figee.avec-poignee table.base th.th-titre,
.enveloppe-tableau.col-titre-figee.avec-poignee table.base td.cellule-titre { left: 28px; }
/* Le coin haut-gauche est figé DEUX FOIS (en haut et à gauche) : il doit
   passer au-dessus de ses deux voisins, sinon il disparaît sous l'un d'eux
   dès qu'on défile en diagonale. */
.enveloppe-tableau.col-titre-figee.entete-fige table.base th.th-titre,
.enveloppe-tableau.col-titre-figee.entete-fige table.base th.th-poignee { z-index: 5; }

/* ⚠ ET SURTOUT : PAS D'EN-TÊTE FIGÉ À L'IMPRESSION. C'est la régression que la
   relecture adverse a attrapée, et elle était silencieuse. Le réglage étant
   ACTIF PAR DÉFAUT, la boîte gardait à l'impression son `max-height` et son
   `overflow: auto` — or aucun navigateur n'imprime ce qui dépasse d'un
   conteneur à défilement. Une collection de 300 fiches sortait donc sur UNE
   page au lieu de dix, sans le moindre avertissement, là où la V8.29 imprimait
   tout. On rend donc la boîte à sa hauteur naturelle, et l'en-tête à sa
   position statique — un en-tête « collé en haut de l'écran » n'ayant de toute
   façon aucun sens sur du papier. */
@media print {
  .enveloppe-tableau.entete-fige {
    max-height: none !important; min-height: 0 !important; overflow: visible !important;
  }
  .enveloppe-tableau.entete-fige table.base th {
    position: static !important; box-shadow: none !important; z-index: auto !important;
  }
  /* V9.34 — MÊME RAISON POUR LA COLONNE. Une colonne « collée à gauche » n'a
     pas plus de sens sur du papier qu'un en-tête collé en haut, et le
     `position: sticky` fait dériver la cellule au moment du découpage en
     pages. Le fond de ligne posé pour l'opacité part aussi : imprimer une
     trame grise sur chaque ligne survolée-au-hasard gâche l'encre. */
  .enveloppe-tableau.col-titre-figee table.base th.th-titre,
  .enveloppe-tableau.col-titre-figee table.base td.cellule-titre,
  .enveloppe-tableau.col-titre-figee table.base th.th-poignee,
  .enveloppe-tableau.col-titre-figee table.base td.td-poignee {
    position: static !important; box-shadow: none !important; z-index: auto !important;
  }
  .enveloppe-tableau.col-titre-figee table.base tbody tr { background: none !important; }
}
/* ============================================================
   V9.35 (RELI-61) — LA LÉGENDE D'UNE COLONNE, SOUS SON NOM
   ------------------------------------------------------------
   « Importance », et dessous, en petit : « 1 = faible, 10 = critique ».

   ⚠ TROIS RÈGLES DE L'EN-TÊTE JOUENT CONTRE ELLE, ET IL FAUT LES DÉFAIRE
   TOUTES LES TROIS — en défaire deux donne une légende invisible, ce qui est
   pire qu'une légende absente, parce qu'on l'a écrite et qu'on la croit là :
     · `white-space: nowrap` (table.base th) : la légende est une PHRASE, elle
       doit pouvoir passer à la ligne dans une colonne étroite ;
     · `text-transform: uppercase` + `font-family: var(--mono)` : hérités du
       nom de colonne, ils rendraient « 1 = FAIBLE, 10 = CRITIQUE » en chasse
       fixe — illisible, et faux (la légende est saisie telle quelle) ;
     · `overflow: hidden` sur le `th` (css ligne ~1290, pour la troncature des
       noms) : il coupe la seconde ligne net. On ne le retire pas — il sert au
       nom — mais le `th` reprend une hauteur automatique dès qu'il porte une
       légende, sinon il resterait à la hauteur d'une ligne et n'en montrerait
       que la moitié.

   ⚠ ET L'EN-TÊTE FIGÉ CONTINUE DE MARCHER. Il se cale sur la hauteur réelle du
   `th` (`position: sticky; top: 0`), pas sur une valeur écrite quelque part :
   une légende le rend simplement plus haut. C'est vérifié à la lecture, pas
   dans un navigateur — voir le § « ce qui n'est pas fait » de la note.
   ============================================================ */
table.base th .col-legende {
  display: block;
  margin-top: 2px;
  font-family: var(--sans, inherit);
  font-size: 10.5px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink-3, var(--ink-2));
  white-space: normal;
  line-height: 1.25;
}
/* La hauteur d'un en-tête qui porte une légende n'est plus celle d'une ligne. */
table.base th.th-legende,
table.base th:has(.col-legende) { white-space: normal; vertical-align: top; }

/* Sur la FICHE, la légende vit sous le libellé du champ — même classe, autre
   contexte : ici rien ne la contrarie, il suffit de la rendre discrète et de
   défaire la casse du <label> de formulaire. */
.form-edition > label .col-legende,
label .col-legende {
  display: block; margin-top: 3px;
  font-size: 11.5px; font-weight: 400; letter-spacing: 0;
  text-transform: none; color: var(--ink-3, var(--ink-2));
}

/* ⚠ PAS DE LÉGENDE À L'IMPRESSION NI DANS UN EN-TÊTE FIGÉ ÉTROIT ? Si —
   justement. Elle s'imprime : c'est du contenu, pas une affordance d'écran,
   et un tableau imprimé est précisément celui qu'on lit sans pouvoir cliquer
   sur quoi que ce soit pour comprendre une échelle. */

table.base th .fleche-tri { color: var(--accent-fonce); }
table.base th.th-plus { cursor: default; width: 40px; text-align: center; }
table.base td.cellule-editable { cursor: text; }
/* V7.0 (RELI-390) — « des fois je vois mal la souris, surtout sur le tableau ».
   Le seul retour visuel sous le curseur était CE survol de cellule — un
   #f4f9f9 sur fond blanc, quasi indiscernable — et rien au niveau de la
   ligne : perdre le curseur des yeux, c'était le perdre tout court. Deux
   renforts : la LIGNE survolée se teinte (on retrouve la souris d'un coup
   d'œil, même entre deux cellules), et la CELLULE éditable passe à l'accent
   pâle, franchement visible. Les fonds spéciaux (ligne-nouvelle, formules)
   sont recouverts PENDANT le survol seulement : c'est le but — dire où l'on
   est — et tout revient en repartant. */
table.base tbody tr:hover td { background: var(--sunk); }
table.base td.cellule-editable:hover,
table.base tbody tr:hover td.cellule-editable:hover { background: var(--accent-pale); }
table.base td input, table.base td select {
  width: 100%; border: 1px solid var(--accent); border-radius: 3px; padding: 2px 5px; font-size: 13px; background: var(--input-bg);
}
table.base td .valeur-multi { display: inline-block; background: var(--line-soft); border-radius: 8px; padding: 0 7px; margin: 0 3px 2px 0; font-size: 12px; }
table.base td .booleen { font-family: var(--mono); }
td.cellule-titre a { font-weight: 600; }
tr.ligne-nouvelle td { background: var(--sunk); }
tr.ligne-nouvelle input { border: 1px solid var(--line); }
tr.ligne-nouvelle input:focus { border-color: var(--accent); }
.desc-courte-cellule { color: var(--ink-2); }

/* popover ajout colonne / multiselect */
.popover {
  position: fixed; z-index: 90; background: var(--card); border: 1px solid var(--line); /* V3.1 : au-dessus des modales (palette couleurs dans « Gérer les options ») */
  border-radius: var(--r); box-shadow: var(--ombre-flot); padding: 12px; min-width: 230px;
  max-height: calc(100vh - 24px); overflow-y: auto; /* V3.2 : jamais coupé — défilement interne en dernier recours */
  /* V8.40 — PLAFOND DE LARGEUR, ET C'EST UN CORRECTIF, PAS UN RAFFINEMENT.
     Un popover est en position fixe SANS `width` : sa largeur est celle de son
     contenu. Il y avait donc un `max-height` pour la hauteur et RIEN pour la
     largeur — asymétrie qui n'avait pas de raison d'être. Un contenu qui porte
     un paragraphe d'aide un peu long l'étirait jusqu'au bord de l'écran :
     mesuré à 1430 px sur un écran de 1440, et 1910 px sur un écran de 1920,
     pour le réglage d'une colonne « nombre ». La plupart des popovers posaient
     un `min-width` de 220 à 340 px et ne s'en approchaient jamais — d'où un
     défaut resté invisible des années, jusqu'à ce qu'on ouvre le même écran
     cinq fois de suite avec cinq contenus différents.
     520 px laisse toute leur place aux plus larges (le sélecteur de type,
     l'éditeur de formules) sans qu'aucun puisse prendre l'écran. Le
     `calc(100vw - 24px)` couvre le téléphone, où 520 px déborderait. */
  max-width: min(520px, calc(100vw - 24px));
}
/* V8.23 — un popover ouvert DEPUIS un autre passe au-dessus de lui, et le dit
   d'un liseré : sans repère visuel, deux panneaux superposés se lisent comme
   un seul panneau devenu incohérent. */
.popover-enfant { z-index: 95; border-color: var(--accent); box-shadow: 0 10px 30px rgba(24, 43, 58, 0.24); }
.popover label { display: block; font-size: 12px; color: var(--ink-2); margin: 8px 0 3px; }
.popover input[type="text"], .popover select, .popover textarea {
  width: 100%; border: 1px solid var(--line); border-radius: 4px; padding: 5px 7px; font-size: 13px;
}
.popover .actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 10px; }
.popover .case { display: flex; align-items: center; gap: 6px; font-size: 13px; margin: 3px 0; }

/* V9.5.1 — LA LISTE D'OPTIONS D'UN POPOVER DE CHOIX (multiselect, select,
   relation, « rattacher aussi à… »). Le plafond de 240 px vivait en style
   en ligne dans quatre appels de vues.js ; il est ici parce que
   `ouvrirPopover()` doit pouvoir le RECALCULER (js/api.js, ajusterListe) en
   fonction de la place réellement disponible sous — ou sur — l'ancre. C'est ce
   recalcul qui supprime la seconde barre de défilement : sans lui, la liste
   gardait ses 240 px alors que le popover, lui, n'avait plus la place, et les
   deux défilaient en même temps.
   `overscroll-behavior: contain` évite au passage que la molette, arrivée en
   bout de liste, se mette à faire défiler le tableau DERRIÈRE le popover. */
.cc-pop-liste { max-height: 240px; overflow-y: auto; overscroll-behavior: contain; }

/* V8.48 — le choix de portée de « Lier les fiches… ». Encadré, parce que ce
   n'est pas un réglage de plus dans une pile de réglages : c'est la question
   « sur quoi ça va porter ? », et elle doit se lire avant le type de lien.
   ⚠ `.popover label { display:block }` ci-dessus s'applique aussi aux deux
     lignes de choix : sans le `display:flex` ici, le bouton radio se mettrait
     au-dessus de son libellé. */
.popover .cc-portee-lien {
  border: 1px solid var(--line); border-radius: 6px;
  padding: 2px 9px 8px; margin: 8px 0 2px; background: var(--paper);
}
.popover .cc-portee-lien > label:first-child { margin-top: 6px; font-weight: 600; color: var(--ink); }
.popover label.cc-portee-choix {
  display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap;
  font-size: 12.5px; color: var(--ink); margin: 4px 0 0; cursor: pointer;
}
.popover label.cc-portee-choix input[type="radio"] { width: auto; flex: none; margin: 0; }
.popover label.cc-portee-choix em { font-style: normal; color: var(--ink-3); font-size: 11.5px; }

/* ---------- Kanban ---------- */
.kanban { display: flex; gap: 12px; overflow-x: auto; align-items: flex-start; padding-bottom: 10px; }
.kanban-col {
  min-width: 230px; width: 230px; background: var(--sunk); border: 1px solid var(--line);
  border-radius: var(--r); padding: 8px; flex: none;
}
.kanban-col.drop-into { border-color: var(--accent); background: var(--accent-pale); }
.kanban-col h4 {
  margin: 0 0 8px; font-family: var(--mono); font-size: 11px; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--ink-2); display: flex; justify-content: space-between;
}
.kanban-carte {
  background: var(--card); border: 1px solid var(--line); border-radius: 5px;
  padding: 8px 10px; margin-bottom: 8px; cursor: grab;
}
.kanban-carte:active { cursor: grabbing; }
.kanban-carte .t { font-weight: 600; font-size: 13px; }
.kanban-carte .d { color: var(--ink-2); font-size: 12px; margin-top: 2px; }
.kanban-carte.en-deplacement { opacity: 0.4; }

/* ---------- Notes ---------- */
.note-item {
  display: flex; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--line-soft);
  align-items: baseline;
}
.note-item:last-child { border-bottom: none; }
.badge-type {
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase;
  padding: 1px 7px; border-radius: 8px; flex: none;
}
.badge-type.decision { background: var(--accent-pale); color: var(--st-actif); }
.badge-type.question { background: var(--warn-pale); color: var(--st-ouvert); }
.badge-type.assumption { background: var(--prive-pale); color: var(--prive); }
.badge-type.todo { background: var(--ok-pale); color: var(--st-resolu); }
.badge-statut { font-family: var(--mono); font-size: 10.5px; flex: none; }
.badge-statut::before { content: "● "; font-size: 8px; vertical-align: 1px; }
.badge-statut.ouvert { color: var(--st-ouvert); }
.badge-statut.actif { color: var(--st-actif); }
.badge-statut.resolu { color: var(--st-resolu); }
.badge-statut.abandonne { color: var(--st-abandonne); }
.badge-statut.remplace { color: var(--st-remplace); }
.note-contenu { flex: 1; }
.note-contenu .remplacement { color: var(--ink-2); font-size: 12px; margin-top: 2px; }
.note-contenu .origine { font-size: 11.5px; color: var(--ink-3); font-family: var(--mono); }
.note-actions { display: flex; gap: 6px; flex: none; }
.note-actions select { font-size: 11.5px; border: 1px solid var(--line); border-radius: 4px; padding: 1px 3px; }
.note-date { font-family: var(--mono); font-size: 11px; color: var(--ink-3); flex: none; }

.form-note { display: flex; gap: 8px; margin-top: 10px; }
.form-note select { border: 1px solid var(--line); border-radius: var(--r); padding: 5px; font-size: 13px; }
.form-note input { flex: 1; border: 1px solid var(--line); border-radius: var(--r); padding: 5px 9px; font-size: 13px; }

/* V7.2 (RELI-440) — carte Notes : onglets par type, recherche, réponse,
   et les notes multi-lignes (ADR) qui se lisent en lignes. */
.notes-outils { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 10px 0; }
.notes-onglets { display: flex; flex-wrap: wrap; gap: 4px; }
.note-onglet {
  border: 1px solid var(--line); background: var(--card); border-radius: 12px;
  padding: 2px 10px; font-size: 12px; color: var(--ink-2); cursor: pointer;
}
.note-onglet:hover { border-color: var(--accent); }
.note-onglet.actif { background: var(--accent-pale); border-color: var(--accent); color: var(--accent-fonce); font-weight: 600; }
.note-onglet-nb { font-family: var(--mono); font-size: 10.5px; color: var(--ink-3); }
.note-onglet.actif .note-onglet-nb { color: var(--accent-fonce); }
.note-recherche { flex: 1; min-width: 160px; border: 1px solid var(--line); border-radius: 8px; padding: 4px 10px; font-size: 13px; }
.note-item .cc-texte-lecture { white-space: pre-line; }
.note-reponse { font-size: 13px; color: var(--st-resolu); margin-top: 2px; white-space: pre-line; }

/* ---------- Fiche détail ---------- */
.fiche-grille { display: grid; grid-template-columns: minmax(0, 1fr) 330px; gap: 18px; align-items: start; }
@media (max-width: 1050px) { .fiche-grille { grid-template-columns: 1fr; } }
.detail-long textarea { min-height: 220px; font-size: 13.5px; line-height: 1.55; }
.rattachement-bloc { border: 1px solid var(--line-soft); border-radius: var(--r); padding: 10px 12px; margin-bottom: 10px; }
.rattachement-bloc .chemin { font-family: var(--mono); font-size: 11px; color: var(--ink-3); }
.champ-ligne { display: flex; align-items: center; gap: 8px; margin: 6px 0; }
.champ-ligne label { width: 110px; flex: none; font-size: 12.5px; color: var(--ink-2); }
.champ-ligne input, .champ-ligne select {
  flex: 1; border: 1px solid var(--line); border-radius: 4px; padding: 4px 7px; font-size: 13px; min-width: 0;
}
.lien-item { display: flex; gap: 8px; padding: 5px 0; font-size: 13px; align-items: baseline; }
.lien-item .rel { font-family: var(--mono); font-size: 11.5px; color: var(--accent-fonce); background: var(--accent-pale); border-radius: 8px; padding: 1px 7px; flex: none; }
.fichier-item { display: flex; gap: 8px; align-items: baseline; padding: 4px 0; font-size: 13px; }
.fichier-item .taille { font-family: var(--mono); font-size: 11px; color: var(--ink-3); }

/* ---------- Export : le manifeste ---------- */
.export-grille { display: grid; grid-template-columns: 330px minmax(0, 1fr); gap: 18px; align-items: start; }
@media (max-width: 1000px) { .export-grille { grid-template-columns: 1fr; } }
.export-form label.bloc { display: block; font-size: 12.5px; color: var(--ink-2); margin: 12px 0 4px; }
.export-form .cases { display: flex; flex-direction: column; gap: 4px; }
.export-form .case { display: flex; align-items: center; gap: 7px; font-size: 13px; }
.export-form select, .export-form input[type="text"], .export-form input[type="number"] {
  width: 100%; border: 1px solid var(--line); border-radius: 4px; padding: 5px 8px; font-size: 13px;
}
.filtre-ligne { display: flex; gap: 6px; margin-bottom: 6px; }
.filtre-ligne select, .filtre-ligne input { flex: 1; min-width: 0; }

.manifeste {
  background: #10222f; color: #d7e4e6; border-radius: var(--r);
  border-left: 4px solid var(--accent);
  font-family: var(--mono); font-size: 12.5px; line-height: 1.6;
  padding: 0; overflow: hidden;
}
.manifeste-tete {
  display: flex; justify-content: space-between; align-items: center; gap: 10px;
  padding: 8px 14px; background: #0b1922; border-bottom: 1px solid #24404d;
  font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: #7fa6ad;
}
.manifeste-tete .stats span { margin-left: 12px; color: #a9c6ca; }
.manifeste pre {
  margin: 0; padding: 14px 16px; overflow: auto; max-height: 62vh;
  white-space: pre-wrap; word-break: break-word;
}
.manifeste .vide { padding: 30px; text-align: center; color: #6d8a91; }

/* ---------- Recherche ---------- */
.resultat-groupe h3 { font-family: var(--mono); font-size: 11.5px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-3); margin: 18px 0 6px; }
.resultat-item { padding: 7px 0; border-bottom: 1px solid var(--line-soft); font-size: 13.5px; }
.resultat-item .extrait { color: var(--ink-2); font-size: 12.5px; }

/* ---------- Connexion ---------- */
.page-connexion {
  /* #app est en display:flex — sans flex:1 la page se réduit à la largeur
     de la boîte et laisse le fond blanc à droite. */
  flex: 1 1 100%; width: 100%;
  min-height: 100vh; display: flex; align-items: center; justify-content: center;
  /* V8.57 — MÊME INVERSION QUE LE BANDEAU. Cet écran est sombre par CHOIX,
     dans les deux thèmes : son fond demandait « du foncé », pas « l'encre ».
     `var(--ink)` devenant clair en sombre, la page virait au blanc sous une
     tache marine (le dégradé, lui, resté en dur). `--topbar` dit le rôle. */
  background: var(--topbar);
  background-image: radial-gradient(circle at 20% 20%, #22394a 0, transparent 45%),
                    linear-gradient(transparent 31px, rgba(255,255,255,0.03) 32px);
  background-size: auto, 100% 32px;
}
.boite-connexion {
  background: var(--card); border-radius: 8px; padding: 30px 32px; width: 360px;
  border-top: 4px solid var(--accent);
}
.boite-connexion .marque { color: var(--ink); font-size: 21px; letter-spacing: -0.01em; }
.boite-connexion .marque .cap { color: var(--accent-fonce); }
.boite-connexion .sous-titre { color: var(--ink-2); font-size: 12.5px; margin: 4px 0 20px; }
.boite-connexion label { display: block; font-size: 12.5px; color: var(--ink-2); margin: 12px 0 4px; }
.boite-connexion input {
  width: 100%; border: 1px solid var(--line); border-radius: var(--r); padding: 8px 10px; font-size: 14px;
}
.boite-connexion .btn-primaire { width: 100%; margin-top: 18px; padding: 9px; }
.erreur-connexion { color: var(--danger); font-size: 13px; margin-top: 10px; min-height: 18px; }

/* ---------- V8.18 : connexion externe (Google / Microsoft) ----------
   Volontairement SOBRES et identiques entre eux : ni bleu Google ni bleu
   Microsoft. Reproduire les boutons officiels imposerait leurs polices, leurs
   logos et leurs règles de marque — et ferait de l'écran d'entrée un patchwork
   qui change d'allure selon le fournisseur configuré. Ici, la marque du
   fournisseur est dans le TEXTE ; le bouton, lui, appartient à Relieo. */
.boite-connexion .btn-sso {
  display: flex; align-items: center; justify-content: center; gap: 9px;
  width: 100%; box-sizing: border-box; margin-top: 10px; padding: 9px;
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--card); color: var(--ink); font-size: 14px; cursor: pointer;
}
/* V8.57 — `--fond-2` N'EXISTE NULLE PART : ce n'était pas un jeton avec un
   repli, c'était un blanc cassé écrit en dur derrière un nom de jeton. Le rôle
   est celui de `--sunk` (surface enfoncée, survol). */
.boite-connexion .btn-sso:hover { border-color: var(--accent); background: var(--sunk); }
.boite-connexion .sso-logo {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; border-radius: 3px;
  background: var(--sunk); color: var(--ink-2);
  font-weight: 700; font-size: 12px; line-height: 1;
}
/* Le « ou » : un trait coupé par un mot, plutôt qu'un mot posé dans le vide. */
.boite-connexion .sso-ou {
  display: flex; align-items: center; gap: 10px;
  margin: 18px 0 2px; color: var(--ink-3); font-size: 12px;
}
.boite-connexion .sso-ou::before,
.boite-connexion .sso-ou::after { content: ''; flex: 1; height: 1px; background: var(--line); }

/* ---------- Toast ---------- */
#toasts { position: fixed; bottom: 18px; right: 18px; z-index: 100; display: flex; flex-direction: column; gap: 8px; }
.toast {
  /* V8.57 — même inversion : un toast est une surface sombre à texte clair.
     En sombre, `var(--ink)` clair + texte clair donnait un toast illisible. */
  background: var(--topbar); color: var(--topbar-txt); border-left: 3px solid var(--accent);
  border-radius: 5px; padding: 9px 14px; font-size: 13px; max-width: 340px;
  box-shadow: 0 6px 18px rgba(24,43,58,0.3);
}
.toast.erreur { border-left-color: var(--danger); }
/* V9.16 — le toast qui porte une action : il vit plus longtemps, il doit donc
   supporter d'être lu plutôt que seulement aperçu. Liseré d'avertissement et
   non de danger : l'écriture a réussi, c'est ce qu'elle a recouvert qu'on
   signale. */
.toast.avec-action {
  border-left-color: var(--warn);
  display: flex; align-items: center; gap: 8px; max-width: 460px;
}
.toast.avec-action span { flex: 1; }

/* ---------- Divers ---------- */
.vide-etat { color: var(--ink-3); font-size: 13px; padding: 14px 0; }
.ligne-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.mt { margin-top: 14px; }
.confirm-suppr { color: var(--danger); font-size: 12.5px; }

@media (max-width: 800px) {
  /* ⚠ V11.5 — LE TIROIR REPREND SA HAUTEUR EXPLICITEMENT. Hors mobile, la
     barre tire sa hauteur du `flex:1` de `.app-corps` ; ici elle est
     `position:fixed`, donc hors flux, et un `height:auto` la réduirait à la
     hauteur de son contenu — un tiroir à moitié vide. On l'ancre en haut et en
     bas, et on dégage la topbar par un padding plutôt que par un `top:52px` :
     avec un bandeau, ce 52 aurait été faux, et le tiroir serait reparti sous
     le message au lieu de le recouvrir. */
  .sidebar {
    position: fixed; top: 0; bottom: 0; left: 0; height: auto;
    padding-top: var(--topbar-h, 52px);
    z-index: 30; transform: translateX(-100%); transition: transform 0.18s;
  }
  .sidebar.ouverte { transform: none; box-shadow: 4px 0 20px rgba(0,0,0,0.15); }
  .btn-burger { display: inline-block !important; }
  .contenu { padding: 16px 14px 80px; }
}
.btn-burger { display: none; background: none; border: none; color: var(--topbar-txt); font-size: 18px; padding: 4px; }

/* ---------- V9.5.1 : replier la barre latérale (écran large) ----------
   Le bouton vit dans le BANDEAU (cf. ccBoutonReplierSidebar, js/app.js) : logé
   dans la barre, il partirait avec elle et il faudrait en inventer un second
   pour la rouvrir. Ici il ne bouge pas — même endroit ouverte ou fermée.

   ⚠ IL N'EXISTE QU'AU-DESSUS DE 800 px, où le burger n'existe pas. En dessous,
   la barre est un tiroir en surcouche : elle ne prend déjà aucune place, la
   replier ne gagnerait rien, et deux boutons pour un même geste se marcheraient
   dessus. Un seul des deux est affiché, jamais les deux. */
.btn-repli {
  display: none; background: none; border: 1px solid transparent; border-radius: 6px;
  color: var(--topbar-txt); padding: 5px; margin-right: 2px; cursor: pointer;
  line-height: 0; /* le <svg> ne doit pas traîner une ligne de texte sous lui */
}
.btn-repli:hover, .btn-repli:focus-visible { border-color: var(--topbar-vif); }
@media (min-width: 801px) { .btn-repli { display: inline-flex; } }

/* La largeur, et RIEN QUE la largeur. `.contenu` est en `flex: 1` : il reprend
   la place tout seul, sans qu'on ait à toucher à sa mise en page. On anime donc
   une seule propriété, ce qui garde la bascule fluide même sur un tableau de
   plusieurs centaines de lignes.
   `visibility` accompagne la fermeture : à 0 px de large, un enfant en
   `position: fixed` (un popover ouvert dans l'arbre) resterait sinon visible et
   cliquable au-dessus du tableau — invisible sur le papier, gênant à l'usage. */
/* ⚠ LA RÈGLE EST ENFERMÉE DANS LE MÊME @media QUE LE BOUTON, et ce n'est pas
   de la symétrie : sans cela, quelqu'un qui replie la barre sur son écran de
   bureau puis rouvre l'application sur son téléphone (l'état est mémorisé)
   retrouvait un tiroir large de zéro pixel — le ☰ s'ouvrait sur du vide, sans
   qu'aucun bouton visible à cette largeur ne permette d'en sortir. Le repli
   n'a de sens que là où la barre occupe vraiment de la place. */
@media (min-width: 801px) {
  .sidebar { transition: width 0.16s ease, min-width 0.16s ease; }
  body.sidebar-repliee .sidebar {
    width: 0; min-width: 0; overflow: hidden;
    border-right: none; visibility: hidden;
  }
}
/* Pas d'animation pour qui a demandé qu'on lui en épargne (réglage système). */
@media (prefers-reduced-motion: reduce) { .sidebar { transition: none; } }


/* ---------- V2 : Vue Graphe ---------- */
.graphe-outils { align-items: center; gap: 10px; flex-wrap: wrap; }
.graphe-outils .case { gap: 5px; font-size: 12.5px; }
.graphe-conteneur {
  height: 64vh; min-height: 380px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r);
}
.graphe-legende { font-size: 12px; color: var(--ink-3); margin-top: 6px; }

/* ---------- V2.6 : Vue Carte (Leaflet + OpenStreetMap) ---------- */
.carte-outils { align-items: center; gap: 10px; flex-wrap: wrap; }
.carte-statut { font-size: 12.5px; color: var(--ink-2); }
.carte-conteneur {
  height: 64vh; min-height: 380px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r);
  overflow: hidden;
  z-index: 0; /* garde la carte sous les popovers / la barre latérale de l'app */
}
.carte-echecs { margin-top: 8px; }

/* Épingle personnalisée (divIcon) — pas d'image marqueur à charger */
.cc-pin-wrap { background: none; border: none; }
.cc-pin {
  display: block; position: relative; width: 22px; height: 22px;
  margin: 0 auto;
  background: var(--accent);
  border: 2px solid #fff;
  border-radius: 50% 50% 50% 0;
  transform: rotate(-45deg);
  box-shadow: 0 1px 4px rgba(0, 0, 0, .35);
}
.cc-pin::after {
  content: ''; position: absolute;
  top: 7px; left: 7px; width: 8px; height: 8px;
  background: #fff; border-radius: 50%;
}
.cc-pin-multi { background: var(--accent-fonce); }
.cc-pin-multi::after {
  content: attr(data-n);
  top: 0; left: 0; width: 22px; height: 22px;
  background: none; color: #fff;
  transform: rotate(45deg);
  display: flex; align-items: center; justify-content: center;
  font: 700 11px/1 var(--sans);
}

/* ============================================================
   V9.28 (RELI-54) — L'ÉPINGLE PROVISOIRE, ET LES ÉPINGLES À PLUSIEURS COULEURS.

   ⚠ L'ANGLE DU DÉGRADÉ EST CALCULÉ DANS LE REPÈRE DE L'ÉPINGLE, PAS DE L'ÉCRAN.
     `.cc-pin` porte `transform: rotate(-45deg)` : un dégradé défini à θ dans
     l'élément apparaît à θ − 45° à l'écran. Le `135deg` posé par js/carte.js
     donne donc une coupe VERTICALE vue par l'utilisateur — deux couleurs, deux
     moitiés gauche/droite. Le « corriger » en 90deg, par réflexe, ferait une
     coupe de travers : c'est écrit ici pour que la question ne se repose pas.

   ⚠ ET LE PROVISOIRE DOIT SE VOIR PROVISOIRE. Une épingle de recherche qui
     ressemblerait aux autres ferait croire que la fiche est déjà créée — on
     partirait, et il n'y aurait rien. D'où le contour tireté et l'absence de
     remplissage : elle a l'air d'un emplacement, pas d'un contenu.
   ============================================================ */
.cc-pin-provisoire {
  background: var(--paper); border-style: dashed; border-color: var(--accent);
  box-shadow: 0 1px 6px rgba(0, 0, 0, .3);
}
.cc-pin-provisoire::after { background: var(--accent); }
/* V9.29 — la liste des endroits trouvés : des lignes longues, tronquées à
   droite, le nom complet au survol. Une largeur maximale, sans quoi un
   « display_name » de Nominatim (rue, ville, région, code, pays) étirerait le
   popover au-delà de l'écran. */
.carte-candidats { max-width: 340px; }
.carte-candidats .bloc-menu-item {
  display: flex; align-items: baseline; gap: 8px; width: 100%; text-align: left;
  font-size: 12.5px;
}
/* Le nom prend la place, l'étiquette garde la sienne : c'est le nom qu'on
   tronque, jamais le « hors zone » — sans quoi la seule information qui
   distingue deux lignes identiques disparaîtrait la première. */
.carte-cand-nom { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.carte-cand-ou {
  flex: 0 0 auto; font-size: 10.5px; text-transform: uppercase; letter-spacing: .03em;
  color: var(--ink-3); border: 1px solid var(--line); border-radius: 999px; padding: 0 5px;
}
.carte-cand-loin .carte-cand-nom { color: var(--ink-2); }
.carte-recherche {
  min-width: 190px; max-width: 260px; padding: 3px 8px;
  border: 1px solid var(--line); border-radius: 999px; background: var(--card);
  font-size: 13px;
}
/* Les valeurs qui expliquent les bandes de l'épingle, dans son popup. */
.cc-popup-vals { display: flex; flex-wrap: wrap; gap: 4px 8px; margin: 3px 0 0; }
.cc-popup-val { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; }
.cc-popup-puce {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--ink-3); box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .15);
}

/* Popup au thème « carte marine » */
.cc-popup { font-family: var(--sans); }
.cc-popup-tete { font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-3); margin-bottom: 6px; }
.cc-popup-item + .cc-popup-item { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--line-soft); }
.cc-popup-titre { font-weight: 700; color: var(--ink); font-size: 14px; }
.cc-popup-lieu { font-size: 12.5px; color: var(--ink-2); margin: 3px 0; }
.cc-popup-col { color: var(--ink-3); }
.cc-popup-desc { font-size: 12px; color: var(--ink-2); margin: 3px 0 6px; }
.cc-popup-lien { display: inline-block; margin-top: 4px; }
.leaflet-popup-content { margin: 12px 14px; }
.leaflet-popup-content-wrapper { border-radius: var(--r); }

/* ---------- V2.6 : Vue Calendrier ---------- */
.cal-outils { align-items: center; gap: 8px; flex-wrap: wrap; }
.cal-titre { font-weight: 600; color: var(--ink); margin-left: 8px; font-size: 14px; }
.cal-bascule { display: inline-flex; gap: 4px; }
.cal-bascule .btn.actif { background: var(--accent-plein); color: var(--sur-accent); border-color: var(--accent-plein); }
.cal-champ-lbl { font-size: 12.5px; color: var(--ink-2); display: inline-flex; align-items: center; gap: 4px; }
.cal-champ { border: 1px solid var(--line); border-radius: 4px; padding: 2px 6px; font-size: 12.5px; }
.cal-grille { display: grid; grid-template-columns: repeat(7, 1fr); gap: 1px;
  background: var(--line); border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; }
.cal-entete { background: var(--accent-pale); color: var(--ink-2); font-size: 11.5px; font-weight: 600;
  text-align: center; padding: 5px 2px; text-transform: uppercase; letter-spacing: .03em; }
.cal-cell { background: var(--card); min-height: 94px; padding: 4px; display: flex; flex-direction: column; gap: 3px; }
.cal-cell-sem { min-height: 320px; }
.cal-hors { background: var(--sunk); }
.cal-hors .cal-num { color: var(--ink-3); }
.cal-auj { box-shadow: inset 0 0 0 2px var(--accent); }
.cal-num { font-size: 12px; color: var(--ink-2); font-weight: 600; }
.cal-entete-sem { background: none; text-align: left; padding: 2px 0 4px; }
.cal-cell-evts { display: flex; flex-direction: column; gap: 2px; overflow: hidden; }
.cal-evt { display: block; background: var(--accent-plein); color: var(--sur-accent); font-size: 11.5px; border-radius: 4px;
  padding: 1px 6px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-decoration: none; }
.cal-evt:hover { background: var(--accent-fonce); }
.cal-plus { font-size: 11px; color: var(--ink-3); padding-left: 4px; }

/* ---------- V2.6 : Vue Planning / Gantt ---------- */
.plan-outils { align-items: center; gap: 10px; flex-wrap: wrap; }
.plan-champ-lbl { font-size: 12.5px; color: var(--ink-2); display: inline-flex; align-items: center; gap: 4px; }
.plan-champ-lbl select { border: 1px solid var(--line); border-radius: 4px; padding: 2px 6px; font-size: 12.5px; }
.plan-periode { border: 1px solid var(--line); border-radius: 4px; padding: 1px 5px; font-size: 12px; }
.plan-conteneur { --plan-label: 190px; border: 1px solid var(--line); border-radius: var(--r);
  overflow: hidden; background: var(--card); }
.plan-entete { display: flex; align-items: stretch; border-bottom: 1px solid var(--line); background: var(--accent-pale); }
.plan-label { width: var(--plan-label); flex: 0 0 var(--plan-label); box-sizing: border-box;
  padding: 6px 10px; font-size: 12.5px; color: var(--ink); text-decoration: none;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; border-right: 1px solid var(--line); }
a.plan-label:hover { color: var(--accent); }
.plan-label-tete { font-weight: 600; color: var(--ink-2); }
.plan-axe { position: relative; flex: 1; height: 26px; }
.plan-tick-lbl { position: absolute; top: 5px; transform: translateX(-50%); font-size: 10.5px; color: var(--ink-2); white-space: nowrap; }
.plan-corps { position: relative; }
.plan-grille { position: absolute; top: 0; bottom: 0; left: var(--plan-label); right: 0; pointer-events: none; }
.plan-grille-l { position: absolute; top: 0; bottom: 0; width: 1px; background: var(--line-soft); }
.plan-row { display: flex; align-items: center; min-height: 30px; border-bottom: 1px solid var(--line-soft); }
.plan-row:last-child { border-bottom: none; }
.plan-track { position: relative; flex: 1; height: 30px; }
.plan-bar { position: absolute; top: 5px; height: 20px; min-width: 8px; background: var(--accent);
  border-radius: 4px; display: flex; align-items: center; text-decoration: none; box-shadow: var(--ombre); }
.plan-bar:hover { background: var(--accent-fonce); }
.plan-bar-txt { color: #fff; font-size: 11px; padding: 0 6px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ---------- V2.8 : autocomplétion des formules ---------- */
.formule-ac {
  position: absolute; z-index: 3000; max-height: 240px; overflow-y: auto;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r);
  box-shadow: 0 6px 20px rgba(0, 0, 0, .14); font-size: 12.5px; min-width: 220px;
}
.formule-ac-item { display: flex; align-items: baseline; gap: 7px; padding: 4px 9px; cursor: pointer; }
.formule-ac-item.actif, .formule-ac-item:hover { background: var(--accent-pale); }
.formule-ac-nom { font-family: var(--mono); font-weight: 600; color: var(--accent-fonce); white-space: nowrap; }
.formule-ac-sig { color: var(--ink-3); font-family: var(--mono); font-size: 11px; white-space: nowrap; }
.formule-ac-aide { color: var(--ink-2); font-size: 11px; margin-left: auto; text-align: right; padding-left: 10px; }

/* ---------- V2 : Équipe ---------- */
.btn-lien-topbar {
  /* V6.2 (RELI-322) — `background: none` MANQUAIT, et c'etait tres visible.
     Equipe et Admin sont des <a> (transparents par defaut) ; la brique
     Messages est le seul <button> a porter cette classe, et un <button> garde
     le `buttonface` gris clair du navigateur. Resultat : un pave gris avec du
     texte #c4d0d7 dans une topbar bleu nuit — la brique avait l'air desactivee.
     Toutes les autres classes de bouton du fichier posent ce reset
     (.btn-quitter, .btn-burger, .btn-compte, .btn-lien) ; celle-ci l'a rate. */
  background: none; -webkit-appearance: none; appearance: none;
  color: var(--topbar-attenue); font-size: 12.5px; margin-right: 12px;
  border: 1px solid var(--topbar-ligne); border-radius: var(--r);
  /* Pas de `gap` : les libelles sont ecrits ' Equipe', ' Admin', ' Messages',
     espace compris — un gap s'y ajouterait au lieu de le remplacer. */
  padding: 3px 9px; display: inline-flex; align-items: center;
}
.btn-lien-topbar:hover { color: var(--topbar-txt); border-color: var(--topbar-vif); }
.equipe-table td { vertical-align: middle; }
.equipe-table select { max-width: 240px; }
.compte-inactif td { opacity: .5; }
.form-equipe {
  display: grid; grid-template-columns: 110px 1fr; gap: 8px 12px;
  align-items: center; max-width: 560px; margin-bottom: 10px;
}
.form-equipe label { font-size: 12.5px; color: var(--ink-2); }

/* ---------- V2 : lecture seule ---------- */
input:disabled, textarea:disabled, select:disabled {
  background: transparent; color: var(--ink); opacity: 1; cursor: default;
}
.titre-page input:disabled { border-color: transparent; }

/* ============================================================
   V2.1 — retours d'usage : enregistrement auto, colonnes
   redimensionnables/masquables, gestion des options, duplication
   ============================================================ */
/* -- Indicateur d'enregistrement (carteTexteAutosave) -- */
.carte h3 { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.entete-sauve { display: inline-flex; align-items: center; gap: 6px; font-weight: 400; text-transform: none; letter-spacing: normal; }
.statut-sauve {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.04em; color: var(--ink-3);
  display: inline-flex; align-items: center; gap: 4px;
}
.statut-sauve::before { content: "\25CF"; font-size: 8px; }
.statut-sauve.enregistre { color: var(--ok); }
.statut-sauve.enregistrement { color: var(--st-ouvert); }
.statut-sauve.non-enregistre { color: var(--danger); }
.bouton-disquette {
  border: 1px solid var(--line); background: var(--card); border-radius: 4px;
  font-size: 12px; line-height: 1; padding: 3px 6px; cursor: pointer;
}
.bouton-disquette:hover { border-color: var(--accent); background: var(--accent-pale); }

/* -- Colonnes redimensionnables + retour à la ligne / troncature -- */
table.base { table-layout: fixed; }
table.base th { overflow: hidden; padding-right: 14px; }
table.base th .poignee-resize {
  position: absolute; top: 0; right: 0; width: 8px; height: 100%; cursor: col-resize;
  user-select: none; touch-action: none;
}
table.base th .poignee-resize:hover, table.base th .poignee-resize:active { background: var(--accent); opacity: .35; }
/* ⚠ V11.11 — `pre-line` ET NON `normal`, ET C'EST UN CORRECTIF DE RÉGRESSION.
   La V3.1 avait promis « sauts de ligne visibles » (description courte,
   cellules texte) par une règle plus bas dans ce fichier — que CE sélecteur,
   plus spécifique (table.base td.cellule-souple), a écrasée le jour où il est
   né. Depuis, une description courte à trois lignes s'affichait en un seul
   paragraphe dans le tableau : le retour utilisateur dit « c'est pas clair »,
   et il a raison. `pre-line` rend exactement comme `normal` pour un texte
   sans saut de ligne (les espaces se replient pareil) — seuls les \n
   retrouvent leur effet. Le mode « tronqué » (ligne du dessous) garde sa
   ligne unique : c'est son contrat. */
table.base td.cellule-souple { white-space: pre-line; overflow-wrap: anywhere; word-break: break-word; }
table.base td.cellule-tronque { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* V2.4 — boutons d'action du tableau espacés et grossis (anti-misclic) */
table.base td.cellule-actions { white-space: nowrap; text-align: center; }
table.base td.cellule-actions .btn-lien {
  font-size: 15px; padding: 4px 6px; margin: 0 1px; border-radius: 5px; color: var(--accent-fonce);
  vertical-align: middle;
}
table.base td.cellule-actions .btn-lien:hover { background: var(--accent-pale); text-decoration: none; }
table.base th.th-plus .btn { padding: 4px 10px; }

/* ---------- V2.4 : formules ---------- */
table.base td.cellule-formule {
  color: var(--accent-fonce); background: var(--sunk);
  font-variant-numeric: tabular-nums; cursor: default;
}
.formule-erreur { color: var(--danger); cursor: help; }
.valeur-formule { color: var(--accent-fonce); font-variant-numeric: tabular-nums; }

.formules-noeud { margin: 0 0 12px; }
.formules-entete { display: flex; align-items: center; gap: 10px; }
.formules-toggle { font-size: 13px; font-weight: 600; text-decoration: none; }
.formules-toggle:hover { text-decoration: none; opacity: .8; }
.formules-cartes { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 8px; }
.formule-carte {
  background: var(--card); border: 1px solid var(--line); border-radius: 8px;
  padding: 8px 12px; min-width: 130px; max-width: 320px;
}
.formule-titre {
  font-size: 11px; letter-spacing: .05em; text-transform: uppercase;
  color: var(--ink-2); display: flex; align-items: center; gap: 6px;
}
.formule-carte .formule-editer { opacity: 0; transition: opacity .1s; }
.formule-carte:hover .formule-editer { opacity: 1; }
.formule-resultat {
  font-size: 19px; font-weight: 600; color: var(--accent-fonce);
  margin-top: 2px; font-variant-numeric: tabular-nums;
}
.formule-resultat .mini-graphe { font-weight: 400; }
/* V11.12 — carte multi-résultats (as) : une ligne par résultat nommé.
   ⚠ V11.12c (retour de banc) — « le tableau devient presque trop invisible
   par rapport aux autres ». Les valeurs à 12 px faisaient de la carte multi
   une note de bas de page à côté des grands chiffres (19 px) des cartes
   simples. Les valeurs montent au registre intermédiaire (15 px), chaque
   ligne respire (filet discret entre les lignes), et la ligne « important »
   prend EXACTEMENT le registre du grand chiffre d'une carte simple : un
   total y pèse autant qu'il aurait pesé seul dans sa carte. */
.formule-multi { display: grid; gap: 0; min-width: 170px; margin-top: 6px; font-weight: 400; }
.formule-multi-ligne { display: flex; align-items: baseline; gap: 12px; font-size: 12px; padding: 3px 0; }
.formule-multi-ligne + .formule-multi-ligne,
.formule-multi-bloc + .formule-multi-ligne { border-top: 1px solid var(--line-soft); }
.formule-multi-nom {
  color: var(--ink-2); overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; max-width: 60%;
}
.formule-multi-valeur {
  margin-left: auto; font-weight: 600; color: var(--accent-fonce);
  font-size: 15px; line-height: 1.25; font-variant-numeric: tabular-nums;
}
/* V11.12b — as("nom", "important") : la ligne qui compte se voit — nom en
   encre pleine, valeur au registre du grand chiffre des cartes simples ;
   la couleur seule n'aurait pas suffi. */
.formule-multi-important .formule-multi-nom { color: var(--ink); font-weight: 600; }
.formule-multi-important .formule-multi-valeur { font-size: 19px; font-weight: 700; }
/* V11.12b — une répartition dans une carte multi : l'étiquette au-dessus,
   le mini-graphe en pleine largeur en dessous. */
.formule-multi-bloc { display: block; font-size: 12px; font-weight: 400; }
.formule-multi-bloc .formule-multi-nom { display: block; max-width: none; }

/* ---------- V8.0 (RELI-451) : bandeau des scénarios ---------- */
/* Le bandeau se lit comme une SUITE du panneau ∑ : mêmes cartes, même en-tête,
   même repli. Ce sont deux moitiés d'une seule idée — les leviers et ce qu'ils
   font bouger — et un langage visuel distinct les aurait fait passer pour deux
   fonctionnalités sans rapport. D'où la réutilisation de .formules-entete et de
   .formule-titre plutôt qu'une famille de classes parallèle. */
.scenarios-noeud { margin: -4px 0 12px; }
.scenarios-cartes { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 8px; }
.scenario-carte {
  background: var(--card); border: 1px solid var(--line); border-radius: 8px;
  padding: 8px 12px; min-width: 150px; max-width: 320px;
  display: flex; flex-direction: column; gap: 4px;
}
/* Une valeur surchargée localement DOIT se distinguer d'une valeur partagée :
   sans cela on croit avoir changé le tableau pour l'équipe — ou l'inverse, et
   c'est pire. Un liseré, comme .bandeau-partage, plutôt qu'une couleur de fond :
   la carte reste lisible et la différence se voit du coin de l'œil. */
.scenario-carte.surcharge { border-left: 3px solid var(--accent); background: var(--accent-pale); }
.scen-ctrl {
  width: 100%; font: inherit; font-variant-numeric: tabular-nums;
  padding: 3px 6px; border: 1px solid var(--line); border-radius: var(--r);
  background: var(--input-bg); color: var(--ink);
}
.scen-bornes { font-size: 11px; color: var(--ink-3); font-variant-numeric: tabular-nums; }
.scen-orphelin {
  font-size: 10px; text-transform: none; letter-spacing: 0;
  color: var(--ink-3); border: 1px dashed var(--line); border-radius: var(--r);
  padding: 0 4px; font-weight: 400;
}
.scen-a-parametrer { font-size: 12px; color: var(--danger); font-style: italic; }
.scenario-carte .scen-partager { align-self: flex-start; }
.scenario-carte .formule-editer { opacity: 0; transition: opacity .1s; }
.scenario-carte:hover .formule-editer { opacity: 1; }
/* Sur une fiche, le scénario se LIT et ne se règle pas : la mention est
   volontairement discrète — elle explique un nombre, elle n'invite pas à agir. */
.scen-mention { font-size: 11px; color: var(--ink-3); margin-left: 6px; }
/* Éditeur : le libellé et son expression sur une ligne, l'expression prenant la
   place restante — c'est elle qu'on relit. */
.scen-options { margin-top: 6px; display: grid; gap: 4px; }
.scen-option-ligne { display: flex; align-items: center; gap: 4px; font-size: 12px; }
.scen-option-ligne input:last-of-type { flex: 1; min-width: 0; }

.mini-graphe { display: grid; gap: 3px; min-width: 190px; margin-top: 4px; }
.mini-graphe-ligne { display: flex; align-items: center; gap: 6px; font-size: 12px; }
.mini-graphe-label {
  flex: none; width: 84px; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; color: var(--ink-2); text-transform: none; letter-spacing: 0;
}
.mini-graphe-barre-zone { flex: 1; background: var(--line-soft); border-radius: 3px; height: 10px; overflow: hidden; }
.mini-graphe-barre { display: block; height: 100%; background: var(--accent); border-radius: 3px; min-width: 2px; }
.mini-graphe-nb { flex: none; font-family: var(--mono); font-size: 11px; color: var(--ink-2); min-width: 22px; text-align: right; }

/* -- Titre : icône crayon au survol (découvrabilité de l'édition inline) -- */
.icone-edit { opacity: 0; color: var(--ink-3); font-size: 11px; transition: opacity .1s; }
td.cellule-titre:hover .icone-edit { opacity: 1; }

/* -- Popover : aide -- */
.popover .aide-modal { font-size: 12px; color: var(--ink-2); margin: 0 0 10px; }

/* -- Modal centrée (ex. gestion des options d'un champ) -- */
/* V8.57 — le voile et l'ombre portée de la modale restent en dur : un voile est
   du NOIR translucide dans les deux thèmes (il assombrit ce qu'il couvre, il ne
   suit pas la palette), et l'ombre `0 20px 50px` n'a d'équivalent ni dans
   `--ombre` ni dans `--ombre-flot` — on ne crée pas un jeton par ombre. */
.modal-backdrop {
  position: fixed; inset: 0; background: rgba(16, 28, 38, 0.45);
  display: flex; align-items: center; justify-content: center; z-index: 80; padding: 20px;
}
.modal {
  background: var(--card); border-radius: 8px; width: 100%; max-width: 440px;
  max-height: 82vh; display: flex; flex-direction: column;
  box-shadow: 0 20px 50px rgba(16, 28, 38, 0.35);
}
.modal-tete {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 16px; border-bottom: 1px solid var(--line-soft);
}
.modal-tete h3 { margin: 0; font-size: 14px; }
.modal-fermer { font-size: 18px; line-height: 1; color: var(--ink-2); }
.modal-corps { padding: 14px 16px; overflow-y: auto; }

/* ⚠ V8.51 — CES RÈGLES N'AVAIENT JAMAIS EXISTÉ, ET ÇA SE VOYAIT.
   `.popover label` est habillé depuis toujours (ligne 478), `.form-edition >
   label` depuis la V3.4b, `.cc-modal-colonne label` a reçu ses jumelles en
   V8.42 — mais `.modal-corps`, le corps de TOUTES les modales génériques
   (`modalSaisie`, `modalChoix`, le guichet du support…), n'avait AUCUNE règle
   de formulaire. Un <label> y restait donc `display:inline`, collé à son
   champ — « Nom|ex. Restaurants » — et l'<input> gardait la largeur par
   défaut du navigateur, qui n'a aucun rapport avec celle de la modale.
   ⚠ CE N'EST PAS UNE RÉGRESSION : ça n'a jamais été écrit. Le correctif de
     la V8.42 portait sur la modale de COLONNE, qui est un autre écran avec
     ses propres sélecteurs — d'où l'impression que « c'était corrigé ».
   ⚠ ENFANTS DIRECTS (`>`) ET TYPES ÉNUMÉRÉS : le même corps accueille des
     modales très différentes (comparaison de brouillons, historique, listes
     d'options). On habille ce qui est POSÉ LÀ, jamais ce qui descend d'un
     composant qui a déjà ses règles. */
.modal-corps > label:not(.case) {
  display: block; font-size: 12px; font-weight: 600; color: var(--ink-2);
  margin: 12px 0 4px;
}
.modal-corps > label:not(.case):first-child { margin-top: 0; }
.modal-corps > input[type="text"]:not(.copier-zone),
.modal-corps > input[type="email"], .modal-corps > input[type="password"],
.modal-corps > input[type="number"], .modal-corps > input[type="search"],
.modal-corps > input[type="url"], .modal-corps > input[type="date"],
.modal-corps > select,
.modal-corps > textarea:not(.copier-zone) {
  width: 100%; box-sizing: border-box; border: 1px solid var(--line);
  border-radius: 6px; padding: 8px 10px; font-size: 13.5px; font-family: inherit;
}

.aide-modal { font-size: 12px; color: var(--ink-2); margin: 0 0 12px; }
.options-liste { display: flex; flex-direction: column; gap: 6px; max-height: 44vh; overflow-y: auto; }
.option-ligne { display: flex; gap: 6px; align-items: center; }
.option-ligne input[type="text"] { flex: 1; border: 1px solid var(--line); border-radius: 4px; padding: 4px 7px; font-size: 13px; }

/* -- Métadonnées (créé/modifié le & par) -- */
.carte-meta { font-size: 12.5px; color: var(--ink-2); }
.carte-meta .meta-ligne { display: flex; gap: 8px; padding: 3px 0; }
.carte-meta .meta-libelle { font-family: var(--mono); font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--ink-3); width: 70px; flex: none; }

/* ============================================================
   V2.3 — Éditeur à blocs (« Détail long », façon Notion)
   ============================================================ */
.carte-blocs { padding-bottom: 6px; }
.blocs-editeur { position: relative; font-size: 13.5px; line-height: 1.55; }
.blocs-zone { min-height: 120px; }
.blocs-pied { height: 48px; cursor: text; }

/* V9.5.5 — UN ÉDITEUR IMBRIQUÉ NE RÉSERVE PAS DE PLACE.
   Les deux réglages ci-dessus sont des SURFACES DE CLIC : il faut pouvoir
   viser sous le dernier bloc pour commencer à écrire dans un document vide.
   À la racine, où l'on a toute la page, c'est le bon comportement.
   Dans une citation ou un encadré — qui contiennent chacun un éditeur complet,
   donc leur propre `.blocs-zone` — ils s'additionnaient : 220 px de plancher
   (règle `.detail-long .blocs-zone`, écrite en descendance, qui atteignait
   donc aussi l'éditeur du dedans) plus 48 px de pied. Une citation d'une ligne
   occupait ainsi près de 270 px, filet compris — le « pourquoi la citation est
   aussi grosse ? » du retour de test.
   À l'intérieur, la place vient du contenu : le bloc englobant est déjà là, on
   ne cherche pas où cliquer. On garde 14 px de pied — assez pour viser sous le
   dernier bloc, trop peu pour se voir. */
.blocs-zone-imbrique { min-height: 0; }
.blocs-editeur-imbrique .blocs-pied { height: 14px; }

.bloc-ligne { display: flex; align-items: flex-start; position: relative; border-radius: 4px; }
.bloc-ligne.bloc-cache { display: none; }
.bloc-marge { width: 24px; flex: none; display: flex; justify-content: center; padding-top: 4px; }
.bloc-corps { flex: 1; min-width: 0; padding: 2px 0; }

.bloc-poignee {
  opacity: 0; border: 0; background: none; cursor: grab; color: var(--ink-3);
  font-size: 13px; padding: 1px 3px; border-radius: 4px; line-height: 1;
}
.bloc-ligne:hover .bloc-poignee { opacity: 1; }
.bloc-poignee:hover { background: var(--line-soft); color: var(--ink-2); }
.bloc-en-deplacement { opacity: .4; }
.bloc-drop-avant { box-shadow: 0 -2px 0 var(--accent); }
.bloc-drop-apres { box-shadow: 0 2px 0 var(--accent); }

/* -- texte riche -- */
.bloc-texte { outline: none; min-height: 1.5em; word-break: break-word; }
/* ⚠ V8.61 — L'INVITE NE S'AFFICHE QUE SUR LA LIGNE OÙ L'ON EST.
   Elle s'affichait sur CHAQUE bloc vide : cinq lignes vides, c'était cinq
   fois « Écrivez, ou tapez "/" … » empilés en gris dans la page. Ce n'était
   pas un défaut de rendu — c'est ce que la règle demandait — mais l'effet
   était celui d'un document qui répète une consigne au lieu d'attendre. Et
   le texte perd son sens en se répétant : une invite s'adresse à l'endroit
   où le curseur est, pas à cinq endroits à la fois.

   Deux cas la font apparaître, et deux seulement :
     · `:focus` — la ligne où l'on écrit. C'est le moment où elle sert.
     · `.bloc-invite` — posée par js/blocs.js sur l'unique bloc d'un document
       ENTIÈREMENT vide. Une page blanche sans un mot n'a l'air de rien : on
       ne sait ni si elle est prête, ni où cliquer. C'est le seul endroit où
       l'invite doit parler avant qu'on la sollicite.

   Les blocs vides non focalisés gardent leur hauteur (`min-height` ci-dessus)
   : la page ne saute pas quand le focus se déplace. */
.bloc-texte:empty:focus::before,
.bloc-texte.bloc-invite:empty::before,
.bloc-legende:empty::before {
  content: attr(data-placeholder); color: var(--ink-3); pointer-events: none;
}
.lecture-seule .bloc-texte:empty::before { content: ''; }
.bloc-texte a { color: var(--accent); }
.bloc-titre1 { font-size: 21px; font-weight: 700; margin-top: 10px; }
.bloc-titre2 { font-size: 17px; font-weight: 700; margin-top: 8px; }
.bloc-titre3 { font-size: 14.5px; font-weight: 700; margin-top: 6px; }
.bloc-avec-repli { display: flex; align-items: baseline; gap: 2px; }
.bloc-avec-repli .bloc-texte { flex: 1; min-width: 0; }
/* V2.4 — flèche agrandie (cible de clic confortable dans les documents) */
.bloc-repli {
  border: 0; background: none; cursor: pointer; color: var(--ink-3); flex: none;
  font-size: 15px; padding: 2px 6px; border-radius: 4px;
  transition: transform .12s; opacity: 0;
}
.bloc-repli:hover { background: var(--line-soft); color: var(--ink); }
.bloc-ligne:hover .bloc-repli, .bloc-repli.replie { opacity: 1; }
.bloc-repli.replie { transform: rotate(-90deg); color: var(--accent); }

.bloc-liste-ligne { display: flex; gap: 8px; }
.bloc-puce { flex: none; color: var(--ink-2); min-width: 14px; text-align: right; user-select: none; }
.bloc-liste-ligne .bloc-texte { flex: 1; min-width: 0; }

.bloc-citation { border-left: 3px solid var(--ink-2); padding-left: 12px; font-style: italic; color: var(--ink-2); }
/* V8.31 — LA CITATION EST DEVENUE UN CONTENEUR de blocs (comme l'encadré en
   V5.0e). Le filet et le retrait restent ceux de la citation d'avant ; ce qui
   change est que le corps peut désormais contenir des paragraphes, des listes,
   des titres, une image.
   ⚠ `font-style: italic` est posé sur le CONTENEUR et non sur le texte : les
   blocs enfants en héritent, et l'on peut donc redresser un mot en gras ou un
   bloc de code à l'intérieur sans se battre contre une règle plus spécifique. */
.bloc-citation-conteneur {
  border-left: 3px solid var(--ink-2); padding-left: 12px; margin: 0;
  font-style: italic; color: var(--ink-2);
}
/* Un bloc de code cité n'est pas de la prose : il ne s'italise pas. */
.bloc-citation-conteneur .bloc-code, .bloc-citation-conteneur pre { font-style: normal; }

.bloc-callout { display: flex; gap: 8px; border-radius: var(--r); padding: 10px 12px; align-items: flex-start; }
.bloc-callout .bloc-texte { flex: 1; min-width: 0; }
.bloc-callout-emoji { border: 0; background: none; cursor: pointer; font-size: 16px; line-height: 1.3; padding: 0; }
.bloc-palette-emoji { display: grid; grid-template-columns: repeat(6, 1fr); gap: 2px; }
.bloc-palette-emoji button { font-size: 16px; padding: 3px; }

/* ⚠ V8.57 — LE BLOC DE CODE EST DÉLIBÉRÉMENT SOMBRE, DÉJÀ EN THÈME CLAIR. Ce
   n'est pas une couleur oubliée, c'est la convention de tous les éditeurs, et
   elle reste lisible quand la palette s'inverse. Le fond, l'encre et les deux
   voiles blancs du bouton « copier » — qui se posent SUR ce fond sombre et non
   sur la page — restent donc écrits en dur. */
.bloc-code-enveloppe { position: relative; }
.bloc-code {
  font-family: var(--mono); font-size: 12.5px; background: #1d2b36; color: #dfe8ee;
  border-radius: var(--r); padding: 8px 34px 8px 12px; white-space: pre-wrap; outline: none;
  min-height: 1.5em; word-break: break-word; /* V3.1 : compact, grandit avec le contenu */
}
.bloc-code-copier {
  position: absolute; top: 6px; right: 6px; border: 0; border-radius: 4px;
  background: rgba(255, 255, 255, 0.12); color: #dfe8ee; cursor: pointer;
  font-size: 12px; padding: 3px 6px; opacity: 0; transition: opacity .1s;
}
.bloc-code-enveloppe:hover .bloc-code-copier { opacity: 1; }
.bloc-code-copier:hover { background: rgba(255, 255, 255, 0.24); }

.bloc-separateur { border: 0; border-top: 1px solid var(--line); margin: 10px 0; }

/* -- tableau -- */
.bloc-tableau-enveloppe { position: relative; padding-right: 22px; padding-bottom: 22px; overflow-x: auto; }
/* V4.2 — bloc formule */
.bloc-formule { display: flex; align-items: center; gap: 8px; padding: 6px 10px; background: var(--line-soft); border-radius: 8px; }
.bloc-formule-sig { font-style: italic; font-weight: 700; color: var(--accent); }
.bloc-formule-eq { color: var(--ink-3); }
.bloc-formule-expr { flex: 1; padding: 5px 8px; border: 1px solid var(--line); border-radius: 6px; font-family: ui-monospace, monospace; font-size: 13px; }
.bloc-formule-res { font-weight: 600; }
.bloc-formule-res.err { color: var(--danger); font-weight: 400; font-size: 13px; }

/* V4.2 — bloc tableur (mini-Excel) */
.bloc-tableur { border-collapse: collapse; margin: 2px 0; }
.bloc-tableur th { background: var(--line-soft); border: 1px solid var(--line); padding: 2px 6px; font-size: 12px; color: var(--ink-3); font-weight: 600; text-align: center; min-width: 34px; }
.bloc-tableur th.coin, .bloc-tableur th.num { position: sticky; left: 0; }
.bloc-tableur td { border: 1px solid var(--line); padding: 0; }
.bloc-tableur td input { border: 0; outline: none; padding: 4px 6px; width: 100%; box-sizing: border-box; font-size: 13px; background: transparent; color: inherit; } /* V4.12h — suit la largeur de la colonne */
.bloc-tableur td input.f { color: var(--accent); font-weight: 600; }
.bloc-tableur td.f { color: var(--accent); font-weight: 600; padding: 4px 6px; }
.bloc-tableur td input:focus { box-shadow: inset 0 0 0 2px var(--accent); }
.bloc-tableur-barre { display: flex; gap: 6px; margin-top: 4px; }
/* V8.25 — la barre RÉDUITE du lecteur : seulement 💬 et sa consigne. Discrète,
   parce qu'elle vit sous un bloc qu'on ne peut pas modifier. */
.bloc-tableur-barre-lecture { align-items: center; opacity: .75; }
.bloc-tableur-barre-lecture:hover { opacity: 1; }
.bloc-tableur-barre-lecture .aide-inline { font-size: .82em; color: var(--ink-3); }

/* V5.1a — DÉPLACER une colonne / une ligne : poignée ⠿ dans le repère, révélée
   au survol, et trait d'insertion pendant le glisser. Le repère garde ses autres
   rôles (clic = sélection, bord droit = redimensionnement). */
.bloc-tableur th { position: relative; }
.bloc-tableur th .deplacer {
  position: absolute; left: 1px; top: 50%; transform: translateY(-50%);
  font-size: 9px; line-height: 1; color: var(--ink-3); cursor: grab;
  opacity: 0; transition: opacity .12s ease; padding: 2px 1px;
}
.bloc-tableur th.num .deplacer { left: 50%; top: 1px; transform: translateX(-50%); }
.bloc-tableur th:hover .deplacer { opacity: .75; }
.bloc-tableur th .deplacer:hover { opacity: 1; color: var(--accent); }
.bloc-tableur th .deplacer:active { cursor: grabbing; }
.bloc-tableur th.en-deplacement { opacity: .5; }
.bloc-tableur th.depot-avant { box-shadow: inset 2px 0 0 var(--accent); }
.bloc-tableur th.depot-apres { box-shadow: inset -2px 0 0 var(--accent); }
.bloc-tableur th.num.depot-avant { box-shadow: inset 0 2px 0 var(--accent); }
.bloc-tableur th.num.depot-apres { box-shadow: inset 0 -2px 0 var(--accent); }

/* V5.1 (RELI-449) — barre d'ONGLETS du tableur, posée au-dessus de la grille.
   Le « ＋ » reste une pastille discrète (même parti pris que le bloc colonnes)
   et le chevron ⌄ n'apparaît que sur l'onglet actif. */
/* ⚠ V8.57 — `var(--fond-2, #f7f9f9)` N'ÉTAIT PAS UN JETON. `--fond-2` n'est
   défini nulle part dans le dépôt : la valeur de repli s'appliquait donc
   toujours, et un blanc cassé écrit en dur se faisait passer pour un jeton
   thémable. Remplacé par `--sunk`, dont c'est exactement le rôle (surface
   enfoncée) — même rendu en clair, et il suit enfin le thème. */
.tableur-onglets { display: flex; flex-wrap: wrap; align-items: center; gap: 3px; margin: 0 0 -1px; }
.tableur-onglet {
  border: 1px solid var(--line); background: var(--sunk); color: var(--ink-2);
  border-bottom-color: var(--line); border-radius: 6px 6px 0 0;
  padding: 3px 10px; font-size: 12.5px; max-width: 200px; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; cursor: pointer;
}
.tableur-onglet:hover { border-color: var(--accent); color: var(--ink); }
.tableur-onglet.actif { background: var(--card, #fff); color: var(--ink); font-weight: 600; border-bottom-color: var(--card, #fff); }
.tableur-onglet-nom { border: 1px solid var(--accent); border-radius: 4px; padding: 1px 4px; font-size: 12.5px; width: 130px; }
.tableur-onglet-menu {
  border: none; background: none; color: var(--ink-3); cursor: pointer;
  padding: 0 4px; font-size: 13px; line-height: 1; border-radius: 4px; margin-left: -2px;
}
.tableur-onglet-menu:hover { background: var(--accent-pale); color: var(--accent); }
.tableur-onglet-plus {
  border: none; background: none; color: var(--ink-3); cursor: pointer;
  padding: 1px 6px; font-size: 13px; line-height: 1.2; border-radius: 5px; opacity: .3; transition: opacity .12s ease;
}
.bloc-ligne:hover .tableur-onglet-plus, .tableur-onglet-plus:hover, .tableur-onglet-plus:focus-visible { opacity: 1; color: var(--accent); }
.tableur-onglet-plus:hover { background: var(--accent-pale); }

/* V5.1 (RELI-448) — palette à deux étages : teintes, puis nuances de la teinte. */
.tableur-teintes { display: flex; gap: 5px; flex-wrap: wrap; align-items: center; max-width: 252px; }
.tableur-nuances { display: none; gap: 5px; flex-wrap: wrap; align-items: center; max-width: 252px; margin-top: 6px; padding-top: 6px; border-top: 1px solid var(--line); }
.tableur-nuances.ouvert { display: flex; }
.tableur-nuances .bloc-pastille { width: 22px; height: 22px; }
.tableur-nuances .bloc-pastille.actif { outline: 2px solid var(--accent); outline-offset: 1px; }
.tableur-pipette { width: 26px; height: 22px; padding: 0; border: 1px solid var(--line); border-radius: 5px; background: none; cursor: pointer; }

.bloc-tableau { border-collapse: collapse; width: calc(100% - 2px); }
.bloc-tableau td {
  border: 1px solid var(--line); padding: 5px 8px; min-width: 70px;
  outline: none; vertical-align: top;
}
/* V9.5.4 — `first-of-type` ET NON `first-child`, ET C'EST UN CORRECTIF.
   « Première ligne grisée (en-tête) » ne grisait plus rien, et la cause n'est
   pas dans cette règle : c'est le `<colgroup>` posé par le redimensionnement
   des colonnes qui l'a désamorcée à distance.

   Les lignes sont ajoutées à la table par le DOM (`table.append(tr)`), pas
   analysées depuis du HTML — or le `<tbody>` implicite n'est créé QUE par
   l'analyseur HTML. Les `<tr>` sont donc des enfants directs de `<table>`, et
   le premier enfant de `<table>` est le `<colgroup>`. `tr:first-child` ne
   pouvait plus désigner personne.

   `:first-of-type` compte parmi les `<tr>` seulement : il est juste avec ou
   sans `<colgroup>`, avec ou sans `<tbody>`. On corrige donc le SÉLECTEUR
   plutôt que la structure — insérer un `<tbody>` aurait obligé à relire les
   dix endroits qui parcourent `table.children` (glisser-déposer des lignes,
   redimensionnement, export). */
.bloc-tableau.entete-ligne > tr:first-of-type td,
.bloc-tableau.entete-ligne > tbody > tr:first-of-type td { background: var(--line-soft); font-weight: 600; }
.bloc-tableau.entete-colonne td:first-child { background: var(--line-soft); font-weight: 600; }
.bloc-tab-plus {
  position: absolute; border: 1px dashed var(--line); background: var(--card);
  color: var(--ink-3); cursor: pointer; border-radius: 4px; opacity: 0; font-size: 12px;
}
.bloc-tableau-enveloppe:hover .bloc-tab-plus { opacity: 1; }
.bloc-tab-plus:hover { color: var(--accent); border-color: var(--accent); }
.bloc-tab-plus.col { top: 0; right: 0; bottom: 22px; width: 18px; }
.bloc-tab-plus.ligne { left: 0; right: 22px; bottom: 0; height: 18px; }

/* V5.0m — « ＋ » discret du bloc « Colonnes / cartes » : avant, le bouton
   d'ajout occupait une case entière de la grille (grosse boîte en pointillés
   large comme une colonne). Il vit désormais sous la grille, effacé tant qu'on
   ne survole pas le bloc. */
.bloc-cartes-barre { margin-top: 3px; line-height: 1; }
.bloc-cartes-plus {
  opacity: .22; transition: opacity .12s ease;
  padding: 1px 6px; font-size: 13px; color: var(--ink-3);
}
.bloc-ligne:hover .bloc-cartes-plus,
.bloc-cartes-plus:hover,
.bloc-cartes-plus:focus-visible { opacity: 1; color: var(--accent); }

/* -- médias -- */
.bloc-placeholder-media {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  background: var(--line-soft); border-radius: var(--r); padding: 10px 12px; color: var(--ink-2);
}
.bloc-placeholder-media input { flex: 1; min-width: 160px; border: 1px solid var(--line); border-radius: 4px; padding: 4px 8px; }
.bloc-placeholder-media .ou { font-size: 12px; color: var(--ink-3); }
/* V3.2 — taille par défaut raisonnable (420 px max) + poignée de redimensionnement */
.bloc-image { margin: 4px 0; max-width: 420px; position: relative; }
.bloc-image img { max-width: 100%; width: 100%; border-radius: var(--r); display: block; }
/* V8.57 — l'anneau blanc des poignées de redimensionnement se pose sur une
   IMAGE ou une vidéo, pas sur une surface du thème : il reste `#fff` dans les
   deux thèmes, c'est ce qui détache la poignée d'un fond quelconque. Même
   raison pour le noir de `.bloc-video`, qui est le fond d'une vidéo. */
.bloc-img-poignee {
  position: absolute; right: -4px; bottom: 18px; width: 14px; height: 14px;
  border-radius: 4px; background: var(--accent); border: 2px solid #fff;
  cursor: ew-resize; opacity: 0; transition: opacity .1s;
  box-shadow: 0 1px 4px rgba(0, 0, 0, .3);
}
.bloc-image:hover .bloc-img-poignee { opacity: 1; }
.bloc-legende { font-size: 12px; color: var(--ink-3); margin-top: 4px; outline: none; }
.bloc-video { position: relative; padding-top: 56.25%; border-radius: var(--r); overflow: hidden; background: #000; }
.bloc-video iframe, .bloc-video video { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

/* -- V8.19 : bloc PDF — lecteur intégré, hauteur réglable à la poignée -- */
.bloc-pdf { position: relative; margin: 4px 0; border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; background: var(--card); }
.bloc-pdf iframe { width: 100%; height: calc(100% - 26px); border: 0; display: block; }
.bloc-pdf-lien { display: block; height: 26px; line-height: 26px; padding: 0 10px; font-size: 12px; color: var(--ink-3); text-decoration: none; border-top: 1px solid var(--line); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bloc-pdf-lien:hover { color: var(--accent); }
.bloc-pdf-poignee {
  position: absolute; left: 50%; bottom: -2px; transform: translateX(-50%);
  width: 44px; height: 10px; border-radius: 5px; background: var(--accent); border: 2px solid #fff;
  cursor: ns-resize; opacity: 0; transition: opacity .1s; box-shadow: 0 1px 4px rgba(0, 0, 0, .3);
}
.bloc-pdf:hover .bloc-pdf-poignee { opacity: 1; }

/* -- V8.19 : bloc AVIS + type avis (popover, cellules) -- */
.bloc-avis { position: relative; border: 1px solid var(--line); border-radius: var(--r); padding: 10px 12px; background: var(--card); margin: 4px 0; }
/* V8.23 — le ⚙ des réglages : invisible tant qu'on ne s'approche pas. Un bloc
   de sondage doit ressembler à un sondage. Il reste atteignable au clavier
   (focus-visible) et au doigt (écran tactile : pas de :hover, donc visible). */
.bloc-avis-reglage {
  position: absolute; top: 6px; right: 6px; border: 0; background: transparent; cursor: pointer;
  font-size: 14px; line-height: 1; padding: 3px 5px; border-radius: 5px; color: var(--ink-3);
  opacity: 0; transition: opacity .12s;
}
.bloc-avis:hover .bloc-avis-reglage, .bloc-avis-reglage:focus-visible { opacity: 1; }
.bloc-avis-reglage:hover { background: var(--accent-pale); color: var(--ink); }
@media (hover: none) { .bloc-avis-reglage { opacity: .55; } }
.bloc-avis-question { width: 100%; font-weight: 600; border: 0; border-bottom: 1px dashed var(--line); border-radius: 0; padding: 4px 0; background: transparent; }
.bloc-avis-question-lect { font-weight: 600; margin-bottom: 6px; }
.bloc-avis-config { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin: 6px 0; font-size: 12px; color: var(--ink-3); }
.bloc-avis-note { font-size: 12px; color: var(--ink-3); margin: 6px 0 0; }
.bloc-avis-resultat { font-weight: 600; margin-top: 6px; }
.avis-choix { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 6px; }
.avis-choix-colonne { flex-direction: column; align-items: flex-start; gap: 3px; }
.avis-btn { letter-spacing: 2px; }
.avis-btn-actif { background: var(--accent-plein); color: var(--sur-accent); border-color: var(--accent-plein); }
.avis-repartition { margin-top: 6px; display: grid; gap: 2px; }
.avis-rep-ligne { display: grid; grid-template-columns: minmax(40px, auto) 1fr 26px; gap: 6px; align-items: center; font-size: 12px; }
.avis-rep-label { color: var(--ink-3); white-space: nowrap; }
.avis-rep-zone { background: var(--sunk); border-radius: 4px; height: 8px; overflow: hidden; }
.avis-rep-barre { display: block; height: 100%; background: var(--accent); border-radius: 4px; }
.avis-rep-nb { text-align: right; color: var(--ink-3); }
.cellule-avis .avis-vide { color: var(--ink-3); font-size: 12px; }

/* -- V8.19 : type image (vignettes) + date relative + formule surchargée -- */
.cellule-image { max-height: 42px; max-width: 120px; border-radius: 6px; display: block; object-fit: cover; }
.cellule-image-apercu { max-width: 200px; max-height: 140px; border-radius: var(--r); display: block; margin-bottom: 6px; object-fit: cover; }
.kanban-image { width: 100%; max-height: 140px; object-fit: cover; border-radius: 6px; margin: 4px 0; display: block; }
.date-relative { white-space: nowrap; }
.formule-surcharge { color: var(--accent); font-size: 11px; }
.gln-verdict { font-size: 13px; white-space: nowrap; }

/* ============================================================
   V8.28 (point 3) — LE MARQUEUR GS1 EST UN PSEUDO-ÉLÉMENT.
   ------------------------------------------------------------
   Le drapeau du pays émetteur (et le ⚠ des codes invalides) était un NŒUD DE
   TEXTE dans la cellule : une sélection à la souris suivie de Ctrl+C emportait
   « 🇫🇷 3017620422003 ». C'était le SEUL canal du produit par lequel une
   déduction de pays sortait — les seize autres (export CSV, PDF, DOCX, IA,
   MCP, API, page publiée, bouton « copier la ligne ») ne l'ont jamais fait,
   parce que le serveur ne sait pas ce qu'est un préfixe GS1.

   Le glyphe arrive par la propriété personnalisée `--cc-gln`, posée en JS
   (ccGlnMarqueur, js/api.js). Le contenu d'un `::before` n'entre JAMAIS dans
   le presse-papiers, quel que soit le navigateur — là où `user-select: none`
   sur un <span> ne tient pas partout sur une sélection de tableau.
   `user-select: none` est tout de même posé : ceinture et bretelles, et il
   empêche le double-clic de sélectionner « autour » du marqueur.

   Le marqueur est un INDICE DE SAISIE, pas une donnée : il ne s'imprime pas
   (cf. @media print plus bas), et il est `aria-hidden` — l'infobulle du code
   porte déjà la phrase complète, y compris ce que le préfixe ne dit PAS.
   ============================================================ */
.gln-marqueur {
  /* `inline-block` de largeur nulle : le marqueur ne pousse rien, c'est son
     ::before qui occupe la place. Sans cela, une cellule sans drapeau (plage
     non attribuée) et une cellule avec drapeau ne s'alignent pas. */
  display: inline-block;
  user-select: none; -webkit-user-select: none;
  font-size: 13px; line-height: 1;
}
.gln-marqueur::before {
  content: var(--cc-gln, '');
  margin-right: 4px;
  /* Les emojis drapeaux sont des glyphes en couleur : ils ne suivent pas
     `color`, et sont donc identiques en thème clair et en thème sombre. Le ⚠,
     lui, est un glyphe de texte : il HÉRITE de la couleur de la cellule, comme
     le nœud de texte qu'il remplace — aucune couleur n'est posée ici, pour que
     rien ne change de teinte dans aucun des deux thèmes. */
}

/* -- menu « / » -- */
.bloc-menu-slash {
  position: fixed; z-index: 300; background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r); box-shadow: var(--ombre-flot);
  width: 290px; max-height: 320px; overflow-y: auto; padding: 4px;
}
.bloc-menu-titre { font-size: 10.5px; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-3); padding: 5px 8px 3px; }
/* V8.61 — le pied du menu « / » : les autres sigles. Discret par construction
   (pas de gras, pas d'icône, un filet au-dessus) : c'est un rappel, pas un
   sixième choix — il ne doit jamais se lire comme une ligne cliquable. */
.bloc-menu-pied {
  font-size: 11.5px; color: var(--ink-3); line-height: 1.5;
  padding: 6px 8px 2px; margin-top: 4px; border-top: 1px solid var(--line);
}
.bloc-menu-item {
  display: flex; gap: 10px; align-items: center; width: 100%; text-align: left;
  border: 0; background: none; cursor: pointer; padding: 6px 8px; border-radius: 4px;
}
.bloc-menu-item.sel, .bloc-menu-item:hover { background: var(--accent-pale); }
.bloc-menu-item .ic {
  flex: none; width: 28px; height: 28px; display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--line); border-radius: 4px; background: var(--card); font-size: 12px; font-weight: 600;
}
.bloc-menu-item strong { display: block; font-size: 13px; }
.bloc-menu-item small { color: var(--ink-3); font-size: 11px; }

/* -- V4.7 : sélecteur de type de colonne (même langage visuel que le menu « / ») -- */
.champ-type { position: relative; }
.champ-type-bouton {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; cursor: pointer;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r); padding: 6px 8px;
}
.champ-type-bouton:hover { border-color: var(--accent); }
.champ-type-bouton .ic {
  flex: none; width: 28px; height: 28px; display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--line); border-radius: 4px; background: var(--card); font-size: 12px; font-weight: 600;
}
.champ-type-bouton strong { display: block; font-size: 13px; }
.champ-type-bouton small { display: block; color: var(--ink-3); font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.champ-type-bouton .chevron { margin-left: auto; color: var(--ink-3); }

/* -- barre de mise en forme -- */
/* ⚠ V8.57 — MÊME INVERSION QUE `.topbar` : la barre posait `background:
   var(--ink)`, c'est-à-dire l'ENCRE employée pour ce qu'elle VAUT (« du
   foncé ») et non pour ce qu'elle EST. En thème sombre `--ink` devient clair :
   la barre flottante serait devenue blanche et ses boutons — écrits en clair —
   y auraient disparu. Elle prend donc `--topbar`, le jeton des surfaces qui
   restent foncées, et ses boutons `--topbar-txt`. Le survol reste un voile
   blanc : il se pose sur cette surface foncée, pas sur la page. */
.bloc-barre-fmt {
  position: absolute; z-index: 250; display: none; align-items: center; gap: 2px;
  background: var(--topbar); border-radius: 6px; padding: 3px 4px; box-shadow: 0 6px 18px rgba(24,43,58,.3);
}
.bloc-barre-fmt.visible { display: flex; }
.fmt-btn {
  border: 0; background: none; color: var(--topbar-txt); cursor: pointer; min-width: 26px;
  padding: 3px 6px; border-radius: 4px; font-size: 13px;
}
.fmt-btn:hover { background: rgba(255,255,255,.14); }
.fmt-couleur { text-decoration: underline; text-decoration-color: var(--accent); text-underline-offset: 3px; }
.fmt-palette {
  display: none; position: absolute; top: 100%; left: 0; margin-top: 4px; z-index: 251;
  background: var(--card); border: 1px solid var(--line); border-radius: 6px; padding: 6px; gap: 4px;
}
.fmt-palette.ouverte { display: flex; }
.bloc-pastille {
  width: 20px; height: 20px; border-radius: 50%; border: 1px solid var(--line); cursor: pointer; padding: 0;
}
.bloc-pastille.sel { outline: 2px solid var(--accent); outline-offset: 1px; }
.bloc-palette { display: flex; gap: 6px; margin: 4px 0 8px; }

/* -- menus poignée / lien -- */
.bloc-menu-poignee { min-width: 230px; }
.bloc-menu-transfo { display: flex; flex-wrap: wrap; gap: 2px 8px; margin: 4px 0 8px; max-width: 340px; } /* V5.1 — le tableur y pose 7 boutons : on force le retour à la ligne plutôt qu'un popover démesuré */
.bloc-lien-resultats { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; max-height: 160px; overflow-y: auto; margin-top: 4px; }

/* la carte détail n'a plus de textarea : neutralise l'ancienne règle */
/* V9.5.5 — `:not(.blocs-zone-imbrique)`, ET LE `:not` EST LE CORRECTIF.
   Cette règle est écrite en DESCENDANCE : elle atteignait donc aussi la
   `.blocs-zone` que porte l'éditeur logé DANS une citation ou un encadré, et
   leur imposait le plancher de 220 px prévu pour le document entier.
   L'exclusion est posée ici plutôt qu'ailleurs pour une raison de spécificité :
   `.detail-long .blocs-zone` (0,2,0) l'emporte sur `.blocs-zone-imbrique`
   (0,1,0) quelle que soit la place dans le fichier. Une contre-règle n'aurait
   pas suffi — il faut corriger la règle qui gagne. */
.detail-long .blocs-zone:not(.blocs-zone-imbrique) { min-height: 220px; }

/* ---------- V2.5 : liens, lieux, drag & drop colonnes, formules per() ---------- */
td .cellule-lien { color: var(--accent-fonce); text-decoration: underline; text-underline-offset: 2px; }
td .cellule-lieu-pin { text-decoration: none; font-size: 13px; }
.champ-lien-ctrl { display: inline-flex; gap: 6px; align-items: center; }
.champ-lien-ctrl input { flex: 1; }

/* colonnes du tableau : indicateur de dépôt pendant le glisser-déposer */
table.base th[draggable="true"] { cursor: grab; }
/* V7.0 (RELI-390) — pendant le glisser, la main se FERME, comme sur toutes les
   autres poignées du produit (.kanban-carte, .tache-poignee) : l'en-tête de
   colonne était la seule à garder la main ouverte. */
table.base th[draggable="true"]:active, th .col-grip:active { cursor: grabbing; }
table.base th.col-drop-avant { box-shadow: inset 3px 0 0 var(--accent); }
table.base th.col-drop-apres { box-shadow: inset -3px 0 0 var(--accent); }

/* bloc tableau (éditeur de documents) : poignées lignes / colonnes
   V3.1 — les poignées vivent dans l'enveloppe (superposition), plus dans les
   cellules éditables : le caret peut à nouveau se poser partout. */
.bloc-tab-drag {
  position: absolute; z-index: 6; border: 0; background: var(--card); color: var(--ink-3);
  font-size: 11px; line-height: 1; padding: 2px 3px; border-radius: 4px;
  cursor: grab; opacity: 0; transition: opacity .1s; user-select: none;
  box-shadow: 0 0 0 1px var(--line);
}
.bloc-tab-drag.colonne { transform: rotate(90deg); }
.bloc-tableau-enveloppe:hover .bloc-tab-drag { opacity: 1; }
.bloc-tableau td.tab-drop-haut { box-shadow: inset 0 3px 0 var(--accent); }
.bloc-tableau td.tab-drop-bas { box-shadow: inset 0 -3px 0 var(--accent); }
.bloc-tableau td.tab-drop-gauche { box-shadow: inset 3px 0 0 var(--accent); }
.bloc-tableau td.tab-drop-droite { box-shadow: inset -3px 0 0 var(--accent); }
.bloc-tableau-enveloppe { padding-left: 22px; padding-top: 20px; }

/* ---------- V2.9 : éditeur de cellule flottant (texte long visible) ---------- */
td.cellule-en-edition { position: relative; overflow: visible; }
textarea.editeur-cellule {
  position: absolute; top: 1px; left: 1px; z-index: 25;
  min-height: 32px; max-height: 220px; padding: 6px 8px; box-sizing: border-box;
  font: inherit; font-size: 13px; line-height: 1.45;
  border: 1.5px solid var(--accent); border-radius: 5px;
  background: var(--card); color: var(--ink);
  box-shadow: 0 8px 22px rgba(24, 43, 58, 0.18);
  resize: none; overflow-y: auto; white-space: pre-wrap;
}

/* ---------- V2.9 : formules de nœud — bloc large + vues de répartition ---------- */
.formule-carte.large { flex: 1 1 100%; max-width: none; }
.mini-graphe-reste { font-size: 11px; color: var(--ink-3); margin-top: 2px; font-weight: 400; }
.rep-zone { display: flex; align-items: center; gap: 14px; margin-top: 6px; flex-wrap: wrap; }
.rep-camembert { width: 92px; height: 92px; flex: none; }
.rep-legende { display: grid; gap: 3px; font-size: 12px; font-weight: 400; min-width: 160px; flex: 1; }
.rep-legende-ligne { display: flex; align-items: center; gap: 6px; }
.rep-pastille { width: 9px; height: 9px; border-radius: 3px; flex: none; }
.rep-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 170px; color: var(--ink); }
.rep-nb { margin-left: auto; font-family: var(--mono); font-size: 11px; color: var(--ink-2); white-space: nowrap; }
.rep-barre { display: flex; height: 14px; border-radius: 4px; overflow: hidden; margin: 8px 0 6px; min-width: 190px; }
.rep-barre-seg { display: block; min-width: 2px; }

/* réglages d'affichage dans le popover de formule */
.popover .ligne-affichage { display: flex; align-items: center; gap: 6px; font-size: 12.5px; flex-wrap: wrap; margin-top: 6px; color: var(--ink-2); }
.popover .ligne-affichage select, .popover .ligne-affichage input[type="number"] { width: auto; }

/* ---------- V3 : recherche du tableau + actions de ligne ---------- */
.recherche-tableau {
  border: 1px solid var(--line); border-radius: 6px; padding: 5px 9px;
  font-size: 13px; width: 220px; background: var(--card);
}
.recherche-tableau:focus { border-color: var(--accent); outline: none; }
.nb-resultats { font-size: 12px; color: var(--ink-3); font-family: var(--mono); }
.cellule-actions { white-space: nowrap; }
.cellule-actions .action-suppr:hover { color: var(--danger); }

/* ---------- V3 : kanban enrichi ---------- */
.kanban-col h4.reordonnable { cursor: grab; }
.kanban-col h4.reordonnable:active { cursor: grabbing; }
.kanban-col.kcol-drop-avant { box-shadow: inset 3px 0 0 var(--accent); }
.kanban-col.kcol-drop-apres { box-shadow: inset -3px 0 0 var(--accent); }
.kanban-ajout {
  width: 100%; box-sizing: border-box; border: 1px dashed var(--line);
  border-radius: 5px; padding: 5px 8px; font-size: 12.5px;
  background: transparent; color: var(--ink); margin-top: 6px;
}
.kanban-ajout::placeholder { color: var(--ink-3); }
.kanban-ajout:focus { border-style: solid; border-color: var(--accent); outline: none; background: var(--card); }
.kanban-champ { font-size: 12px; color: var(--ink-2); margin-top: 2px; }
.kanban-champ-nom { color: var(--ink-3); font-size: 11px; }

/* ---------- V3.1 : sauts de ligne visibles (description courte, cellules texte) ---------- */
.desc-courte-cellule, td.cellule-souple, .kanban-carte .d { white-space: pre-line; }

/* ---------- V3.1 : sélecteur d'emojis ---------- */
.emoji-selecteur { min-width: 292px; }
.emoji-selecteur input[type="search"] { width: 100%; margin-bottom: 6px; }
.emoji-groupes { display: flex; gap: 2px; margin-bottom: 6px; flex-wrap: wrap; }
.emoji-groupe-btn {
  border: 1px solid transparent; background: none; font-size: 15px; padding: 2px 5px;
  border-radius: 5px; cursor: pointer;
}
.emoji-groupe-btn:hover { background: var(--line-soft); }
.emoji-groupe-btn.actif { border-color: var(--accent); background: var(--accent-pale); }
.emoji-grille {
  display: grid; grid-template-columns: repeat(8, 1fr); gap: 2px;
  max-height: 240px; overflow-y: auto;
}
.emoji-btn {
  border: 0; background: none; font-size: 19px; padding: 3px; border-radius: 5px;
  cursor: pointer; line-height: 1.2;
}
.emoji-btn:hover { background: var(--accent-pale); }

/* ---------- V3.1 : couleurs (fiche + options de select) ---------- */
.palette-couleurs { display: grid; grid-template-columns: repeat(5, 1fr); gap: 6px; }
.pastille-couleur {
  width: 26px; height: 26px; border-radius: 7px; border: 1px solid var(--line);
  cursor: pointer; padding: 0;
}
.pastille-couleur.sel { outline: 2px solid var(--accent-fonce); outline-offset: 1px; }
.pastille-couleur.mini { width: 16px; height: 16px; border-radius: 5px; flex: none; }
.pastille-couleur.mini.vide { background: repeating-linear-gradient(45deg, var(--card), var(--card) 3px, var(--line-soft) 3px, var(--line-soft) 6px); }
.option-ligne { display: flex; align-items: center; gap: 6px; }
.titre-avec-icone { display: flex; align-items: center; gap: 8px; }
.fiche-icone-btn {
  border: 1px solid transparent; background: none; font-size: 24px; line-height: 1;
  padding: 3px 5px; border-radius: 7px; cursor: pointer; flex: none;
}
.fiche-icone-btn:hover { border-color: var(--line); background: var(--line-soft); }
.fiche-icone-btn.vide { opacity: 0.35; }
.fiche-icone-btn:disabled { cursor: default; }
.fiche-couleur-btn {
  width: 16px; height: 16px; border-radius: 5px; border: 1px solid var(--line);
  background: repeating-linear-gradient(45deg, var(--card), var(--card) 3px, var(--line-soft) 3px, var(--line-soft) 6px);
  cursor: pointer; flex: none; padding: 0;
}
.fiche-couleur-btn:disabled { cursor: default; }
/* V8.57 — la pastille est peinte par l'UTILISATEUR (attribut `style`) : sa
   couleur est une donnée, et les teintes proposées sont claires. L'encre doit
   donc rester sombre dans les DEUX thèmes — `var(--ink)` la rendrait claire en
   sombre, illisible sur une pastille restée pâle. */
td .valeur-multi[style] { color: #182b3a; } /* pill colorée : texte sombre lisible */
.cellule-id { font-family: var(--mono); font-size: 12px; color: var(--ink-2); }

/* ---------- V3.1 : bloc Mermaid ---------- */
.bloc-mermaid { border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; }
.bloc-mermaid-onglets { display: flex; gap: 0; border-bottom: 1px solid var(--line-soft); background: var(--line-soft); }
.bloc-mermaid-onglet {
  border: 0; background: none; padding: 5px 12px; font-size: 12px; cursor: pointer;
  color: var(--ink-2); border-right: 1px solid var(--line-soft);
}
.bloc-mermaid-onglet.actif { background: var(--card); color: var(--ink); font-weight: 600; }
.bloc-mermaid-diag { padding: 12px; overflow-x: auto; background: var(--card); text-align: center; }
.bloc-mermaid-diag svg { max-width: 100%; height: auto; }
.bloc-mermaid-err { color: var(--danger); font-size: 12.5px; text-align: left; }
.bloc-mermaid .bloc-code { border-radius: 0; }
.bloc-mermaid-aide { margin: 0; padding: 5px 10px; font-size: 11.5px; color: var(--ink-3); background: var(--card); }
/* V8.6 — cadre redimensionnable du diagramme. Sans largeur réglée, on garde le
   comportement V3.1 (taille naturelle, débordement en défilement horizontal) ;
   avec une largeur, le SVG s'étire pour la remplir — c'est ce qui permet
   d'AGRANDIR un petit schéma et pas seulement de contenir un grand. */
.bloc-mermaid-cadre { position: relative; }
.bloc-mermaid-cadre.dimensionne .bloc-mermaid-diag { overflow: visible; }
.bloc-mermaid-poignee { right: 2px; bottom: 6px; }
.bloc-mermaid-cadre:hover .bloc-mermaid-poignee { opacity: 1; }

/* ---------- V3.1 : code inline dans les textes ---------- */
.blocs-editeur code, .bloc-texte code {
  font-family: var(--mono); font-size: 0.88em; background: var(--sunk); color: var(--danger);
  padding: 1px 5px; border-radius: 4px;
}

/* ---------- V3.2 : affordance colonnes du tableau + pagination ---------- */
th .col-grip {
  opacity: 0; cursor: grab; color: var(--ink-3); font-size: 10px;
  margin-right: 5px; transition: opacity .1s; user-select: none;
}
th:hover .col-grip { opacity: 1; }
th .col-menu-btn {
  opacity: 0; border: 0; background: none; cursor: pointer; color: var(--ink-3);
  font-size: 11px; padding: 0 3px; margin-left: 4px; border-radius: 4px; transition: opacity .1s;
}
th:hover .col-menu-btn { opacity: 1; }
th .col-menu-btn:hover { background: var(--line-soft); color: var(--ink); }
.sel-pagination { border: 1px solid var(--line); border-radius: 5px; padding: 4px 6px; font-size: 12px; background: var(--card); }
.pagination { display: inline-flex; align-items: center; gap: 6px; }
.pagination-info { font-size: 12px; font-family: var(--mono); color: var(--ink-2); }

/* menu « / » : liste plus haute et défilable sans se fermer */
/* V8.2 — …mais jamais plus haute que la fenêtre : sur un portable en 720 px de
   haut, un menu ouvert au deux tiers de la page débordait sous le bord. */
.bloc-menu-slash { max-height: min(420px, calc(100vh - 24px)); }
/* Variante large : la palette de modèles montre une liste ET un aperçu. */
.bloc-menu-large { width: auto; max-width: min(620px, calc(100vw - 24px)); }

/* ---------- V3.2 : notes — Tâche (échéance/assigné), Fact (source), Idée ---------- */
/* V8.57 — le bleu de « fait » reste en dur : aucun jeton bleu n'existe, et le
   passer à l'accent le rendrait indistinguable de `.badge-type.decision`, déjà
   en pâle sarcelle. Or ces badges sont un CODE de catégorie : ils doivent
   rester distincts entre eux avant d'être thémables. Le couple fond clair /
   encre foncée se suffit et reste lisible en sombre. */
.badge-type.fact { background: #dcebf5; color: #2d5f86; }
.badge-type.idee { background: var(--warn-pale); color: var(--warn); }
.note-extras { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 3px; font-size: 12px; color: var(--ink-2); }
.note-echeance.retard { color: var(--danger); font-weight: 600; }
.note-assigne { color: var(--ink-2); }
.note-source { color: var(--accent-fonce); text-decoration: none; }
.note-source:hover { text-decoration: underline; }
.form-note-extras { display: inline-flex; gap: 6px; align-items: center; }
.form-note-extras input[type="date"], .form-note-extras select, .form-note-extras input[type="url"] {
  border: 1px solid var(--line); border-radius: 5px; padding: 4px 6px; font-size: 12px;
}

/* ---------- V3.3 : bloc tableau lié ---------- */
.bloc-tablie-barre { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
.bloc-tablie-titre { font-size: 12.5px; font-weight: 600; color: var(--accent-fonce); text-decoration: none; }
.bloc-tablie-titre:hover { text-decoration: underline; }
.bloc-tablie-info { font-size: 11.5px; color: var(--ink-3); }
.bloc-tablie-env { padding-left: 0; padding-top: 0; }
.bloc-tableau.tablie td { font-size: 12.5px; }

/* ---------- V3.3b : indicateur de section repliée ---------- */
.bloc-replie-info {
  margin-left: 30px; padding: 2px 8px; font-size: 11.5px; color: var(--ink-3);
  cursor: pointer; border-radius: 4px; user-select: none;
}
.bloc-replie-info:hover { background: var(--line-soft); color: var(--ink-2); }

/* ---------- V3.4 : gestion des formulaires ---------- */
.form-item { border: 1px solid var(--line); border-radius: 8px; padding: 10px 12px; margin-bottom: 10px; }
.form-item-tete { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 6px; }
.form-item-lien { display: flex; gap: 6px; margin-bottom: 6px; }
.form-item-lien input { flex: 1; border: 1px solid var(--line); border-radius: 5px; padding: 4px 8px; font-size: 12px; font-family: var(--mono); color: var(--ink-2); }

/* ---------- V3.4b : mise en forme des formulaires de saisie en modale ---------- */
.form-edition > label:not(.case) {
  display: block; font-size: 12px; font-weight: 600; color: var(--ink-2);
  margin: 14px 0 4px; text-transform: uppercase; letter-spacing: 0.04em;
}
.form-edition > label:first-child { margin-top: 0; }
.form-edition > input[type="text"], .form-edition > textarea, .form-edition > select {
  width: 100%; box-sizing: border-box; border: 1px solid var(--line); border-radius: 6px;
  padding: 8px 10px; font-size: 13.5px; font-family: inherit;
}
.form-edition .form-section {
  border: 1px solid var(--line-soft); border-radius: 8px;
  padding: 10px 12px; margin-top: 14px; background: var(--sunk);
}
.form-edition .form-section > label:not(.case) {
  display: block; font-size: 12px; font-weight: 600; color: var(--ink-2);
  margin: 0 0 6px; text-transform: uppercase; letter-spacing: 0.04em;
}
.form-edition .case { display: flex; align-items: flex-start; gap: 8px; font-size: 13.5px; margin: 6px 0; }
.form-edition .case input[type="checkbox"], .form-edition .case input[type="radio"] { margin-top: 2px; flex: none; }
.form-mode-aide { color: var(--ink-3); font-weight: 400; font-size: 12px; }
.form-sous-bloc { margin: 8px 0 2px 24px; }
.form-sous-bloc label:not(.case) { display: block; font-size: 12px; color: var(--ink-2); margin-bottom: 4px; }
.form-sous-bloc textarea,
.form-sous-bloc input[type="text"], .form-sous-bloc input[type="url"], .form-sous-bloc select {
  width: 100%; box-sizing: border-box; border: 1px solid var(--line); border-radius: 6px;
  padding: 7px 9px; font-size: 13px; font-family: inherit; background: var(--input-bg);
}
/* V8.29 — le message de fin est une zone de texte de plein droit dans sa
   section, pas un sous-bloc indenté : il se saisit comme l'introduction. */
.form-edition .form-section > textarea {
  width: 100%; box-sizing: border-box; border: 1px solid var(--line); border-radius: 6px;
  padding: 8px 10px; font-size: 13.5px; font-family: inherit;
}
/* V8.29 — LA LISTE DES CHAMPS PASSE SUR UNE COLONNE. Deux colonnes tenaient
   tant qu'une ligne portait une case et un nom ; avec la question reformulée
   ET la case « obligatoire », 200 px les écrasaient l'une sur l'autre. Une
   ligne = un champ, ses trois réglages alignés, et on lit la colonne
   « obligatoire » d'un seul coup d'œil vertical. */
.form-liste-champs {
  max-height: 240px; overflow-y: auto; display: block;
}
.form-champ-ligne {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 4px 2px; font-size: 13.5px;
}
.form-champ-ligne + .form-champ-ligne { border-top: 1px solid var(--line-soft); }
.form-champ-ligne > .case:first-child { min-width: 150px; flex: 0 0 auto; }
.form-champ-ligne input[type="text"] {
  border: 1px solid var(--line); border-radius: 5px; padding: 5px 8px; font-family: inherit; background: var(--input-bg);
}
.form-champ-ligne input[type="text"]:disabled { background: var(--sunk); color: var(--ink-3); }
.form-req.desactive { opacity: .45; }
.form-edition .actions .btn { padding: 8px 18px; font-size: 14px; }

/* la modale des formulaires est un peu plus large */
.modal { max-width: 560px; }

/* ===== V3.5 — configuration des vues d'un nœud (popover ⚙) ===== */
.cfg-vue-ligne { display: flex; align-items: center; gap: 4px; padding: 2px 0; }
.cfg-vue-ligne .btn-mini { padding: 0 5px; font-size: 12px; line-height: 1.4; }
.cfg-vue-ligne .btn-mini:disabled { opacity: 0.3; cursor: default; }
.cfg-vue-ligne .case { font-size: 13px; }

/* V3.5 — valeurs relationnelles cliquables (navigation vers la fiche liée) */
.valeur-rel { cursor: pointer; text-decoration: underline; text-decoration-color: var(--ink-3); text-underline-offset: 2px; }
.valeur-rel:hover { filter: brightness(0.95); text-decoration-color: currentColor; }
.champ-rel-ctrl { display: flex; align-items: center; flex-wrap: wrap; gap: 4px; }
.rel-liens { display: inline-flex; flex-wrap: wrap; gap: 4px; }

/* V3.5 — réordonner les formules de nœud (‹ › sur les cartes) */
.formule-bouger { display: inline-flex; gap: 0; opacity: 0; transition: opacity 0.12s; }
.formule-carte:hover .formule-bouger { opacity: 1; }
.formule-bouger .btn-mini { padding: 0 3px; }
.formule-bouger .btn-mini:disabled { opacity: 0.3; cursor: default; }

/* V3.5 — smart text : jetons $valeur et @mention dans l'éditeur à blocs */
/* V8.61 — même bande que `.cc-ref` (voir le commentaire de cette règle) : ces
   jetons cohabitent dans la même phrase, deux alignements différents s'y
   verraient. */
.cc-valeur, .cc-mention {
  display: inline-block; padding: 0 5px; border-radius: 4px; vertical-align: middle;
  font-size: 0.92em; line-height: 1.35; white-space: nowrap;
  user-select: all;
}
.cc-valeur { background: var(--accent-pale); color: var(--accent-fonce); border: 1px solid var(--line); }
.cc-mention { background: var(--prive-pale); color: var(--prive); border: 1px solid var(--line); font-weight: 600; }
.bloc-menu-smart .ic { font-weight: 700; }

/* V3.6 — sélecteur d'organisation (topbar) */
.sel-organisation {
  background: rgba(255,255,255,0.08); color: var(--topbar-txt);
  border: 1px solid var(--topbar-ligne); border-radius: 6px;
  padding: 4px 8px; font-size: 12.5px; max-width: 180px;
  cursor: pointer;
}
.sel-organisation option { color: var(--ink); background: var(--card); }

/* V3.7 — mini logo de la marque (emoji ou image) */
.marque-logo { margin-right: 6px; font-size: 1.05em; vertical-align: -2px; }
img.marque-logo { height: 20px; width: auto; border-radius: 4px; }

/* V3.7 — champ mot de passe avec œil */
.champ-mdp { position: relative; display: block; }
.champ-mdp input { width: 100%; box-sizing: border-box; padding-right: 34px; }
.btn-oeil {
  position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
  background: none; border: none; cursor: pointer; font-size: 14px; padding: 4px;
  opacity: 0.65;
}
.btn-oeil:hover { opacity: 1; }
.lien-mdp-oublie { margin-top: 10px; font-size: 12.5px; }
/* ============================================================
   V9.5.9 — LA CONFIRMATION DEVIENT VISIBLE.

   `.info` ne changeait que la COULEUR DU TEXTE : 13 px, vert pâle, en bas de
   la boîte, sous les liens « Rejoindre mon équipe » et « Créer mon
   organisation ». Sur fond blanc, à six lignes du bouton qu'on vient de
   cliquer, c'est indiscernable d'un pied de page. Le retour d'usage est sans
   appel : quatre clics, en croyant que rien ne se passait.

   Une confirmation n'est pas une nuance de texte, c'est un ÉVÉNEMENT. Elle
   prend donc un fond, un liseré à gauche, du contraste et un ✓ — la même
   grammaire que les autres retours du produit. L'ERREUR, elle, ne change pas :
   elle était déjà rouge, donc déjà repérable, et la banaliser en encart la
   rendrait moins urgente.
   ============================================================ */
.erreur-connexion.info {
  color: var(--ok, #2f6b3a);
  background: rgba(61, 122, 70, .10);
  border-left: 3px solid var(--ok, #3d7a46);
  border-radius: 6px;
  padding: 10px 12px;
  margin-top: 14px;
  font-size: 13.5px;
  line-height: 1.45;
  text-align: left;
}
.erreur-connexion.info::before { content: "✓ "; font-weight: 700; }

/* Le bouton « Mot de passe oublié ? » confirme sur place, là où l'œil
   revient. Désactivé 30 s : c'est la vraie garde contre le re-clic. */
.lien-mdp-oublie.fait { color: var(--ok, #3d7a46); font-weight: 600; }
.lien-mdp-oublie:disabled { cursor: default; opacity: .85; }
.btn-compte {
  background: none; border: none; cursor: pointer;
  font-size: 12.5px; color: var(--topbar-attenue); padding: 4px 6px; border-radius: 6px;
}
.btn-compte:hover { background: rgba(255,255,255,0.08); }

/* V3.7 — onglet Rôles (Équipe) */
/* ============================================================
   V9.36 (RELI-64) — UNE CARTE DE RÔLE SE REPLIE
   ------------------------------------------------------------
   ⚠ LA TÊTE EST UN <button>, et un bouton arrive avec tout un décor de
   navigateur : fond `buttonface` gris, bordure, coins arrondis, police
   système, texte centré. Sans ces remises à zéro, la ligne de titre d'un rôle
   ressemblerait à un gros bouton gris posé dans une carte — c'est le défaut
   exact qu'a connu `.btn-lien-topbar` en V8.32, et il tenait au même mot
   manquant (`background`).

   On garde la disposition de `.form-item-tete` (flex, gap, retour à la ligne)
   et on ne défait QUE ce que le navigateur ajoute.
   ============================================================ */
.form-item-tete.role-tete {
  background: none; border: none; border-radius: 0; padding: 0;
  font: inherit; color: inherit; text-align: left; width: 100%;
  cursor: pointer;
}
.form-item-tete.role-tete:hover .role-chevron { color: var(--accent-fonce); }
.role-chevron {
  display: inline-block; width: 12px; color: var(--ink-3, var(--ink-2));
  font-size: 11px; line-height: 1; flex: none;
}
/* Le résumé pousse les deux boutons à droite — c'est lui qui prend la place
   libre, et non une marge automatique sur le premier bouton : avec le retour
   à la ligne d'un écran étroit, `margin-left:auto` renvoyait « Supprimer »
   seul sur sa ligne, aligné à droite, loin du rôle qu'il supprime. */
.role-resume { flex: 1; }
.role-tete-actions { display: inline-flex; gap: 4px; flex: none; }
/* ⚠ `[hidden]` NE SUFFIT PAS SUR UN ÉLÉMENT QUI PORTE UN `display`. La règle
   par défaut du navigateur (`[hidden] { display: none }`) a la spécificité la
   plus faible qui soit : n'importe quelle règle d'auteur posant un `display`
   la bat, et le corps replié resterait visible. Ici `.role-corps` n'en pose
   pas — mais la prochaine personne qui lui donnera un `grid` ne le saura pas. */
.role-corps[hidden] { display: none; }

.role-grille { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 8px; }
.role-grille > div > label { display: block; font-size: 12px; font-weight: 600; color: var(--ink-2); margin-bottom: 4px; }
.role-liste { max-height: 180px; overflow-y: auto; border: 1px solid var(--line-soft); border-radius: 6px; padding: 6px 8px; }
.role-liste .case { margin: 3px 0; font-size: 13px; }
@media (max-width: 700px) { .role-grille { grid-template-columns: 1fr; } }

/* V3.7 — bandeau licence (sous-sites) */
/* V9.5.9 — alerte d'exploitation (écran super admin). N'apparaît que quand
   quelque chose est cassé : un bandeau permanent ne se lit plus. */
.bandeau-alerte {
  background: var(--danger-pale); color: var(--danger);
  border-left: 3px solid var(--danger);
  border-radius: 6px; padding: 10px 12px; margin-bottom: 10px;
  font-size: 13px; line-height: 1.45;
}
.bandeau-alerte ul { margin: 6px 0 0; padding-left: 18px; }

.bandeau-licence { padding: 7px 14px; font-size: 13px; text-align: center; }
.bandeau-licence.grace { background: var(--warn-pale); color: var(--warn); }
.bandeau-licence.suspendu, .bandeau-licence.resilie { background: var(--danger-pale); color: var(--danger); font-weight: 600; }

/* V5.5 — bandeau « mise à jour en cours » (mode lecture seule de l'instance).
   Rayures discrètes : on reconnaît un chantier au premier coup d'œil, sans
   crier à la panne — le site fonctionne, il ne s'écrit simplement plus. */
.bandeau-maintenance {
  padding: 7px 14px; font-size: 13px; text-align: center; font-weight: 600;
  color: var(--warn); background: var(--warn-pale);
  background-image: repeating-linear-gradient(135deg, rgba(122, 92, 18, .07) 0 10px, transparent 10px 20px);
  border-bottom: 1px solid var(--line);
}
.page-connexion .bandeau-maintenance { border-radius: 8px; margin-bottom: 12px; border: 1px solid var(--line); }

/* V9.4 — bandeau « aperçu client ».
   Il ne signale ni une panne ni une alerte : il rappelle que ce qu'on voit est
   VOLONTAIREMENT bridé. D'où une teinte à part, qui ne se confonde ni avec le
   jaune de la maintenance ni avec le rouge d'un impayé — sinon on finirait par
   la lire comme un incident et par chercher un bug qui n'existe pas. Les
   rayures, elles, reprennent le vocabulaire de la maintenance : « état
   temporaire, et voulu ». */
.bandeau-apercu {
  padding: 7px 14px; font-size: 13px; text-align: center; font-weight: 600;
  color: #4a3a7a; background: #ece8f7;
  background-image: repeating-linear-gradient(135deg, rgba(74, 58, 122, .07) 0 10px, transparent 10px 20px);
  border-bottom: 1px solid var(--line);
}
.bandeau-apercu .btn { vertical-align: middle; }
[data-theme="sombre"] .bandeau-apercu { color: #c9bdf0; background: #241d3a; }

/* V5.5 — état des interrupteurs d'instance (panneau super admin) */
.etiquette {
  display: inline-block; padding: 2px 9px; border-radius: 999px;
  font-size: 12px; font-weight: 600; margin-right: 6px;
}
.etiquette-ok { background: var(--ok-pale); color: var(--ok); }
.etiquette-alerte { background: var(--danger-pale); color: var(--danger); }
.carte-interrupteurs { border-left: 3px solid var(--warn); }

/* V7.1 (RELI-430) — BANDEAU MOBILE, REFAIT. L'ancien essai (V3.7) posait
   `flex-wrap` sur un bandeau à HAUTEUR FIXE de 52 px : dès que la deuxième
   rangée naissait, tout ce qu'elle contenait débordait du bandeau et
   s'éparpillait PAR-DESSUS le contenu de la page — les captures d'écran du
   ticket montrent exactement ça. La règle inverse est la bonne : UNE SEULE
   rangée de 52 px, toujours, et ce qui n'y tient pas change de forme au lieu
   de déborder — la recherche devient une surcouche à la demande (bouton 🔍),
   les briques Tâches/Messages gardent icône et bulle mais perdent leur
   libellé, et Équipe / Admin / compte / Quitter déménagent dans le menu ⋮.
   Même seuil (800 px) que le tiroir latéral : un seul « mode étroit ». */
@media (max-width: 800px) {
  .topbar { gap: 8px; padding: 0 10px; }
  .topbar .marque { max-width: 30vw; }
  .sel-organisation { max-width: 26vw; }
  .btn-rech-mobile, .btn-menu-mobile { display: inline-flex; }
  /* V8.57 — LE BOUTON DE THÈME DESCEND DANS LE MENU SUR ÉCRAN ÉTROIT.
     La rangée était pleine : loupe, thème, Tâches, Messages, et le menu du
     compte en dernier — c'est-à-dire le premier à sortir du cadre sur un
     téléphone étroit, ou à passer inaperçu au milieu de ses voisins. Le thème
     se règle une fois de temps en temps ; le compte se cherche. Celui qui se
     cherche reste, celui qui se règle descend d'un cran. Le choix reste
     complet dans le menu (ccLigneThemeMobile, js/app.js). */
  .btn-topbar-nav, .topbar .btn-compte, .topbar .btn-quitter, .topbar .btn-theme { display: none; }

  /* ⚠ ET LE MENU DU COMPTE NE SE FAIT PLUS ÉCRASER. `.marque` pouvait se
     réduire à zéro pendant que le dernier bouton sortait du cadre : sur un
     écran de 320 px, le nom du site disparaissait ET le menu devenait
     inatteignable. Le menu ne rétrécit jamais ; le nom du site, si — un nom
     tronqué reste un nom, un bouton absent n'est plus rien. */
  .topbar .btn-menu-mobile, .topbar .btn-rech-mobile, .topbar .btn-burger { flex: none; }
  .topbar .marque { min-width: 0; }
  .btn-lien-topbar { margin-right: 0; padding: 4px 7px; }
  .btn-lien-topbar .brique-txt { display: none; }
  /* la recherche quitte la rangée : surcouche sous le bandeau, à la demande */
  .topbar .recherche-globale { display: none; }
  .topbar.rech-ouverte .recherche-globale {
    display: block; position: fixed; top: 52px; left: 0; right: 0;
    max-width: none; padding: 8px 10px; background: var(--topbar);
    border-bottom: 1px solid var(--topbar-ligne); z-index: 39;
  }
  .topbar.rech-ouverte .recherche-globale input { padding-left: 32px; }
  .topbar.rech-ouverte .recherche-globale::before { left: 20px; top: 14px; }
}

/* V3.8 — landing / onboarding */
.landing-points { list-style: none; margin: 0 0 16px; padding: 0; }
.landing-points li { font-size: 13.5px; color: var(--ink-2); margin: 6px 0; }
/* ⚠ V11.2 (RELI-68) — L'ILLUSTRATION DE LA VISITE GUIDÉE.
   `aspect-ratio` + `height:auto` : la place est réservée AVANT que l'image
   arrive, sinon le titre et le texte sautent de 280 px au premier affichage —
   c'est-à-dire au moment précis où la personne lit sa première phrase du
   produit. Le fichier fait 1104 × 560, l'affichage 552 de large : le rapport
   est déclaré ici plutôt que déduit, pour que la réservation vaille même si
   l'image ne se charge jamais.
   ⚠ LE FILET N'EST PAS DÉCORATIF : en thème sombre (app2.css), ces images
   restent claires — elles ont un fond blanc cuit dedans. Le filet les pose
   comme une illustration encadrée au lieu d'un trou lumineux dans l'écran.
   Ce n'est PAS une variante sombre, c'est un pansement ; la vraie réponse est
   une seconde série d'images, qui n'est pas faite. */
.onboarding-illus {
  display: block; width: 100%; height: auto; aspect-ratio: 1104 / 560;
  margin: -2px 0 16px; border-radius: 8px; border: 1px solid var(--line);
  background: var(--sunk);
}
.onboarding h3 { margin: 0 0 8px; font-size: 19px; }
.onboarding-points { display: flex; gap: 6px; justify-content: center; margin: 12px 0; }
.onboarding-point { width: 8px; height: 8px; border-radius: 50%; background: var(--line); }
.onboarding-point.actif { background: var(--accent); }

/* V3.8 — impression de l'app : contenu seul (Ctrl+P décent partout) */
@media print {
  .topbar, .sidebar, .bandeau-licence, .onglets, .btn, .btn-lien, .btn-mini, .drop-racine { display: none !important; }
  .contenu { margin: 0 !important; padding: 0 !important; }
  /* ⚠ V11.5 — SANS CETTE LIGNE, CHAQUE TIRAGE COMMENÇAIT PAR UNE BANDE BLANCHE.
     Le décalage de la topbar était porté par `.contenu` (remis à zéro deux
     lignes plus haut, exprès) ; il est porté depuis la V11.5 par `.app-chrome`,
     qui, lui, n'était remis à zéro nulle part. La topbar n'existe pas sur du
     papier : la place qu'on lui réserve à l'écran n'y a rien à réserver.
     On ne masque PAS `.app-chrome` pour autant : les bandeaux « maintenance »
     et « aperçu » qu'il contient s'imprimaient avant et continuent — seul
     `.bandeau-licence` est masqué, et c'est la règle du dessus qui le dit. */
  .app-chrome { margin-top: 0 !important; }
  body { background: #fff !important; }
  /* V8.0 (RELI-451) — LE BANDEAU DES SCÉNARIOS RESTE À L'IMPRESSION, à plat.
     Un tableau imprimé sous une hypothèse doit dire laquelle : sans cela, deux
     tirages du même tableau donnent deux jeux de chiffres et rien ne les
     distingue. On neutralise seulement l'apparence de formulaire — un <select>
     imprime son option choisie, un <input> sa valeur, donc le texte suffit. */
  .scen-ctrl { border: none !important; background: none !important; padding: 0 !important;
               -webkit-appearance: none; appearance: none; font-weight: 600; }
  .scenario-carte.surcharge { background: none !important; }
  /* V8.25 — la barre réduite du lecteur (💬 + sa consigne) n'a rien à faire sur
     un papier : le bouton était déjà masqué par la règle .btn-mini, restait sa
     phrase « Cliquez une cellule pour la commenter » sous chaque tableur. */
  .bloc-tableur-barre-lecture { display: none !important; }
  /* V8.28 (point 3) — le marqueur GS1 est une aide à la SAISIE : sur un
     tirage papier il n'apporte rien et affirme quelque chose de faux (le
     préfixe dit qui a délivré le code, pas d'où vient le produit). Le code
     nu, lui, reste imprimé tel qu'il est enregistré. */
  .gln-marqueur::before { content: none !important; }
  .gln-marqueur { display: none !important; }
}

/* ============================================================
   V11.6 — LA LIGNE DE STATISTIQUES DE L'EXPORT DE CONTEXTE.
   ⚠ `.export-stats` ET `.stat` ÉTAIENT POSÉES PAR js/export.js ET DÉCLARÉES
     NULLE PART. Cinq `<span>` en flux normal, sans marge ni séparateur : la
     ligne se lisait « 1 collections14 fiches0 notes755 mots12 ms assemblage ».
     Le défaut n'a rien de subtil — il tenait seulement à ce que personne
     n'avait regardé cet écran une fois l'aperçu généré. C'est une capture
     d'écran pour la page d'accueil qui l'a montré.
   ============================================================ */
.export-stats { display: flex; flex-wrap: wrap; gap: 4px 16px; margin: 0 0 10px; font-size: 13px; color: var(--ink-2); }
.export-stats .stat b { color: var(--ink); font-weight: 700; }

/* V3.8b — cartes de paramètres empilées (labels au-dessus, champs pleine largeur) */
.form-empile > label:not(.case) { display: block; font-size: 12.5px; font-weight: 600; color: var(--ink-2); margin: 12px 0 4px; }
.form-empile > input[type="text"], .form-empile > textarea {
  width: 100%; max-width: 560px; box-sizing: border-box;
  border: 1px solid var(--line); border-radius: 6px; padding: 8px 10px; font-size: 13.5px; font-family: inherit;
}
.form-empile .case { margin: 8px 0; }

/* V3.8b — onboarding : Passer / Suivant bien séparés */
.onboarding .actions { display: flex; justify-content: space-between; align-items: center; margin-top: 6px; }

/* V3.8b — bouton 🌐 quand la page est déjà publiée */
.onglet-config.publie, .btn.publie { color: var(--accent-fonce); font-weight: 700; }

/* V4.4 — panneau « variables disponibles » des blocs formule et tableur */
.bloc-vars { margin: 6px 0 2px; border: 1px solid var(--line); border-radius: 8px; background: var(--sunk); }
.bloc-vars summary { cursor: pointer; padding: 5px 9px; font-size: 12px; color: var(--ink-2); user-select: none; }
.bloc-vars-liste { display: grid; grid-template-columns: 1fr; gap: 2px; max-height: 180px; overflow: auto; padding: 4px 6px 7px; }
.bloc-vars-liste .btn-lien { text-align: left; white-space: normal; }
.bloc-vars-liste code { font-weight: 600; }
.bloc-vars-type { color: var(--ink-3); margin-left: 6px; font-size: 12px; }

/* V4.5 — formulaire d'informations de facturation (Abonnement) */
/* V9.2 — REPRISE DU STYLE. Les champs héritaient du navigateur : hauteurs
   inégales entre <input> et <select>, bordure fine à peine visible, aucun état
   de focus ni d'erreur. Trois conséquences concrètes : la grille paraissait
   bancale, on ne voyait pas où l'on tapait, et un refus du serveur n'avait
   nulle part où s'afficher. `align-items: start` (et non `end`) parce que les
   champs portent désormais une ligne d'erreur sous eux : aligner par le bas
   décalait toute la rangée dès qu'une erreur apparaissait. */
.fact-grille { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 12px 14px; margin: 10px 0; align-items: start; }
.fact-champ label { display: block; font-size: 12px; font-weight: 600; color: var(--ink-2); margin: 0 0 5px; }
.fact-champ input, .fact-champ select {
  width: 100%; box-sizing: border-box;
  padding: 8px 10px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--input-bg); color: var(--ink); font-size: 14px; line-height: 1.3;
  transition: border-color .12s ease, box-shadow .12s ease;
}
.fact-champ select { height: 37px; }   /* même hauteur qu'un input : sans ça, la rangée « Type / Email » est de guingois */
.fact-champ input::placeholder { color: var(--ink-3); opacity: .75; }
.fact-champ input:hover:not(:disabled), .fact-champ select:hover:not(:disabled) { border-color: var(--ink-3); }
.fact-champ input:focus, .fact-champ select:focus {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-pale);
}
/* L'astérisque n'est PAS décorative : c'est la seule marque qui distingue un
   champ dont l'absence bloquera le paiement. Elle doit donc rester lisible. */
.fact-champ .requis, .requis { color: #a03d2c; font-weight: 700; }
.fact-champ input.en-erreur, .fact-champ select.en-erreur { border-color: #a03d2c; background: #fdf6f4; }
.fact-champ input.en-erreur:focus, .fact-champ select.en-erreur:focus { box-shadow: 0 0 0 3px rgba(160, 61, 44, .18); }
.champ-erreur { margin: 5px 0 0; font-size: 12px; line-height: 1.35; color: #a03d2c; }
.champ-erreur[hidden] { display: none; }
.fact-large { grid-column: span 2; }
.fact-petit { max-width: 160px; }
@media (max-width: 640px) {
  .fact-large { grid-column: span 1; }
  .fact-petit { max-width: none; }   /* sur mobile, un champ bridé à 160px au milieu d'une colonne pleine largeur fait accident */
}
[data-theme="sombre"] .fact-champ input.en-erreur, [data-theme="sombre"] .fact-champ select.en-erreur { background: rgba(160, 61, 44, .12); }
[data-theme="sombre"] .fact-champ .requis, [data-theme="sombre"] .requis,
[data-theme="sombre"] .champ-erreur { color: #e9a091; }

/* V9.2 — GRILLE DES OFFRES : deux états visuels, pas un.
   « Votre formule » (ce qu'on paie déjà) et « Sélectionnée » (ce qu'on est en
   train de souscrire) se ressemblaient — le liseré ne bougeait pas au clic sur
   « Choisir », et le formulaire de paiement s'ouvrant plus bas, souvent hors
   écran, rien ne confirmait le choix. */
.grille-offres { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 12px; }
.carte-offre {
  border: 2px solid var(--line); border-radius: 10px; padding: 12px;
  display: flex; flex-direction: column; gap: 2px;
  transition: border-color .12s ease, box-shadow .12s ease, transform .12s ease;
}
.carte-offre .offre-nom { font-weight: 700; }
.carte-offre .offre-desc { color: var(--ink-3); font-size: 12px; min-height: 30px; }
.carte-offre .offre-prix { font-weight: 600; margin: 6px 0 0; }
.carte-offre .offre-ttc { color: var(--ink-3); font-size: 12px; margin: 0 0 6px; }
.carte-offre .btn, .carte-offre .chip { margin-top: auto; align-self: flex-start; }
.carte-offre.plan-actuel { border-color: var(--accent); }
.carte-offre.plan-choisi {
  border-color: var(--accent); background: var(--accent-pale);
  box-shadow: 0 0 0 3px var(--accent-pale); transform: translateY(-1px);
}
/* Bouton d'une offre qu'on ne peut pas encore souscrire : il reste cliquable
   (le clic explique ce qui manque et emmène au champ), mais il n'a plus l'air
   d'une action prête. */
.btn.btn-attente { opacity: .6; }
.offre-bloquee { color: #a03d2c; }
.offre-bloquee[hidden] { display: none; }
[data-theme="sombre"] .offre-bloquee { color: #e9a091; }

/* ============================================================
   V5.2 (RELI-208) — NŒUDS COMMUNS ENTRE ORGANISATIONS
   Un vocabulaire visuel unique : le même pictogramme dans l'arbre, le même
   liseré dans le bandeau. Turquoise = « chez nous » (nous sommes propriétaires),
   ambre = « chez quelqu'un d'autre » (nous sommes invités) — la nuance qui
   évite de modifier des données hébergées ailleurs sans s'en rendre compte.
   ============================================================ */
/* Pastille générique : le projet utilisait `.chip` sans jamais la définir
   (seule `.chip-filtre` existait). On pose enfin la règle de base — les
   variantes ci-dessous ne font plus que la colorer. */
.chip {
  display: inline-flex; align-items: center; gap: 4px; padding: 1px 8px;
  border: 1px solid var(--line, #d4dcd9); border-radius: 999px;
  font-size: 12.5px; color: var(--ink-2); background: var(--card); white-space: nowrap;
}
.pastille-partage { display: inline-flex; align-items: center; margin-left: 5px; color: var(--warn); flex: 0 0 auto; }
.pastille-partage.proprio { color: var(--accent-fonce); }
.pastille-partage .cc-icone { vertical-align: -2px; }

.bandeau-partage {
  border: 1px solid var(--line); border-left: 4px solid var(--warn); background: var(--warn-pale);
  border-radius: 9px; padding: 10px 14px; margin: 2px 0 12px; font-size: 13.5px;
}
.bandeau-partage.proprio { border-color: var(--line); border-left-color: var(--accent); background: var(--accent-pale); }
.bandeau-partage .partage-titre { display: flex; align-items: center; gap: 4px; font-size: 14px; }
.bandeau-partage .partage-titre .cc-icone { vertical-align: -3px; }
.partage-corps { display: block; }
.partage-orgs { display: flex; flex-wrap: wrap; gap: 6px; margin: 7px 0 0; }
.chip-proprio { background: var(--accent-fonce); color: #fff; border-color: transparent; }
.partage-sous { color: var(--ink-3); font-size: 12.5px; }
.partage-note { color: var(--ink-2); font-size: 12.8px; line-height: 1.55; margin: 8px 0 0; }
/* Listes de la modale de partage et de l'onglet Équipe → Partages */
.partage-liste { border: 1px solid var(--line, #d4dcd9); border-radius: 8px; overflow: hidden; }
.partage-ligne {
  display: flex; align-items: center; gap: 8px; justify-content: space-between;
  padding: 6px 10px; border-top: 1px solid var(--line, #d4dcd9); font-size: 13.5px; flex-wrap: wrap;
}
.partage-liste .partage-ligne:first-child, .carte > .partage-ligne:first-of-type { border-top: none; }
.partage-ligne.inactif { opacity: 0.55; }
.partage-ligne code { background: var(--sunk); border-radius: 4px; padding: 1px 6px; font-weight: 600; letter-spacing: 0.04em; }
.partage-role { font-size: 12.5px; padding: 2px 6px; }
.partage-regles { margin: 8px 0 0; padding-left: 20px; font-size: 13px; line-height: 1.7; color: var(--ink-2); }

/* Avertissements : partage ouvert à des tiers / hébergement chez le propriétaire.
   Volontairement voyants — ce sont des consentements, pas de la décoration. */
.partage-alerte {
  border: 1px solid var(--warn); background: var(--warn-pale); border-radius: 8px;
  padding: 9px 12px; margin: 10px 0 0; font-size: 13px; line-height: 1.55; color: var(--warn);
}
.partage-erreur { color: var(--danger, #a03d2c); font-size: 13px; min-height: 18px; margin-top: 6px; }

/* V5.3 (RELI-208) — présence FÉDÉRÉE : l'organisation d'origine s'affiche à
   côté du prénom. Sur un nœud commun, « Camille » ne suffit plus à identifier
   quelqu'un — deux organisations peuvent avoir la leur. */
.presence-pill .presence-org {
  margin-left: 5px; padding: 0 6px; border-radius: 999px;
  /* V8.57 — le blanc translucide reste : il se pose SUR la pastille de présence,
     une surface colorée qui ne s'éclaircit dans aucun thème. */
  background: rgba(255, 255, 255, 0.16); font-size: 11px; font-weight: 600; white-space: nowrap;
}

/* V5.3 (RELI-208) — nœud FÉDÉRÉ : la ligne d'état de la copie locale, dans le
   bandeau du nœud partagé (adresse de l'hôte, dernière synchronisation). */
.partage-fed {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  margin-top: 7px; font-size: 12.8px; color: var(--ink-2);
}
.partage-fed a { color: var(--accent-fonce); word-break: break-all; }
.partage-fed .btn-lien { margin: 0; }
/* V5.3 — copie locale d'un nœud fédéré non rafraîchie : la pastille vire au rouge. */
.pastille-partage.desync { color: var(--danger, #a03d2c); }

/* V5.4 — premier démarrage d'une instance (créer / importer) et rapport d'import. */
.boite-install { max-width: 460px; }
.boite-install .onglets { margin-bottom: 12px; }
.install-journal { margin-top: 10px; }
.install-tables {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 2px 12px; margin: 8px 0; font-size: 12.5px; color: var(--ink-2);
}
.install-tables > div { display: flex; justify-content: space-between; border-bottom: 1px dotted var(--line, #d4dcd9); }

/* ============================================================
   V5.6 (RELI-307) — ÉLÉMENTS PRIVÉS DANS UNE ORGANISATION
   Le code couleur prolonge celui du partage V5.2 sans le brouiller :
     · turquoise (--accent-fonce) = chez nous / autorisé  → partage propriétaire
     · ambre (#b07b16)            = chez quelqu'un d'autre → partage invité
     · VIOLET (#6b4f9e)           = restreint aux personnes → PRIVÉ
   Une troisième teinte plutôt qu'une nuance des deux premières : « partagé
   avec une autre organisation » et « caché à mes collègues » sont deux
   affirmations opposées, elles ne doivent jamais se confondre au coin de l'œil.
   ============================================================ */

/* V9.21 (TEST-56) — verrou de sortie : même grammaire que le privé, autre teinte.
   ⚠ V9.22.4 — ET UNE SEULE LIGNE. Empilé sous le bandeau du privé (riche, et
   qui doit l'être), il faisait deux cartes pleine largeur pour deux
   restrictions — quatre si la collection est aussi partagée et annuaire. Le
   nouveau venu se fait discret : une ligne, un liseré de couleur, l'action au
   bout. L'explication vit au survol et en tête de la fenêtre « Gérer ». */
.pastille-excl { display:inline-flex; align-items:center; margin-left:5px; color:var(--hors-ia); }
.pastille-excl.herite { color:color-mix(in srgb, var(--hors-ia) 65%, var(--paper)); }
.pastille-excl .cc-icone { vertical-align:-2px; }
.bandeau-excl {
  display:flex; gap:8px; align-items:center; flex-wrap:wrap;
  border:1px solid var(--line); border-left:4px solid var(--hors-ia); background:var(--hors-ia-pale);
  border-radius:8px; padding:6px 12px; margin:0 0 10px; font-size:13px; color:var(--hors-ia);
}
.bandeau-excl .cc-icone { flex:0 0 auto; }
.excl-note { font-size:12px; color:var(--ink-3); min-width:0; }
@media (max-width: 720px) {
  /* Sur téléphone, « Gérer » passe à la ligne plutôt que d'écraser le motif. */
  .bandeau-excl { align-items:flex-start; }
  .bandeau-excl .btn-lien { margin-left:0 !important; }
}
.cc-exclu { color:var(--hors-ia); }
.cc-exclu .cc-icone { vertical-align:-2px; }

/* ============================================================
   V9.24 — L'ÂGE DES BANDEAUX (voir js/bandeaux.js).
   Un bandeau replié devient une pastille de la taille d'un bouton : elle doit
   se voir sans peser. Le trait de gauche des bandeaux est conservé en bordure
   complète, plus fine — c'est ce qui la relie visuellement au bandeau qu'elle
   remplace. ⚠ ET ELLE RESTE CLIQUABLE AU CLAVIER : c'est un <button>, pas une
   pastille décorative, parce que le contenu replié doit rester atteignable
   sans souris.
   ============================================================ */
.bandeau-age { display:contents; }
.bandeau-pastille {
  display:inline-flex; align-items:center; gap:6px;
  margin:0 0 10px; padding:4px 10px;
  border:1px solid var(--line); border-radius:999px;
  /* ⚠ PAS DE JETON INVENTÉ AVEC REPLI : `var(--fond-2, #f7f9f9)` est la faute
     corrigée en V8.57 — un repli qui ne se déclenche jamais ressemble à une
     précaution et fige une couleur hors du thème. On n'écrit que des jetons
     qui existent. */
  background:var(--sunk); color:var(--ink-2);
  font-size:12.5px; line-height:1.4; cursor:pointer;
}
.bandeau-pastille:hover { border-color:var(--ink-3); }
.bandeau-pastille .cc-icone { flex:0 0 auto; opacity:.85; }
.bandeau-replier { margin-left:auto; align-self:flex-start; opacity:.75; }
.bandeau-replier:hover { opacity:1; }
/* Le bandeau du verrou tient sur une ligne : son « Replier » ne doit pas
   pousser « Gérer » hors de vue sur petit écran. */
.bandeau-excl .bandeau-replier { margin-left:6px; }

/* V9.22.5 — LE POINT DE CUMUL : « il y a autre chose à savoir ici ».
   La ligne ne porte plus qu'UNE pastille (voir js/pastilles.js) ; quand
   plusieurs états s'appliquent, un point discret le signale et le survol les
   énumère. Il emprunte la COULEUR de la pastille qu'il complète — currentColor
   — pour rester un détail de celle-ci et non un quatrième pictogramme. Le
   liseré au fond de page l'empêche de se coller au trait de l'icône. */
.pastille-cumul { position:relative; }
.pastille-cumul::after {
  content:''; position:absolute; right:-2px; bottom:-1px;
  width:4px; height:4px; border-radius:50%;
  background:currentColor; box-shadow:0 0 0 1.5px var(--paper);
}
.pastille-prive { display:inline-flex; align-items:center; margin-left:5px; color:var(--prive); }
.pastille-prive.proprio { color:var(--prive); }
/* V8.57 — l'invité était un second violet écrit en dur, qui restait clair sur fond
   clair en thème sombre. Aucun jeton ne dit « le privé, en atténué » et on n'en
   crée pas : on le DÉRIVE vers le fond de page, donc il s'atténue dans le sens du
   thème en vigueur au lieu d'être figé. */
.pastille-prive:not(.proprio) { color:color-mix(in srgb, var(--prive) 70%, var(--paper)); }   /* invité : je ne suis pas chez moi */
.pastille-prive .cc-icone { vertical-align:-2px; }

.bandeau-prive {
  display:flex; gap:14px; align-items:flex-start; flex-wrap:wrap;
  border:1px solid var(--line); border-left:4px solid var(--prive); background:var(--prive-pale);
  border-radius:8px; padding:10px 14px; margin:0 0 14px;
}
.bandeau-prive.proprio { border-color:var(--line); border-left-color:var(--prive); }
.prive-corps { flex:1 1 320px; min-width:0; }
.prive-titre { display:flex; align-items:center; gap:6px; font-weight:600; color:var(--prive); }
.prive-titre .cc-icone { vertical-align:-2px; }
.prive-orgs { display:flex; flex-wrap:wrap; gap:4px; margin-top:6px; }
.prive-note { font-size:12px; color:var(--ink-3); margin-top:5px; }
.prive-actions { display:flex; gap:6px; align-items:center; flex-wrap:wrap; }
.prive-intro { margin-top:0; font-size:13px; color:var(--ink-3); }
.prive-alerte { font-size:12px; color:var(--danger); margin:6px 0 0; }

.prive-liste { display:flex; flex-direction:column; gap:2px; margin:8px 0; }
.prive-ligne {
  display:flex; align-items:center; gap:10px; justify-content:space-between;
  padding:6px 8px; border-radius:6px;
}
.prive-ligne:nth-child(odd) { background:var(--line-soft); }
.prive-ligne-fin { display:flex; align-items:center; gap:6px; }
.prive-ajout { display:flex; gap:6px; align-items:center; flex-wrap:wrap; margin-top:10px; }
.prive-bloc { margin-bottom:18px; }
.prive-bloc h4 { margin:0 0 2px; }

.chip-prive { background:var(--prive-pale); color:var(--prive); border:1px solid var(--line); }
.prive-note-corbeille { border-left:3px solid var(--prive); padding-left:8px; }

/* La case « Garder privé » de la modale de création. Volontairement discrète :
   elle ne doit pas se lire comme un réglage à trancher, mais comme une option
   qu'on ignore neuf fois sur dix. */
.prive-case {
  display:grid; grid-template-columns:auto 1fr; gap:2px 8px; align-items:center;
  margin:10px 0 4px; padding:8px 10px; border:1px solid var(--line); border-radius:8px;
  background:var(--prive-pale); cursor:pointer; font-size:13px;
}
.prive-case input { margin:0; }
.prive-case > span:nth-of-type(1) { font-weight:600; color:var(--prive); display:flex; align-items:center; gap:5px; }
.prive-case-aide { grid-column:2; font-size:12px; line-height:1.35; }

/* L'interrupteur « créer en privé » de la ligne de nouvelle fiche : éteint il
   se fond dans les actions, allumé il est franchement visible — on ne doit
   jamais créer trois brouillons privés sans s'en rendre compte. */
.bascule-prive { color:var(--ink-3); padding:0 5px; white-space:nowrap; }
/* ⚠ V9.18 (TEST-15) — LE TEXTE DU BOUTON ALLUME ETAIT ILLISIBLE EN THEME
   SOMBRE. La regle posait `color:#fff` sur `--prive`, qui vaut #6b4f9e en
   clair (6,5:1 avec le blanc, correct) mais #b39ddb en sombre — soit 2,4:1,
   tres au-dessous du 4,5:1 exige pour du texte normal. Et a 11 px, le seuil
   allege « grand texte » (3:1) ne s'applique pas : 11 px n'est pas du grand
   texte. Le libelle etait donc, litteralement, illisible sur la moitie des
   postes — ce que le ticket appelle « moche et illisible quand selectionne ».

   ⚠ ON NE CHOISIT PAS UNE COULEUR EN DUR : `--prive-encre` est declaree par
   THEME, a cote de `--prive`, pour que les deux ne puissent plus diverger. La
   prochaine personne qui touche a la teinte du prive voit son encre juste a
   cote. Un contraste corrige dans une regle eloignee se redecorrige tout seul
   au lot suivant. */
.bascule-prive.actif { color:var(--prive-encre, #fff); background:var(--prive); border-radius:5px; font-weight:600; }
.bascule-prive.actif .bascule-prive-txt { letter-spacing:.04em; }

/* ⚠ V9.18 (TEST-15) — LA LIGNE DE SAISIE PORTE LA MARQUE, PAS SEULEMENT LE
   PLACEHOLDER. Un texte d'invite disparait des la premiere lettre tapee,
   c'est-a-dire exactement au moment ou l'on decide. Il ne pouvait donc pas
   avertir la personne qui SAISIT — seulement celle qui hesite. La bordure et
   le fond, eux, restent la pendant toute la frappe. */
.saisie-privee > td, tr.saisie-privee > td { background:var(--prive-pale, rgba(107,79,158,.08)); }
tr.saisie-privee > td:first-child { box-shadow: inset 3px 0 0 var(--prive); }
input.saisie-privee { background:var(--prive-pale, rgba(107,79,158,.08)); box-shadow: inset 3px 0 0 var(--prive); }
/* V6.1 (RELI-321) — le bouton porte son nom : une icone de cadenas seule
   passait pour de la decoration, et c'etait le seul moyen de creer une fiche
   privee. Le libelle s'efface sur les ecrans etroits, ou la place manque. */
.bascule-prive-txt { font-size:11px; letter-spacing:.02em; }
.kanban-prive { display:block; margin:2px 0 0 2px; text-align:left; }
/* Pas de `display:none` sous 700px : masquer le libelle ramenerait exactement
   l'icone muette que la V6.1 corrige. On reduit, on ne supprime pas. */
@media (max-width: 700px) { .bascule-prive-txt { font-size:10px; } }

.prive-regles h4 { display:flex; align-items:center; gap:6px; margin:0 0 4px; }

/* ⚠ V8.57 — CE BLOC EST UN SECOND THÈME SOMBRE, ET IL N'ÉCOUTE PAS LE NÔTRE. Il
   suit le réglage du SYSTÈME, alors que le thème du produit se choisit dans
   l'application (`html[data-theme="sombre"]`, app2.css) : sur un poste en sombre, il
   repeint ces quatre règles même quand l'utilisateur a demandé le thème clair. Les
   valeurs restent en dur — les passer en jetons recopierait le thème sombre
   par-dessus lui-même. C'est la règle entière qui est de trop, mais la retirer se
   décide ailleurs que dans un balayage de couleurs. */
/* V8.57 — CE BLOC ÉCOUTAIT LE SYSTÈME, PAS LE RÉGLAGE DU PRODUIT.
   Il datait d'avant le thème (V5.6) et faisait ce qu'on faisait à l'époque :
   un `@media (prefers-color-scheme: dark)`. Depuis que Relieo a son propre
   réglage, cela produit deux défauts opposés et tous deux invisibles à la
   relecture :
     · sur une machine en sombre, il repeignait le privé en violet foncé même
       quand la personne avait explicitement demandé le thème CLAIR ;
     · sur une machine en clair, il ne se déclenchait PAS alors que la personne
       avait demandé le thème sombre — le privé y restait la seule zone claire.
   Il écoute désormais `data-theme`, comme tout le reste. Les valeurs sont
   celles qui étaient là, à ceci près qu'elles passent par les jetons du privé
   quand ceux-ci disent déjà la même chose.
   ⚠ RÈGLE GÉNÉRALE : dans ce dépôt, plus aucun `@media (prefers-color-scheme)`.
     La résolution système ⇄ réglage se fait UNE fois, en JavaScript
     (js/ui.js), et n'a pas à être refaite dans chaque feuille. */
/* ⚠ PAS D'IMBRICATION CSS ICI. Elle serait plus courte à lire, et le reste du
   dépôt n'en emploie nulle part : une feuille qui mélange les deux écritures
   se relit deux fois. Sélecteurs complets, comme partout ailleurs. */
html[data-ui="v2"][data-theme="sombre"] .bandeau-prive { background: var(--prive-pale); border-color: #3d3450; }
html[data-ui="v2"][data-theme="sombre"] .prive-case { background: var(--prive-pale); border-color: #3d3450; }
html[data-ui="v2"][data-theme="sombre"] .prive-titre,
html[data-ui="v2"][data-theme="sombre"] .chip-prive { color: var(--prive); }
html[data-ui="v2"][data-theme="sombre"] .chip-prive { background: #2e2740; border-color: #3d3450; }
html[data-ui="v2"][data-theme="sombre"] .pastille-prive,
html[data-ui="v2"][data-theme="sombre"] .pastille-prive.proprio { color: var(--prive); }

/* ============================================================
   V5.7 (RELI-312) — COMMENTAIRES, MENTIONS ET SUIVIS
   Couleur dédiée : ambre profond. Le turquoise est l'accent du produit, le
   violet appartient au privé (V5.6), l'ambre clair au partage — restait une
   teinte chaude et lisible sur le papier, qui dit « quelqu'un a écrit ici »
   sans crier. Le surlignage reprend le jaune universel du surligneur : ce
   n'est pas l'endroit où être original.
   ============================================================ */
:root {
  --com: #9a6a12;
  --com-pale: #fdf3dc;
  --com-surlignage: #ffe9a8;

  /* ------------------------------------------------------------------
     V8.54 — L'AMBRE DIT « COMMENTAIRE », PAS « MOI ».

     `--com-pale` remplissait DEUX rôles à la fois : la trace d'un commentaire
     (son emploi d'origine, V5.7) et le fond de mes propres messages plus de
     la conversation sélectionnée (V5.8/V6.2). D'où deux défauts qui n'en font
     qu'un.

       1. Un aplat. Sur un fil un peu long, la moitié droite de l'écran devient
          un bloc crème continu : ce n'est plus un accent, c'est un fond. Une
          couleur chaude tient à la CUILLÈRE, pas à la louche.
       2. Le sens s'efface. Si l'ambre est déjà partout où j'écris, elle ne
          signale plus rien quand elle apparaît sur un passage commenté.

     Donc : mes messages et la conversation ouverte prennent un NEUTRE — une
     nuance de l'encre du produit, pas une couleur de plus. L'ambre ne sert
     plus qu'à ce qu'elle a toujours voulu dire : un commentaire, un non-lu.

     ⚠ V8.55 — ET RIEN NE REMPLACE LE CRÈME. La V8.54 avait ajouté, en plus du
       fond neutre, un liseré ambre de 3 px sur chaque bulle et sur la
       conversation ouverte, par réflexe : retirer une couleur semblait exiger
       d'en poser une autre. Ça ne l'exigeait pas. L'alignement, la forme du
       coin et le fond disaient déjà tout ; le liseré n'ajoutait pas une
       information, il ajoutait un ornement — et un ornement qui n'informe pas
       se voit immédiatement pour ce qu'il est. Le jeton `--msg-moi-accent`
       qu'il utilisait a été SUPPRIMÉ plutôt que laissé inutilisé : un jeton
       orphelin finit toujours par être réemployé pour autre chose.

     ⚠ Ce sont des JETONS, pas des valeurs posées dans les règles : le thème
       sombre (app2.css) les redéfinit, ce que `--com-pale` codé en dur
       n'aurait jamais permis — mes messages y étaient d'ailleurs crème vif
       sur fond marine, seule surface claire de l'écran.
     ------------------------------------------------------------------ */
  --msg-moi: #f1f4f5;
  --msg-moi-bord: #dde5e8;
}

/* --- Surlignage d'un passage commenté ---------------------------------
   Posé par la CSS Custom Highlight API, DONC HORS DU DOM : aucune balise
   n'est injectée dans le contenu éditable (sanitiserHtml la supprimerait, et
   contenteditable la réécrirait dans le bloc). */
::highlight(commentaire) { background: var(--com-surlignage); color: inherit; }
/* V8.57 — le surlignage ACTIF était un jaune vif figé : en thème sombre il restait
   éclatant sous un texte devenu clair, donc illisible, là où `--com-surlignage` suit
   déjà le thème. Aucun jeton ne dit « le même, en plus marqué » et on n'en ajoute
   pas : on pousse le surlignage vers l'ambre du commentaire, ce qui l'assombrit en
   clair et l'éclaircit en sombre — dans les deux cas, plus marqué que son voisin. */
::highlight(commentaire-actif) { background: color-mix(in srgb, var(--com-surlignage) 70%, var(--com)); color: inherit; }

/* Repli pour les navigateurs sans CSS Custom Highlight API : le bloc entier
   porte un liseré. Moins précis, jamais destructeur. */
@supports not (selector(::highlight(x))) {
  .bloc-ligne.a-commentaire .bloc-texte { box-shadow: inset 3px 0 0 var(--com-surlignage); }
}
.bloc-ligne.a-commentaire { cursor: pointer; }
tr.tr-commentee > td:first-child { box-shadow: inset 3px 0 0 var(--com); }
tr.tr-commentee { cursor: pointer; }

/* Bloc visé par un lien profond (?b=…) : mis en évidence à l'arrivée. */
.bloc-cible { animation: cc-bloc-cible 2.2s ease-out; border-radius: var(--r); }
@keyframes cc-bloc-cible {
  0%, 60% { background: var(--com-pale); }
  100% { background: transparent; }
}
@media (prefers-reduced-motion: reduce) { .bloc-cible { animation: none; background: var(--com-pale); } }

/* --- Brique « Messages » du bandeau et sa bulle ------------------------ */
.btn-messages { position: relative; }
/* V8.57 — ces deux voiles blancs se posent SUR le bandeau, foncé dans les deux
   thèmes (`--topbar`) : ils restent du blanc translucide, pas une surface. */
.btn-messages.actif { background: rgba(255, 255, 255, .14); border-radius: var(--r); }
.topbar-bulle {
  position: absolute; top: 2px; right: -2px; min-width: 16px; height: 16px;
  padding: 0 4px; border-radius: var(--r); background: var(--danger); color: #fff;
  font-size: 10px; font-weight: 800; line-height: 16px; text-align: center;
}
.topbar-bulle[hidden] { display: none; }

/* --- Barre latérale en mode conversations ------------------------------ */
/* V6.2 (RELI-322) — FIN DU BLANC SUR BLANC. La sidebar est `var(--card)`
   (#ffffff) ; on y posait des cartes `var(--card)` separees par un liseré
   `--line-soft` (#e4e9e7). Nulle part ailleurs l'app ne fait cela : une carte
   est toujours du blanc SUR `--paper`. On donne donc son fond de page au
   panneau, et les lignes redeviennent des cartes. Meme geste pour la bordure :
   `--line`, celle de `.carte`, `.popover` et `.sidebar` — le langage
   « panneau » de l'app — au lieu du `--line-soft` deux crans plus clair. */
.conversations { flex: 1; overflow: auto; padding: 8px; background: var(--paper); }
.conversations[hidden] { display: none; }
.conv-tete { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 6px; }
.conv-ligne {
  display: grid; grid-template-columns: 1fr auto; gap: 2px 8px;
  width: 100%; text-align: left; padding: 8px 10px; margin-bottom: 4px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r); cursor: pointer;
}
.conv-ligne:hover { border-color: var(--com); }
.conv-ligne.non-lu { border-left: 3px solid var(--com); }
.conv-nom { font-weight: 700; }
.conv-sous { grid-column: 1; font-size: 11px; color: var(--ink-3); }
.conv-bulle {
  grid-row: 1 / span 2; align-self: center;
  min-width: 18px; height: 18px; padding: 0 5px; border-radius: var(--r);
  background: var(--com); color: #fff; font-size: 11px; font-weight: 800; line-height: 18px; text-align: center;
}
.conv-separateur { height: 1px; background: var(--line); margin: 10px 0 6px; }
.conv-liste { display: flex; flex-direction: column; gap: 4px; }
.conv-entree {
  display: flex; flex-direction: column; gap: 2px; padding: 8px 10px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r);
  text-decoration: none; color: inherit;
}
.conv-entree:hover { border-color: var(--com); }
.conv-entree.non-lu { border-left: 3px solid var(--com); }
.conv-entree-objet { font-size: 11px; font-weight: 700; color: var(--ink-2); }
.conv-entree-passage { font-size: 11px; color: var(--com); font-style: italic; }
.conv-entree-texte { font-size: 12px; }
.conv-entree time { font-size: 10px; color: var(--ink-3); }

/* --- Fil de discussion (popover) et panneau ---------------------------- */
/* V8.34 — `min-width: 320px` est un PLANCHER DE CONFORT, pas une contrainte de
   fonctionnement : un fil de discussion plus étroit se lit mal, mais un fil
   plus large que son parent DÉBORDE, et un plancher en pixels ne se laisse
   jamais comprimer. Sur un téléphone de 360 px, le parent (modale ou panneau)
   fait moins que ça une fois ses marges déduites. `min(320px, 100%)` garde le
   plancher partout où il tient et l'abandonne là où il ne tient pas —
   c'est exactement ce qu'on voulait dire. */
.com-fil { min-width: min(320px, 100%); max-width: 420px; display: flex; flex-direction: column; gap: 8px; }
.com-passage {
  margin: 0; padding: 6px 10px; border-left: 3px solid var(--com-surlignage);
  background: var(--com-pale); border-radius: 0 var(--r) var(--r) 0;
  font-style: italic; font-size: 12px; color: var(--ink-2);
}
.com-passage.orphelin { border-left-color: var(--ink-3); background: var(--line-soft); }
.com-passage.orphelin em { font-style: normal; color: var(--ink-3); font-size: 11px; }
.com-message { padding: 6px 0; border-top: 1px solid var(--line-soft); }
.com-message:first-of-type { border-top: 0; }
.com-message.supprime { color: var(--ink-3); font-size: 12px; }
.com-tete { display: flex; align-items: baseline; gap: 6px; font-size: 12px; }
.com-tete time { color: var(--ink-3); font-size: 11px; margin-left: auto; }
.com-tete em { color: var(--ink-3); font-size: 10px; font-style: normal; }
.com-externe { color: var(--com); font-size: 11px; font-weight: 700; }
.com-corps { white-space: pre-wrap; font-size: 13px; margin-top: 2px; }
.com-actions { display: flex; gap: 6px; margin-top: 2px; }

.com-saisie { position: relative; }
.com-champ { width: 100%; resize: vertical; font: inherit; font-size: 13px; }
.com-jetons { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; }
.com-jeton {
  display: inline-flex; align-items: center; gap: 3px; padding: 1px 6px;
  border-radius: var(--r); background: var(--line-soft); color: var(--ink-2);
  font-size: 11px; font-weight: 600;
}
/* V6.2 — un seul accent dans cet ecran : l'ambre de la V5.7. Une mention
   ordinaire etait en sarcelle (`--accent-pale`), ce qui faisait cohabiter les
   deux accents de l'app sans que rien ne distingue leurs roles. Elle passe au
   NEUTRE, et l'ambre reste ce qu'il a toujours ete ici : le signal.
   ⚠ NE PAS teindre la base en `--com-pale` : le premier jet l'a fait, et
   `.externe` devenait alors indiscernable d'une mention interne — or c'est
   precisement l'information que la V5.8 s'est engagee a toujours donner. */
.com-jeton.externe { background: var(--com-pale); color: var(--com); border: 1px solid var(--com-surlignage); }
.com-jeton em { font-style: normal; opacity: .75; }
.com-mentions-menu {
  position: absolute; left: 0; right: 0; top: 100%; z-index: 320;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r);
  /* V6.2 — ombre TEINTEE ENCRE, celle de .popover, au lieu d'un noir pur. */
  box-shadow: var(--ombre-flot); max-height: 200px; overflow: auto;
}
.com-mentions-menu button { display: block; width: 100%; text-align: left; padding: 5px 10px; }
.com-mentions-menu em { color: var(--com); font-style: normal; font-size: 11px; }

/* V8.34 — MÊME DÉFAUT QUE .com-fil, EN PIRE, ET MESURÉ : la modale hôte fait
   width:100%/max-width:440px moins 40 px de fond et 32 px de gouttières, soit
   280 px sur un écran de 320 et 350 px sur un écran de 390. Ce contenu en
   réclamait 380 : il sortait de sa propre modale de 100 px, sous le voile,
   hors d'atteinte du doigt — la modale ne défile pas horizontalement. */
.com-modale { display: flex; flex-direction: column; gap: 10px; min-width: min(380px, 100%); }
.com-ia { display: flex; align-items: flex-start; gap: 6px; font-size: 12px; flex-wrap: wrap; }
.com-ia em { flex-basis: 100%; color: var(--ink-3); font-size: 11px; font-style: normal; padding-left: 20px; }
.com-ia-mini { display: inline-flex; align-items: center; gap: 3px; font-size: 11px; color: var(--ink-2); }

.com-panneau-corps { display: flex; flex-direction: column; gap: 6px; }
.com-resume {
  display: flex; flex-direction: column; gap: 2px; width: 100%; text-align: left;
  padding: 8px 10px; background: var(--card);
  border: 1px solid var(--line); border-left: 3px solid var(--com-surlignage);
  border-radius: var(--r); cursor: pointer;
}
.com-resume:hover { border-color: var(--com); }
.com-resume.non-lu { border-left-color: var(--com); }
.com-resume.orphelin { border-left-color: var(--ink-3); }
.com-resume-passage { font-size: 12px; font-style: italic; color: var(--com); }
.com-resume-extrait { font-size: 13px; }
.com-resume-pied { font-size: 11px; color: var(--ink-3); }
.com-resume-pied em { font-style: normal; }
.com-resolu { border-top: 1px solid var(--line); padding: 8px 0; }

/* En lecture seule, la barre flottante se réduit au seul bouton
   « Commenter » : commenter n'est pas modifier. */
.bloc-barre-fmt-lecture { padding: 2px; }

/* ============================================================
   V5.8 (RELI-313) — MESSAGERIE : CONVERSATIONS ENTRE PERSONNES
   Aucune couleur nouvelle : la messagerie reprend l'ambre de la V5.7. Les
   deux fonctionnalités forment une seule brique dans le bandeau, une seule
   pastille, une seule liste — leur donner deux teintes suggérerait deux
   endroits, alors qu'il n'y en a qu'un.
   ============================================================ */

/* V8.54 — la conversation ouverte : fond neutre, liseré ambre à gauche.
   V8.55 — et le liseré ambre s'en va aussi. La V8.54 l'avait gardé en pensant
   qu'il fallait remplacer le fond crème par autre chose. Il ne fallait rien
   remplacer : le FOND suffit à dire quelle conversation est ouverte, et
   `.conv-entree.non-lu` a déjà, lui, un liseré ambre qui veut dire quelque
   chose de précis. Deux traits ambre voisins pour deux sens différents, c'est
   un décor, pas une information — et le décor se reconnaît à ce qu'on peut le
   retirer sans rien perdre. */
.conv-entree.actif { border-color: var(--msg-moi-bord); background: var(--msg-moi); }
.conv-externe { font-style: normal; color: var(--com); font-size: 10px; font-weight: 700; }
.conv-entree-objet .conv-bulle { margin-left: auto; }
.conv-entree-objet { display: flex; align-items: center; gap: 4px; }

/* --- Écran d'une conversation ------------------------------------------ */
.msg-carte { display: flex; flex-direction: column; max-height: calc(100vh - 110px); }
.msg-entete { border-bottom: 1px solid var(--line); padding-bottom: 8px; margin-bottom: 8px; }
.msg-participants { display: flex; flex-wrap: wrap; gap: 4px; align-items: center; margin-top: 6px; }
.msg-jeton {
  display: inline-flex; align-items: center; gap: 3px; padding: 2px 8px;
  border-radius: var(--r); background: var(--line-soft); font-size: 12px;
}
.msg-jeton.externe { background: var(--com-pale); color: var(--com); font-weight: 600; }
/* Un rôle ne ressemble pas à une personne : trait plein, pour qu'on voie du
   premier coup d'œil que la conversation suivra l'équipe. */
.msg-jeton.role { background: var(--card); border: 1px solid var(--com); color: var(--com); font-weight: 700; }
/* Présent VIA un rôle : le contour pointillé dit « pas ajouté nommément ». */
.msg-jeton.via-role { background: transparent; border: 1px dashed var(--line); }
.msg-jeton em { font-style: normal; opacity: .8; }
.msg-jeton button { margin-left: 2px; opacity: .5; }
.msg-jeton:hover button { opacity: 1; }

.msg-fil { flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 6px; padding: 4px 2px; }
.msg-jour {
  align-self: center; font-size: 11px; color: var(--ink-3);
  background: var(--line-soft); padding: 1px 10px; border-radius: var(--r); margin: 6px 0 2px;
}
.msg-bulle {
  max-width: 78%; align-self: flex-start; padding: 6px 10px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r) var(--r) var(--r) 2px;
}
/* V6.2 — « mes » messages passent de la sarcelle a l'ambre. L'en-tete du bloc
   V5.8 annonce « aucune couleur nouvelle, la messagerie reprend l'ambre de la
   V5.7 » : ces deux lignes disaient le contraire, et l'ecran melangeait les
   deux accents de l'app sans que rien ne distingue leurs roles. */
/* V8.54 — voir le bloc de jetons plus haut : le fond passe au neutre.
   V8.55 — ET LE LISERÉ AMBRE DISPARAÎT.

   La V8.54 avait remplacé le fond crème par un fond neutre PLUS un liseré de
   3 px sur le bord droit, comme si retirer une couleur exigeait d'en poser une
   autre. Non : trois choses disaient déjà qui parle — l'alignement à droite,
   le coin bas-droit rentré, et le fond gris contre le blanc de la bulle reçue.
   Un quatrième marqueur n'ajoutait aucune information, il ajoutait un ornement,
   et un ornement décoratif se voit toujours pour ce qu'il est.

   La bulle reprend donc une bordure de 1 px, uniforme, comme celle qu'on
   reçoit. Seuls le fond et la forme distinguent — c'est assez, et c'est même
   la raison pour laquelle toutes les messageries font ainsi. */
.msg-bulle.moi {
  align-self: flex-end; background: var(--msg-moi); border-color: var(--msg-moi-bord);
  border-radius: var(--r) var(--r) 2px var(--r);
}
.msg-bulle.supprime { color: var(--ink-3); font-size: 12px; }
.msg-tete { display: flex; align-items: baseline; gap: 6px; font-size: 12px; }
.msg-tete time { color: var(--ink-3); font-size: 11px; margin-left: auto; }
.msg-tete em { color: var(--ink-3); font-size: 10px; font-style: normal; }
.msg-corps { white-space: pre-wrap; font-size: 13px; margin-top: 2px; }

.msg-composeur { display: flex; gap: 8px; align-items: flex-end; border-top: 1px solid var(--line); padding-top: 8px; margin-top: 8px; }
.msg-composeur .com-saisie { flex: 1; }
.msg-composeur .btn { flex: none; }

@media (max-width: 900px) {
  .msg-carte { max-height: none; }
  .msg-bulle { max-width: 92%; }
}

/* ---------- V6.1 (RELI-321) : pilotage des instances dédiées ---------- */
/* Dépassement de sièges dans le tableau de flotte. Rouge, mais pas alarmiste :
   rien n'est cassé, rien n'est suspendu — c'est une info commerciale, pas une
   panne. D'où une teinte, sans fond ni bordure. */
.alerte-quota { color:var(--danger); font-weight:600; }

/* Mention « ouvert parce que vous êtes super admin ». Volontairement discrète
   et non cliquable : elle s'adresse à une seule personne sur toute l'instance,
   et elle ne doit surtout pas ressembler à une alerte destinée au client. */
.cc-exempt {
  display:inline-block; margin-top:6px; padding:2px 7px; border-radius:999px;
  font-size:11px; line-height:1.6; color:var(--warn); background:var(--warn-pale); border:1px solid var(--warn);
}

/* ============================================================
   V6.3 (RELI-323) : annuaire d'équipe, profil, vignettes
   ============================================================ */

/* ---------- Vignettes ----------
   L'INITIALE EST LE CAS NORMAL, pas un état dégradé : la plupart des équipes
   ne mettront jamais de photo. Elle est donc dessinée avec le même soin —
   fond de couleur stable par personne, texte blanc, cercle net. La photo,
   quand elle existe, se pose PAR-DESSUS en position absolue : pas de saut de
   mise en page pendant le chargement, et si l'image échoue (droit refusé,
   fichier perdu) il suffit de la retirer du DOM pour retrouver l'initiale.
   `flex:none` + `align-self:center` : les en-têtes de message alignent leurs
   enfants sur la ligne de base, ce qui ferait flotter un cercle. */
.cc-avatar {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  flex: none; align-self: center; overflow: hidden;
  width: 24px; height: 24px; border-radius: 50%;
  color: #fff; font-weight: 700; line-height: 1; letter-spacing: .2px;
  user-select: none; vertical-align: middle;
}
.cc-avatar img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

/* ---------- Actions d'un message : le ⋯ ----------
   Deux boutons-texte sous chaque message prenaient, dans un panneau latéral,
   plus de place que les messages. Le ⋯ s'efface au repos et n'apparaît qu'au
   survol — mais il reste TOUJOURS dans le DOM et focalisable : au toucher et
   au clavier, le survol n'existe pas, et un bouton qu'on ne peut pas atteindre
   vaut un bouton absent. */
.com-menu-btn {
  margin-left: 6px; padding: 0 4px; border: 0; background: none; cursor: pointer;
  color: var(--ink-3); font-size: 15px; line-height: 1; border-radius: 4px;
  opacity: 0; transition: opacity .12s;
}
.com-message:hover .com-menu-btn,
.msg-bulle:hover .com-menu-btn,
.com-menu-btn:focus-visible { opacity: 1; }
.com-menu-btn:hover { background: var(--sunk-2); color: var(--ink); }
@media (hover: none) { .com-menu-btn { opacity: .55; } }

/* Contenu d'un message supprimé, révélé par un owner : lisible mais
   visiblement à part — ce n'est pas du fil, c'est de la modération. */
.com-supprime-corps {
  margin-top: 4px; padding: 4px 8px; border-left: 2px solid var(--line);
  color: var(--ink-2); font-style: normal; white-space: pre-wrap;
}

.msg-mention-ajout { color: var(--com, #9a6a12); font-style: normal; }

/* ---------- Bandeau de l'annuaire ---------- */
.bandeau-equipe {
  margin: 0 0 12px; padding: 8px 12px; border-radius: var(--r, 10px);
  background: var(--sunk); border: 1px solid var(--line);
  font-size: 13px; line-height: 1.5;
}
.bandeau-equipe .aide-modal { font-size: 12px; }

/* ---------- Page « Mon profil » ---------- */
.profil-tete { display: flex; align-items: center; gap: 14px; }
.profil-vignette .cc-avatar { box-shadow: 0 1px 4px rgba(0, 0, 0, .18); } /* V8.57 — ombre laissée en dur : ni `--ombre` ni `--ombre-flot` n'a ce flou, et on ne crée pas un jeton par ombre */
.profil-nom strong { font-size: 17px; display: block; }
.profil-nom .extrait { margin: 2px 0 0; }
.profil-identite { padding-bottom: 14px; }
.profil-cases { display: flex; flex-wrap: wrap; gap: 4px 14px; }
/* Un champ verrouillé reste AFFICHÉ, en lecture, avec sa raison : le masquer
   ferait croire à un bug, le griser sans un mot ferait croire à une panne. */
.profil-verrouille label { color: var(--ink-3); }
.profil-verrouille .aide-modal { display: block; margin: 2px 0 0; font-size: 11px; }

/* ============================================================
   V6.4 (RELI-324) : mentions @, références #, et « Mes tâches »
   ============================================================ */

/* ---------- Pastilles de mention et de référence ----------
   Trois couleurs pour trois natures, et c'est délibéré : une personne, une
   fiche et une collection ne s'ouvrent pas sur la même chose. La personne
   reprend le violet des mentions de commentaires (V5.7) — c'est déjà le code
   couleur du « quelqu'un » dans le produit ; la fiche prend la sarcelle des
   jetons $valeur, celle du « contenu » ; la collection s'en distingue par un
   fond plus soutenu, parce qu'elle est un contenant. */
/* ⚠ V8.61 — `vertical-align: middle` N'EST PAS COSMÉTIQUE ICI, IL CORRIGE UN
   DÉCALAGE DE 2,3 px ENTRE DEUX PASTILLES VOISINES.

   `.cc-ref` est un `inline-flex`, et la ligne de base d'une boîte inline-flex
   est celle de son PREMIER élément flex. Une pastille de fiche commence par du
   texte : sa ligne de base est celle du texte, elle s'aligne sur la phrase.
   Une pastille de personne commence par un AVATAR — une boîte sans texte, donc
   sans ligne de base propre : le navigateur prend alors son bord inférieur, et
   la pastille entière descend de la hauteur qui sépare ce bord de la ligne de
   base du texte. D'où « @Marceline » posé 2,3 px plus bas que « #v8.21 » dans
   la même phrase, sans qu'aucune règle ne parle jamais de ces 2,3 px.

   Aligner sur le MILIEU rend le calcul indépendant du contenu : avatar ou pas,
   les deux pastilles occupent la même bande. On l'applique aussi aux jetons
   `$valeur` et `@mention` de l'éditeur, qui vivent dans les mêmes phrases —
   trois familles de pastilles alignées entre elles valent mieux que deux.

   · `line-height: 1.35` (au lieu de 1,55) : à 0,92 em dans une ligne à 1,6, la
     pastille faisait 23,4 px pour une ligne de 24 — elle remplissait
     l'interligne et les pastilles de deux lignes voisines se frôlaient. 20,6 px
     laissent respirer sans rien rogner du texte. */
.cc-ref {
  display: inline-flex; align-items: center; gap: 3px; vertical-align: middle;
  padding: 0 6px; border-radius: 4px; text-decoration: none;
  font-size: 0.92em; line-height: 1.35; white-space: nowrap; font-weight: 600;
}
.cc-ref:hover { text-decoration: none; filter: brightness(0.96); }
/* V8.57 — les trois natures tenaient à trois fonds en dur, dont deux sarcelles que
   rien ne sépare une fois ramenées au même `--accent-pale`. On garde la distinction
   là où elle survit au changement de thème : la collection porte le TRAIT de
   l'accent, la fiche un trait neutre. */
.cc-ref-personne   { background: var(--prive-pale); color: var(--prive); border: 1px solid var(--line); }
/* V9.5.24 — une personne SANS COMPTE porte le « @ » et le style personne : le
   signe suit ce qu'on a écrit, pas la plomberie de résolution (sa cible
   technique est une fiche, ce qui permet le lien et la mention différée).
   Le trait discontinu dit l'attente sans crier : la mention est enregistrée,
   elle sera notifiée le jour où le compte existera. */
.cc-ref-personne.sans-compte { border-style: dashed; opacity: .88; }
.cc-ref-fiche      { background: var(--accent-pale); color: var(--accent-fonce); border: 1px solid var(--line); }
.cc-ref-collection { background: var(--accent-pale); color: var(--accent-fonce); border: 1px solid var(--accent); }
/* Une transclusion (#fiche.attribut) affiche une VALEUR, pas un nom : elle se
   fond dans la phrase, avec un simple soulignement pointillé pour dire qu'elle
   vient d'ailleurs et qu'elle suivra sa source. */
.cc-ref-valeur {
  background: none; border: 0; padding: 0; font-weight: 600; color: var(--ink);
  border-bottom: 1px dotted var(--ink-3); border-radius: 0;
}
.cc-ref .cc-avatar { margin-right: 1px; }

/* ---------- V11.9 : le calque de surlignage ----------
   Le champ reste un `<textarea>` (caret, autocomplétion, correcteur) ; le
   calque peint DERRIÈRE lui le même texte, aux mêmes métriques — recopiées en
   JS depuis le style calculé du champ (js/mentions.js, CC_SURLIGNAGE_PROPS) —
   et ne colore que les jetons.

   ⚠ TROIS INVARIANTS, ET CHACUN CORRIGE UN DÉCALAGE POSSIBLE :
     · le calque est en `inset: 0` sur une boîte qui a EXACTEMENT la boîte du
       champ (pas de padding, pas de bordure) : il suit donc la hauteur que
       `autotaille()` pose, et le redimensionnement à la poignée ;
     · le champ passe en `display: block` — en `inline-block`, sa ligne de base
       laisse quelques pixels sous lui, et la boîte devient plus haute que le
       calque ;
     · le fond du champ passe au calque. Le champ, lui, devient transparent :
       sans quoi il masquerait le surlignage qu'on vient de peindre. La BORDURE
       reste au champ, y compris son liseré d'accent au focus. */
.cc-surlignage-boite { position: relative; display: block; }
.cc-surlignage-boite > textarea,
.cc-surlignage-boite > textarea:focus { display: block; position: relative; z-index: 1; background: transparent; }
.cc-surlignage {
  position: absolute; inset: 0; z-index: 0; overflow: hidden;
  pointer-events: none; user-select: none;
  color: transparent; background: var(--input-bg);
  /* Repli si le calage JS n'a pas encore eu lieu (premier rendu, police en
     cours de chargement) : mieux vaut un calque aux bonnes règles de retour à
     la ligne qu'un calque en `normal`, qui recouperait toutes les lignes. */
  white-space: pre-wrap; overflow-wrap: break-word;
}
/* ⚠ AUCUN `padding`, AUCUNE `border`, AUCUN `margin` SUR LE JETON. Tout ce qui
   prend de la place dans le flux décalerait le texte du calque par rapport à
   celui du champ, d'autant de pixels que de jetons. Le halo se dessine donc au
   `box-shadow`, qui ne prend aucune place — et le fond déborde de 2px sans que
   personne ne bouge. Même vocabulaire de couleurs que les vraies pastilles
   (.cc-ref-personne, .cc-ref-fiche) : ce qu'on écrit ressemble à ce qu'on
   relira. */
.cc-surlignage-jeton { border-radius: 3px; }
.cc-surlignage-personne { background: var(--prive-pale); box-shadow: 0 0 0 2px var(--prive-pale); }
.cc-surlignage-ref { background: var(--accent-pale); box-shadow: 0 0 0 2px var(--accent-pale); }

/* V7.9 (RELI-450) — la pastille d'un BLOC (span[data-ref], par opposition au
   <a> des champs de texte simple) est cliquable, partout et sans modificateur :
   la main du curseur suffit à le dire, et c'est ce que tout lien promet. On
   avait d'abord distingué édition et lecture par un curseur `alias` (« il faut
   Ctrl ») — un raccourci que rien à l'écran ne peut enseigner, donc un lien
   perçu comme cassé. L'indice est en CSS et non en JavaScript : le HTML d'un
   bloc est enregistré tel quel, un attribut posé à chaud partirait en base et
   jusque dans les exports. */
span.cc-ref[data-ref] { cursor: pointer; }

/* Menu d'autocomplétion — même grammaire visuelle que le menu « / » des blocs. */
.cc-mentions-menu {
  position: fixed; z-index: 700; min-width: 240px; max-width: 320px; max-height: 240px;
  overflow: auto; background: var(--card, #fff); border: 1px solid var(--line);
  border-radius: var(--r, 10px); box-shadow: var(--ombre-flot); padding: 4px;
}
/* V9.5.16 — la personne sans compte est proposée pour être COMPRISE, pas
   choisie : atténuée, curseur neutre, aucun survol. Elle reste lisible —
   l'atténuer à l'excès effacerait la seule chose qui compte, son nom. */
/* V9.5.20 — une personne anonymisée n'a pas le même poids qu'un collègue en
   activité : même place, même taille, mais grise et en italique. Le fil de
   commentaires cesse de faire croire à un interlocuteur de plus. */
.cc-anonyme { color: var(--ink-3); font-style: italic; }

.cc-mentions-item.sans-compte { opacity: .72; cursor: default; }
.bloc-menu-item.sans-compte { opacity: .72; cursor: default; }
.bloc-menu-item.sans-compte:hover { background: none; }
.bloc-menu-item.sans-compte small { font-style: italic; color: var(--ink-3); }
.cc-mentions-item.sans-compte:hover { background: none; }
.cc-mentions-item.sans-compte small { font-style: italic; color: var(--ink-3); }

.cc-mentions-item {
  display: flex; align-items: center; gap: 7px; width: 100%; padding: 5px 7px;
  border: 0; background: none; border-radius: 6px; cursor: pointer; text-align: left;
}
.cc-mentions-item:hover, .cc-mentions-item.sel { background: var(--sunk-2); }
.cc-mentions-item .ic { width: 18px; text-align: center; color: var(--ink-3); font-weight: 700; }
.cc-mentions-item small { display: block; color: var(--ink-3); font-size: 11px; }

/* ---------- Champ texte à deux modes ----------
   `pre-wrap` : le contenu reste du texte, retours à la ligne compris — on ne
   le convertit pas en HTML pour l'afficher. Le pointeur en « texte » et le
   liseré au survol disent qu'on peut cliquer pour écrire, sans en faire un
   bouton : ce n'en est pas un. */
.cc-texte-lecture {
  white-space: pre-wrap; word-break: break-word; min-height: 2.4em;
  padding: 6px 8px; border: 1px solid transparent; border-radius: var(--r, 8px);
  line-height: 1.6; box-sizing: border-box;
}
/* V6.5 — la hauteur minimale de la CARTE (contexte, vocabulaire, ontologie,
   description courte) est celle du textarea de `.zone-editable` : passer de la
   lecture à l'écriture ne doit pas faire sauter la carte sous le curseur.
   ⚠ Portée à la carte, et à elle seule : `.cc-texte-lecture` sert AUSSI au
   contenu de chaque note (js/vues.js), une ligne compacte de deux lignes. Une
   règle générale à 84px faisait tenir vingt notes sur cinq écrans. */
.carte-texte > .cc-texte-lecture { min-height: 84px; padding: 8px; }
.cc-texte-lecture.editable { cursor: text; }
.cc-texte-lecture.editable:hover { border-color: var(--line); background: var(--sunk); }
.cc-aide-mentions { margin-top: 6px; }

/* ---------- Mes tâches ---------- */
.taches-liste { display: flex; flex-direction: column; gap: 2px; margin-top: 8px; }
.tache-ligne {
  display: flex; align-items: flex-start; gap: 8px; padding: 7px 6px;
  border-top: 1px solid var(--line-soft); border-radius: 6px;
}
.tache-ligne:first-child { border-top: 0; }
.tache-ligne:hover { background: var(--sunk); }
.tache-ligne.porte { opacity: .45; }
/* Une tâche faite reste LISIBLE et reste en place : la faire disparaître au
   clic prive du seul retour qui compte, et empêche de se corriger. */
.tache-ligne.faite .tache-texte { text-decoration: line-through; color: var(--ink-3); }
.tache-poignee { cursor: grab; color: var(--ink-3); user-select: none; padding-top: 2px; }
.tache-poignee:active { cursor: grabbing; }
.tache-corps { flex: 1; min-width: 0; }
.tache-texte { line-height: 1.5; word-break: break-word; }
.tache-meta { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 2px; font-size: 11px; }
.tache-origine { color: var(--ink-3); }
.tache-ou { color: var(--ink-2); text-decoration: none; }
.tache-ou:hover { text-decoration: underline; }
/* V9.13 — édition en place d'une tâche (js/taches.js). Le champ prend toute la
   largeur disponible ; la date et les deux boutons se replient dessous sur
   mobile plutôt que d'écraser le texte. */
.tache-edition { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.tache-edition .tache-edit-champ { flex: 1 1 220px; min-width: 0; }
.tache-edition input[type="date"] { flex: 0 0 auto; }

/* ---------- V6.6 (RELI-331) : Mes tâches — vues, échéances, activité ---------- */

/* Échéance qui approche : orange à J-seuil (2 par défaut, réglable ⚙).
   Le rouge (.retard) existe depuis la V3.2 et couvre « atteinte ou dépassée ». */
.note-echeance.bientot { color: var(--warn); font-weight: 600; }

/* Brique « Tâches » du bandeau : la bulle compte ce qui reste, pas une alerte —
   elle n'a donc pas le rouge des messages non lus. */
.btn-taches { position: relative; }
.btn-taches .topbar-bulle { background: rgba(255, 255, 255, .28); } /* voile blanc sur le bandeau, voir plus haut */

/* Onglets À faire / Fait / Kanban, avec le ⚙ calé à droite sur la même ligne. */
.taches-onglets { margin: 12px 0 10px; align-items: center; }
.taches-onglets-espace { flex: 1; }

/* La grille d'activité : une colonne par semaine, un carré par jour, la teinte
   monte avec le nombre de tâches accomplies. Volontairement sous l'en-tête et
   au-dessus des onglets : c'est un encouragement, pas un tableau de bord. */
.hm-zone { margin: 8px 0 2px; }
.hm-grille {
  display: grid; grid-auto-flow: column;
  grid-template-rows: repeat(7, 10px); grid-auto-columns: 10px;
  gap: 3px; margin-bottom: 5px; max-width: 100%; overflow-x: auto;
}
.hm-case {
  width: 10px; height: 10px; border-radius: 2px;
  background: var(--sunk-2); display: inline-block; flex: none;
}
/* V8.57 — la rampe était quatre sarcelles figées : en thème sombre elle partait du
   clair pour finir au foncé, soit l'échelle À L'ENVERS — le jour le plus vide y
   paraissait le plus rempli. Les deux échelons du milieu n'ont pas de jeton et on
   n'en crée pas : ils se calculent entre le fond enfoncé et l'accent, donc la rampe
   monte toujours dans le sens du thème. */
.hm-case.n1 { background: color-mix(in srgb, var(--accent) 22%, var(--sunk-2)); }
.hm-case.n2 { background: color-mix(in srgb, var(--accent) 55%, var(--sunk-2)); }
.hm-case.n3 { background: var(--accent); }
.hm-case.n4 { background: var(--accent-fonce); }
.hm-legende { display: inline-flex; gap: 3px; align-items: center; font-size: 11px; color: var(--ink-3); }

/* Le kanban : trois colonnes, des cartes qui se prennent à la main.
   « En cours » est un tri personnel ; déposer dans « Fait » coche pour de vrai. */
.taches-kanban { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; margin-top: 8px; }
.kanban-col { background: var(--sunk); border-radius: 8px; padding: 8px; min-height: 140px; }
.kanban-col h4 {
  margin: 2px 4px 8px; font-size: 11px; letter-spacing: .07em;
  text-transform: uppercase; color: var(--ink-2);
}
.kanban-col.drop-into { outline: 2px dashed var(--accent); outline-offset: -2px; background: var(--accent-pale); }
.kanban-vide { margin: 4px; font-size: 12px; color: var(--ink-3); }
.kanban-carte {
  background: var(--card); border: 1px solid var(--line-soft); border-radius: 6px;
  padding: 7px 8px; margin-bottom: 6px; cursor: grab;
}
.kanban-carte:active { cursor: grabbing; }
.kanban-carte.porte { opacity: .45; }
.kanban-carte.faite .tache-texte { text-decoration: line-through; color: var(--ink-3); }
.kanban-carte-tete { display: flex; gap: 7px; align-items: flex-start; }
.kanban-carte .tache-meta { margin-top: 4px; margin-left: 22px; }
@media (max-width: 700px) { .taches-kanban { grid-template-columns: 1fr; } }

/* ============================================================
   V7.5 (RELI-444) — MISE EN FORME LÉGÈRE ET RÉACTIONS
   ============================================================ */

/* -- barre de sélection des messages --
   Elle vit sur <body> et se place en coordonnées ÉCRAN (le miroir de
   texte_riche.js les calcule) : d'où `fixed`, là où la barre des blocs est
   `absolute` dans la racine de son éditeur. Tout le reste — fond, boutons,
   ombre — est hérité de .bloc-barre-fmt : deux barres qui se ressemblent
   doivent se ressembler vraiment. */
.md-barre { position: fixed; z-index: 260; }

/* V8.49 — LA BARRE FIXE, SOUS LE CHAMP.
   Elle ne peut PAS hériter de .bloc-barre-fmt : cette dernière est une bulle
   sombre posée par-dessus le texte, ce qui a un sens quand elle flotte au ras
   d'une sélection et aucun quand elle est posée à demeure sous un champ — un
   rectangle noir permanent en bas d'un composeur pèserait plus lourd que le
   message qu'on écrit. Même jeu de boutons, poids visuel inverse : c'est une
   rangée d'outils, pas une fenêtre. */
.com-outils { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 4px; }
.com-outils .pj-zone { margin-top: 0; }
.md-barre-fixe { display: flex; align-items: center; gap: 1px; }
.md-barre-fixe .fmt-btn { color: var(--ink-3); min-width: 24px; padding: 2px 5px; }
.md-barre-fixe .fmt-btn:hover { background: var(--line-soft); color: var(--ink); }
.md-barre-fixe .fmt-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* -- puces dans un message (V8.49) --
   `white-space: normal` : le conteneur est en `pre-wrap`, et une puce n'a
   aucune raison de conserver l'indentation tapée devant le tiret — celle-ci a
   déjà servi, c'est elle qui a fait la puce. */
.md-puces { margin: 4px 0; padding-left: 20px; white-space: normal; }
.md-puces li { margin: 1px 0; }

/* -- V8.49 : filtrer et replier une liste de réglages (ccListeFiltrable) -- */
.cc-liste-filtre { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 6px 0 10px; }
.cc-liste-filtre input[type="search"] {
  border: 1px solid var(--line); border-radius: 6px; padding: 5px 9px; font-size: 13px;
  flex: 1 1 220px; min-width: 180px; max-width: 320px;
}
.cc-liste-filtre select { border: 1px solid var(--line); border-radius: 6px; padding: 5px 7px; font-size: 13px; }
.cc-liste-reste { display: block; margin: 8px auto 0; }

/* -- V8.50 : « ↓ N de plus » au pied d'une pile (ccPlafonnerListe) --
   ⚠ SANS LA SECONDE LIGNE, `hidden` NE CACHE RIEN. Le `display:none` de
     l'attribut vient de la feuille du NAVIGATEUR ; le `display:inline-flex`
     de `.btn` vient de celle de l'auteur, et gagne. Le bouton resterait donc
     visible, et vide, une fois toute la pile dépliée. Cinquième fois de la
     série (badge publié V8.44, déclencheur de note V8.45…) : tout `hidden`
     posé sur un élément déjà stylé demande sa règle. */
.cc-plus-pile { display: block; width: 100%; margin-top: 6px; font-size: 12px; color: var(--ink-2); }
.cc-plus-pile[hidden] { display: none; }
.taches-liste .cc-plus-pile { width: auto; margin: 8px auto 0; }

/* -- rendu des marques, dans un message comme dans un commentaire -- */
.msg-corps code, .com-corps code {
  background: var(--sunk); border: 1px solid var(--line-soft);
  border-radius: 4px; padding: 0 4px; font-size: 12px;
}
.msg-corps s, .com-corps s { color: var(--ink-3); }
.md-lien { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; word-break: break-word; }

/* -- réactions --
   La rangée ne prend AUCUNE place quand elle est vide : pas de marge, pas de
   hauteur. Un fil sans réaction doit être exactement l'écran de la V7.4. */
.react-rangee { display: flex; flex-wrap: wrap; gap: 4px; align-items: center; }
.react-rangee:not(:empty) { margin-top: 5px; }
.react-pastille {
  display: inline-flex; align-items: center; gap: 4px; cursor: pointer;
  background: var(--card); border: 1px solid var(--line); border-radius: 11px;
  padding: 1px 7px; font-size: 12px; line-height: 18px;
}
.react-pastille:hover:not(:disabled) { border-color: var(--accent); }
.react-pastille:disabled { cursor: default; opacity: .75; }
/* Celles où je suis : c'est l'information qui décide du sens du prochain clic. */
.react-pastille.moi { background: var(--accent-pale); border-color: var(--accent); }
.react-nb { color: var(--ink-2); font-size: 11px; font-variant-numeric: tabular-nums; }
.react-pastille.moi .react-nb { color: var(--accent); font-weight: 600; }

/* Le bouton d'ajout ne se montre qu'au survol du message — ou dès qu'il y a
   déjà des réactions, sinon la rangée « saute » de largeur sous le curseur.
   Il reste visible en permanence au doigt : sans survol, un bouton caché est
   un bouton absent. */
.react-ajouter {
  border: 1px dashed var(--line); background: none; color: var(--ink-3);
  border-radius: 11px; padding: 1px 7px; font-size: 11px; line-height: 18px;
  cursor: pointer; opacity: 0; transition: opacity .12s;
}
.msg-bulle:hover .react-ajouter, .com-message:hover .react-ajouter,
.react-ajouter:focus-visible, .react-rangee .react-pastille ~ .react-ajouter { opacity: 1; }
.react-ajouter:hover { color: var(--accent); border-color: var(--accent); }
@media (hover: none) { .react-ajouter { opacity: 1; } }

.react-choix { display: flex; flex-wrap: wrap; gap: 3px; max-width: 220px; }
.react-rapide {
  border: 1px solid transparent; background: none; cursor: pointer;
  border-radius: 6px; padding: 3px 5px; font-size: 17px; line-height: 1.1;
}
.react-rapide:hover { background: var(--accent-pale); }
.react-rapide.moi { border-color: var(--accent); background: var(--accent-pale); }
.react-plus { font-size: 14px; color: var(--ink-3); }

/* -- V7.7 (RELI-447) : recherche dans la barre des conversations -- */
.conv-recherche {
  width: 100%; margin-bottom: 6px; padding: 5px 8px; font-size: 12px;
  border: 1px solid var(--line); border-radius: 6px; background: var(--card);
}
.conv-recherche:focus { outline: none; border-color: var(--com); }
/* L'extrait d'un message trouvé : distinct du dernier message, qu'il remplace,
   sinon on croit lire l'aperçu habituel et on ne comprend pas le classement. */
.conv-trouve { color: var(--ink-2); font-style: italic; }
.conv-trouve em { font-style: normal; color: var(--ink-3); font-size: 11px; }
/* -- V7.8 (RELI-448) : pagination du fil et message ciblé -- */
.msg-plus {
  align-self: center; margin: 2px 0 6px; padding: 3px 10px;
  border: 1px solid var(--line); border-radius: 12px; background: var(--card);
}
.msg-plus:hover { border-color: var(--com); }
/* Le message atteint depuis une recherche : signalé assez pour qu'on le
   repère d'un coup d'œil, et la marque s'efface d'elle-même — elle dit « c'est
   ici », pas « ce message est spécial ». */
.msg-cible {
  outline: 2px solid var(--com); outline-offset: 2px; border-radius: 8px;
  background: var(--com-pale);
  animation: msg-cible-pulse 1s ease-out 2;
}
/* La marque NE S'EFFACE PLUS toute seule : dans une conversation courte, rien
   ne défile, et un éclair de deux secondes était le SEUL retour visible —
   raté, on ne comprenait pas où l'on avait atterri. Elle reste jusqu'à ce
   qu'on touche le fil (retirée par le script). */
@keyframes msg-cible-pulse {
  0% { outline-color: transparent; }
  50% { outline-color: var(--com); }
  100% { outline-color: var(--com); }
}
.msg-cible::after {
  content: 'résultat de la recherche';
  display: block; margin-top: 3px;
  font-size: 10px; letter-spacing: .06em; text-transform: uppercase; color: var(--com);
}


/* ============================================================
   V8.2 — APERÇU D'UN MODÈLE (lecture seule)
   Volontairement sobre et compact : c'est une CHARPENTE qu'on lit avant de
   poser un modèle — quels titres, quelles sections, quelles cases — pas une
   reproduction fidèle de la fiche qui en sortira.
   ============================================================ */
.modele-apercu { font-size: 13px; line-height: 1.45; color: var(--ink-2); }
.modele-apercu-titre { font-weight: 700; color: var(--ink); margin: 8px 0 2px; }
.modele-apercu-t1 { font-size: 16px; }
.modele-apercu-t2 { font-size: 14.5px; }
.modele-apercu-t3 { font-size: 13.5px; }
.modele-apercu-p { margin: 2px 0; }
.modele-apercu-cit { border-left: 3px solid var(--line); padding-left: 8px; font-style: italic; }
.modele-apercu-callout { background: var(--sunk); border-radius: 8px; padding: 6px 9px; margin: 6px 0; }
.modele-apercu-code {
  background: var(--sunk); border-radius: 6px; padding: 6px 8px; margin: 6px 0;
  font-size: 12px; white-space: pre-wrap; overflow-x: auto;
}
.modele-apercu-hr { border: 0; border-top: 1px solid var(--line); margin: 8px 0; }
.modele-apercu-bloc { margin: 4px 0; }
.modele-liste .bloc-menu-item { width: 100%; }


/* ============================================================
   V8.3 — REDIMENSIONNEMENT DES COLONNES (blocs « tableau » et « tableau lié »)
   Le bloc « tableur » le savait depuis la V4.2 ; ces deux-là non — alors que
   c'est là qu'on met une colonne « Décision » de trois mots à côté d'une
   colonne « Contexte » de trois lignes.
   ============================================================ */

/* `table-layout: fixed` UNIQUEMENT quand une largeur a été posée. Sans cette
   condition, tous les tableaux déjà écrits changeraient d'aspect au simple
   rechargement : en mode automatique les colonnes suivent leur contenu, en
   mode fixe elles se partagent la largeur à parts égales. Un tableau intact
   doit rester intact. */
.bloc-tableau.largeurs { table-layout: fixed; }
/* La table cesse d'être contrainte à 100 % : sinon, quand la somme des
   largeurs demandées dépasse la place disponible, le navigateur les réduit
   TOUTES proportionnellement — on tire une colonne à 280 px, on en obtient
   230, et les voisines rétrécissent sans qu'on ait rien demandé. En largeur
   `max-content` avec un plancher à 100 %, les colonnes valent exactement ce
   qu'on a réglé et l'enveloppe (déjà en `overflow-x: auto`) défile.
   `width: auto` ne suffit PAS : en disposition fixe, Chromium prend alors la
   largeur disponible et rétrécit les colonnes pour l'occuper — mesuré. */
/* V9.5.4 — `min-width: 0`, ET LE PLANCHER À 100 % DISPARAÎT. Demande de test :
   « qu'ils puissent prendre moins que la largeur de la page ». Le plancher
   l'interdisait — un tableau de trois colonnes courtes restait étiré sur toute
   la page, et rétrécir une colonne ne faisait qu'en élargir une autre.

   Ce n'est pas seulement une gêne d'affichage : le plancher RENDAIT FAUSSES
   les largeurs réglées. En disposition fixe, l'espace imposé par un `min-width`
   supérieur à la somme des colonnes est redistribué entre elles — on tirait une
   colonne à 120 px, on en voyait 190, et la valeur enregistrée ne correspondait
   à rien de ce qui était à l'écran.

   Le tableau vaut donc désormais exactement la somme de ses colonnes, ni plus
   ni moins. `ccTabFigerLargeurs()` garantit qu'elles ont TOUTES une largeur dès
   le premier geste, donc cette somme est toujours définie.
   ⚠ Cela ne concerne QUE les tableaux dont on a réglé une colonne (`.largeurs`).
     Un tableau auquel personne n'a touché reste en pleine largeur, comme avant
     — même règle que le `table-layout: fixed` juste au-dessus. Et le
     double-clic sur une poignée efface les largeurs : le tableau reprend alors
     toute la page. */
.bloc-tableau.largeurs { width: max-content; min-width: 0; max-width: none; }
/* Le plancher de 70 px de `.bloc-tableau td` empêcherait de rétrécir une
   colonne en deçà — on le lève seulement là où l'utilisateur a pris la main. */
/* `overflow-wrap` et NON `overflow: hidden` : couper une URL en plein milieu
   sans le dire est pire que la laisser passer à la ligne, et `hidden` rognait
   au passage les 3 px de la poignée du tableau lié, qui vit DANS la cellule. */
.bloc-tableau.largeurs td { min-width: 0; overflow-wrap: anywhere; }

/* Barre de saisie du bloc « tableau » : en SUPERPOSITION dans l'enveloppe,
   parce que les cellules de la première ligne sont éditables et que leur
   contenu est relu tel quel (même raison que les poignées ⠿ depuis la V3.1). */
.bloc-tab-redim {
  position: absolute; width: 7px; z-index: 3; cursor: col-resize;
  border-radius: 3px; background: transparent; transition: background .12s;
}
.bloc-tab-redim:hover, body.cc-redim-en-cours .bloc-tab-redim:hover { background: var(--accent); opacity: .55; }

/* Bloc « tableau lié » : cellules non éditables, la poignée vit dedans. */
.bloc-tableau.tablie td { position: relative; }
.tablie-redim {
  position: absolute; top: 0; right: -3px; width: 7px; height: 100%;
  z-index: 3; cursor: col-resize; border-radius: 3px; background: transparent; transition: background .12s;
}
.tablie-redim:hover { background: var(--accent); opacity: .55; }

/* Pendant le glissement, tout le curseur de la page passe en col-resize et la
   sélection de texte est neutralisée : sans cela, tirer une colonne
   sélectionne le texte des cellules qu'on survole. */
body.cc-redim-en-cours { cursor: col-resize; user-select: none; }
body.cc-redim-en-cours * { cursor: col-resize !important; }

/* La bande verticale « + colonne » est ancrée au bord droit de l'enveloppe
   (`right: 0`). Tant que la table faisait 100 % de large, elle vivait dans la
   gouttière ; en mode fixe la table déborde et défile, et la bande se
   retrouvait POSÉE SUR les cellules — invisible mais cliquable : viser une
   cellule ajoutait une colonne. Elle est désormais recalée par le script,
   contre le bord droit DU TABLEAU, où qu'il soit ; d'où le `right: auto`
   qu'il pose en même temps que le `left`. */
.bloc-tab-plus.col[style*="left"] { right: auto; }
/* ⚠ V9.38 (RELI-80) — LE SCRIPT LE FAIT MAINTENANT DANS TOUS LES CAS, et la
   règle ci-dessus reste pour la même raison : elle neutralise le `right: 0` du
   CSS dès qu'un `left` est posé. Le défaut corrigé n'était pas ici mais dans la
   GARDE côté script, qui réservait ce recalage au seul mode fixe (`.largeurs`).
   Un tableau ordinaire déborde très bien lui aussi — `min-width: 70px` par
   cellule — et la bande atterrissait alors au milieu du tableau sur un écran
   étroit. C'est exactement le symptôme du ticket.
   « + ligne » reçoit le même traitement : il vivait sur `left: 0; right: 22px`,
   donc sur la boîte VISIBLE, et ne suivait ni la largeur réelle du tableau ni
   son défilement. */
.bloc-tab-plus.ligne[style*="left"] { right: auto; bottom: auto; }

/* ============================================================
   ⚠ V9.38 (RELI-80) — AU DOIGT, CES BOUTONS N'EXISTAIENT PAS.

   Tout le dispositif du bloc tableau — le « + », les poignées ⠿, les barres de
   redimensionnement — n'apparaît qu'au SURVOL (`:hover`, plus haut). Le bloc
   tableau est le seul du produit à n'avoir aucune contrepartie tactile : le
   reste en a une depuis longtemps (voir les autres `@media (hover: none)` de ce
   fichier). Sur un téléphone, ces commandes ne se montraient donc que par le
   survol SIMULÉ que Chrome accorde après un appui — c'est-à-dire par accident.

   Ici : visibles en permanence, mais estompées, pour qu'elles se voient sans
   voler l'attention au texte. Et agrandies : 18 px de bande et 11 px de poignée
   ne sont pas des cibles pour un pouce, alors que le produit vise 40 px
   ailleurs. On ne peut pas donner 40 px à une gouttière de 22 px sans déformer
   le tableau ; 26 px est le compromis — nettement mieux, et sans repenser la
   mise en page du bloc.
   ============================================================ */
@media (hover: none) {
  .bloc-tab-plus { opacity: .5; }
  .bloc-tab-drag { opacity: .5; }
  .bloc-tab-plus.col { width: 26px; }
  .bloc-tab-plus.ligne { height: 26px; }
  .bloc-tab-redim { width: 14px; }
  /* ⚠ ET LA GOUTTIÈRE SUIT. Élargir la bande sans élargir la marge de
     l'enveloppe l'aurait fait mordre de 6 px sur la dernière colonne : on
     aurait remplacé un bouton posé au milieu du tableau par un bouton posé sur
     son bord. Le même défaut, quatre pixels plus loin. */
  .bloc-tableau-enveloppe { padding-right: 30px; padding-bottom: 30px; }
}


/* V8.4 — « des modifications ont eu lieu ». Discret et en bas : un bandeau en
   haut aurait poussé toute la page vers le bas, ce qui est exactement le
   déplacement qu'on cherche à éviter pendant une saisie. */
/* Deux bandeaux peuvent coexister — « la collection a changé » et « une
   nouvelle version est en ligne » — et c'est même le moment le plus probable :
   un déploiement. Superposés au même pixel, le second cachait entièrement le
   premier, bouton compris. On les empile. */
.bandeau-maj {
  position: fixed; left: 50%; transform: translateX(-50%); bottom: 18px; z-index: 900;
  display: flex; align-items: center; gap: 10px;
  background: var(--card, #fff); border: 1px solid var(--line);
  border-radius: 999px; padding: 7px 10px 7px 16px; font-size: 13px;
  box-shadow: var(--ombre-flot);
}

/* ============================================================
   V8.4 — VUE GALERIE et ORGANIGRAMME
   Des cartes, pas des colonnes : contrairement au kanban, la position d'une
   carte ne porte aucune donnée — on ne déplace personne d'un service à
   l'autre en la faisant glisser. D'où l'absence totale d'affordance de
   glissement ici.
   ============================================================ */
.galerie {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
}
.galerie-carte {
  display: flex; flex-direction: column; text-decoration: none; color: inherit;
  border: 1px solid var(--line); border-radius: 10px; overflow: hidden;
  background: var(--card, #fff); transition: box-shadow .12s, transform .12s;
}
.galerie-carte:hover { box-shadow: 0 6px 18px rgba(24, 43, 58, .12); transform: translateY(-1px); }
.galerie-photo { aspect-ratio: 4 / 3; background: var(--line-soft); overflow: hidden; }
.galerie-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.galerie-photo-vide {
  display: flex; align-items: center; justify-content: center;
  color: #fff; font-size: 34px; font-weight: 700; /* V8.57 — initiale posée sur la couleur stable de la personne : blanc dans les deux thèmes. */
}
.galerie-corps { padding: 8px 10px 10px; }
.galerie-titre { font-weight: 600; font-size: 14px; line-height: 1.25; overflow-wrap: anywhere; } /* V8.8 */
/* V8.8 — un email interminable pliait la grille au lieu de se plier lui-même */
.galerie-champ { font-size: 12.5px; color: var(--ink-2); margin-top: 3px; overflow-wrap: anywhere; }
.galerie-etiquette { color: var(--ink-3); font-size: 11px; text-transform: uppercase; letter-spacing: .04em; }

/* ============================================================
   V8.8 — L'ORGANIGRAMME EST UN ARBRE, PAS UNE GRILLE.
   La V8.4 indentait les rattachés de 14 px sous une carte-photo de 180 px :
   l'œil ne voyait qu'une grille (retour de test : « les fiches sont restées
   côte à côte »). Le responsable est maintenant CENTRÉ au-dessus de ses
   rattachés, et des TRAITS les relient : une tige descend de la carte
   (.orga-tige), une barre court au-dessus du rang (::before/::after des
   enfants — moitié gauche, moitié droite, supprimées aux extrémités), et la
   verticale de chaque enfant est le border-left du ::after, posé au centre.
   Défilement horizontal assumé : un arbre large se parcourt, il ne
   s'écrase pas. */
.orga { overflow-x: auto; padding: 8px 0 12px; }
.orga-racines { display: flex; gap: 34px; align-items: flex-start; width: max-content; margin: 0 auto; }
.orga-noeud { display: flex; flex-direction: column; align-items: center; position: relative; }
.orga-tige { width: 2px; height: 18px; background: var(--line); }
.orga-fils { display: flex; align-items: flex-start; }
.orga-fils > .orga-noeud { padding: 18px 7px 0; }
.orga-fils > .orga-noeud::before,
.orga-fils > .orga-noeud::after { content: ''; position: absolute; top: 0; width: 50%; height: 18px; border-top: 2px solid var(--line); }
.orga-fils > .orga-noeud::before { right: 50%; }
.orga-fils > .orga-noeud::after { left: 50%; border-left: 2px solid var(--line); margin-left: -1px; }
.orga-fils > .orga-noeud:first-child::before { border-top: 0; }
.orga-fils > .orga-noeud:last-child::after { border-top: 0; }

/* La carte compacte : avatar rond + nom + champs — dense, lisible à 40 personnes. */
.orga-carte { display: flex; gap: 8px; align-items: center; text-decoration: none; color: inherit;
  border: 1px solid var(--line); border-radius: 10px; background: var(--card, #fff);
  padding: 7px 10px; width: 190px; box-sizing: border-box; transition: box-shadow .12s, transform .12s; }
.orga-carte:hover { box-shadow: 0 4px 14px rgba(24, 43, 58, .12); transform: translateY(-1px); }
.orga-avatar { width: 40px; height: 40px; border-radius: 50%; overflow: hidden; flex: none;
  display: flex; align-items: center; justify-content: center; }
.orga-avatar img { width: 100%; height: 100%; object-fit: cover; }
.orga-avatar-vide { color: #fff; font-size: 17px; font-weight: 700; } /* même raison que .galerie-photo-vide */
.orga-corps { display: flex; flex-direction: column; min-width: 0; }
.orga-nom { font-weight: 600; font-size: 13px; line-height: 1.25; overflow-wrap: anywhere; }
.orga-champ { font-size: 11.5px; color: var(--ink-3); line-height: 1.3;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 130px; }

/* V8.12 — annuler / rétablir de l'éditeur de blocs : discret, trouvable */
.blocs-editeur { position: relative; }
.blocs-hist { position: absolute; top: 2px; right: 2px; display: flex; gap: 2px; opacity: .3; transition: opacity .15s; }
.blocs-editeur:hover .blocs-hist, .blocs-hist:focus-within { opacity: 1; }
.blocs-hist-btn { border: 1px solid var(--line); background: var(--card, #fff); border-radius: 6px;
  width: 24px; height: 22px; font-size: 12px; line-height: 1; cursor: pointer; color: var(--ink-2); }
.blocs-hist-btn:hover { color: var(--ink, #182b3a); border-color: var(--accent, #0f7583); }

/* V8.11b — le bouton des vues : le nom de la vue active, accentué discrètement */
.onglet.vue-active { color: var(--accent-fonce); font-weight: 600; }

/* V8.11 (RELI-311) — ordre personnalisé : poignée de ligne et repères de dépôt */
.td-poignee, .th-poignee { width: 28px; text-align: center; padding: 0 2px; color: var(--ink-3); }
.poignee-ligne { cursor: grab; user-select: none; font-size: 13px; opacity: .55; }
tr:hover .poignee-ligne { opacity: 1; }
.ligne-en-deplacement { opacity: .45; }
.ligne-drop-avant td { border-top: 2px solid var(--accent, #0f7583); }
.ligne-drop-apres td { border-bottom: 2px solid var(--accent, #0f7583); }

.bandeau-maj-haut { bottom: 70px; }
.bandeau-maj-bas { bottom: 18px; }

/* ============================================================
   V8.6 — VUE GRAPHIQUE
   Le tracé lui-même est en SVG (js/graphique_svg.js) et n'a besoin d'aucun
   style : tout y est dessiné. Ce qui suit n'habille que ce qu'il y a AUTOUR —
   les onglets de graphes, la carte qui les contient, et la ligne de réglage
   d'une série.
   ============================================================ */
.onglets-graphes { margin-bottom: 6px; }
.onglets-graphes .onglet { font-size: 12.5px; padding: 4px 12px; }
.carte-graphique { padding: 14px 16px 12px; color: var(--ink); }
.cc-graphe { max-width: 100%; }
.graph-series { display: flex; flex-direction: column; gap: 5px; margin: 2px 0 6px; }
.graph-serie-ligne { display: flex; align-items: center; gap: 5px; }
.graph-serie-ligne select { max-width: 130px; }
.popover-graphe label { display: block; margin-top: 8px; font-size: 11.5px; color: var(--ink-3); }
.popover-graphe label.case { display: flex; align-items: center; gap: 5px; margin-top: 2px; font-size: 12.5px; color: var(--ink); }

/* ============================================================
   V8.6 — VERROU DE COLONNE
   Le cadenas doit se voir sans être criard : c'est une information d'état, pas
   une alerte. Assez présent pour qu'on le remarque AVANT de cliquer dans une
   cellule, assez discret pour qu'une colonne gelée reste lisible.
   ============================================================ */
.pastille-verrou {
  border: 0; background: none; padding: 0 0 0 4px; cursor: pointer;
  color: var(--ink-3); vertical-align: middle; line-height: 1;
}
.pastille-verrou:hover { color: var(--ink); }
.pastille-verrou svg { display: inline-block; vertical-align: -1px; }
/* La cellule d'une colonne gelée ne doit pas donner l'impression qu'on peut y
   écrire : ni curseur texte, ni surbrillance au survol. */
th:has(.pastille-verrou) { background: var(--line-soft); }
td.cellule-verrouillee { cursor: default; background: var(--line-soft); }
td.cellule-verrouillee:hover { background: var(--line-soft); }
.champ-ligne.champ-verrouille label { color: var(--ink-3); }
.champ-ligne.champ-verrouille label .pastille-verrou { padding-left: 3px; }
.bloc-verrou { border-left: 3px solid var(--line); }

/* ============================================================
   V8.13.3 (lot A de la V8.14) — L'INDICATEUR DE GLOSSAIRE.
   Discret par construction : il vit SOUS la carte, en 11-12 px, et disparaît
   totalement quand le champ est vide. Un compteur qui crie à chaque frappe
   serait un reproche permanent ; celui-ci ne se colore qu'en cas d'entrée
   réellement ignorée par la recherche.
   ============================================================ */
.cc-analyse { font-size: 11px; color: var(--ink-3); margin: 6px 0 0; display: flex; gap: 8px; align-items: baseline; }
.cc-analyse.ton-ok .cc-analyse-resume { color: var(--ok); }
.cc-analyse.ton-info .cc-analyse-resume { color: var(--ink-2); }
.cc-analyse.ton-avert .cc-analyse-resume { color: var(--st-ouvert); }
.cc-analyse-resume { font-variant-numeric: tabular-nums; }
.cc-analyse-bascule { background: none; border: 0; padding: 0; font: inherit; color: var(--accent); cursor: pointer; text-decoration: underline; }
.cc-analyse-bascule:hover { color: var(--accent-fonce); }
.cc-analyse-detail { list-style: none; margin: 4px 0 0; padding: 0; font-size: 11px; }
.cc-analyse-detail li { display: flex; gap: 6px; align-items: baseline; padding: 3px 0; border-top: 1px solid var(--line-soft); }
.cc-analyse-detail li.ton-avert { color: var(--st-ouvert); }
.cc-analyse-detail li.ton-info { color: var(--ink-2); }
.cc-analyse-num { font-family: var(--mono); font-size: 10px; color: var(--ink-3); flex: none; }
.cc-analyse-texte { font-family: var(--mono); font-size: 10px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 40%; flex: none; }
.cc-analyse-note { color: var(--ink-3); }

/* ============================================================
   V8.15.2 — LE VOCABULAIRE EN TABLEAU.
   Il reprend `.tableau` et `.cellule-editable` du tableau des collections :
   même apparence, même geste, rien de nouveau à apprendre. Les lignes héritées
   sont grisées et non cliquables — on voit d'un coup d'œil ce qui appartient à
   cette collection et ce qui vient d'ailleurs.
   ============================================================ */
.carte-glossaire .cc-gloss-entete { display: flex; gap: 10px; align-items: center; justify-content: space-between; margin: 2px 0 8px; }
.cc-gloss-filtre { flex: 1; min-width: 0; font-size: 12px; padding: 4px 8px; border: 1px solid var(--line); border-radius: var(--r); }
.cc-gloss-compte { font-size: 11px; color: var(--ink-3); font-variant-numeric: tabular-nums; white-space: nowrap; }

.cc-gloss-table td { vertical-align: top; font-size: 13px; }
.cc-gloss-table .cellule-longue { white-space: pre-wrap; }
.cc-gloss-table textarea, .cc-gloss-table input, .cc-gloss-table select {
  width: 100%; font: inherit; font-size: 13px; border: 1px solid var(--accent);
  border-radius: var(--r); padding: 4px 6px; background: var(--input-bg); }
.cc-gloss-tr-herite td { background: var(--line-soft); color: var(--ink-2); }
.cc-gloss-tr-neuf td { background: transparent; border-top: 1px dashed var(--line); }
.cc-gloss-neuf-champ { width: 100%; font: inherit; font-size: 13px; padding: 4px 6px;
  border: 1px dashed var(--line); border-radius: var(--r); background: transparent; }
.cc-gloss-neuf-champ:focus { border-style: solid; border-color: var(--accent); background: var(--input-bg); outline: none; }
.cc-gloss-jeton { display: inline-block; background: var(--accent-pale); color: var(--accent-fonce);
  border-radius: 10px; padding: 0 7px; margin: 0 3px 3px 0; font-size: 11.5px; }
.cc-gloss-vide { color: var(--ink-3); }
.cc-gloss-badge { display: inline-block; font-size: 10px; padding: 1px 6px; border-radius: 10px;
  border: 1px solid var(--line); color: var(--ink-2); white-space: nowrap; margin: 0 3px 3px 0; }
.cc-gloss-badge.herite { background: var(--line-soft); }
.cc-gloss-badge.redefini { background: var(--accent-pale); border-color: var(--accent-pale); color: var(--accent-fonce); }
.cc-gloss-badge.afinir { color: var(--st-ouvert); border-color: var(--st-ouvert); }
.cc-gloss-td-actions { white-space: nowrap; text-align: right; }
.cc-gloss-th-actions { width: 90px; }
.cc-gloss-menu { display: grid; gap: 2px; }
.cc-gloss-menu .btn-lien { text-align: left; }
.cc-gloss-menu .aide-modal { margin-bottom: 6px; }
.cc-gloss-ligne { display: flex; gap: 8px; align-items: baseline; padding: 5px 0; border-top: 1px solid var(--line-soft); font-size: 13px; }
.cc-gloss-terme { font-weight: 600; flex: none; min-width: 110px; }
.cc-gloss-def { flex: 1; min-width: 0; }
.cc-gloss-ed { flex: none; font-size: 11px; }
.cc-gloss-ajout { display: block; margin-top: 8px; font-size: 12px; }
.cc-gloss-editeur { border-left: 3px solid var(--accent-pale); padding: 8px 0 8px 10px; margin: 2px 0 6px; display: grid; gap: 6px; }
.cc-gloss-champ { display: grid; gap: 2px; font-size: 11px; color: var(--ink-2); }
.cc-gloss-champ input, .cc-gloss-champ textarea, .cc-gloss-champ select {
  font: inherit; font-size: 13px; color: var(--ink); padding: 4px 6px;
  border: 1px solid var(--line); border-radius: var(--r); width: 100%; background: var(--card); }
.cc-gloss-actions { display: flex; gap: 6px; }
.cc-gloss-texte { width: 100%; min-height: 180px; font-family: var(--mono); font-size: 12px;
  border: 1px solid var(--line); border-radius: var(--r); padding: 8px; resize: vertical; }

/* V8.14.2 (lot F) — l'écran Glossaire de l'organisation. */
.cc-gouv-section { margin-bottom: 14px; }
.cc-gouv-ligne { display: flex; gap: 8px; align-items: baseline; padding: 4px 0; border-top: 1px solid var(--line-soft); font-size: 13px; flex-wrap: wrap; }
.cc-gouv-ligne:first-child { border-top: 0; }
.cc-gouv-txt { color: var(--ink-2); font-size: 12px; }
.cc-gouv-ou { font-size: 11px; color: var(--ink-3); background: var(--line-soft); padding: 1px 6px; border-radius: 10px; white-space: nowrap; }
.cc-gouv-conflit { border-left: 3px solid var(--st-ouvert); padding: 6px 0 6px 10px; margin-bottom: 10px; }
.cc-gouv-tete { display: flex; gap: 10px; align-items: baseline; }
.cc-gouv-def { display: flex; gap: 8px; align-items: baseline; padding: 3px 0; font-size: 13px; flex-wrap: wrap; }

/* V8.14.3 (lot E) — la carte Relations : le calculé au-dessus du déclaré. */
.cc-rel-titre { font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-3); margin: 0 0 4px; }
.cc-rel-titre .aide-modal { display: inline; text-transform: none; letter-spacing: 0; }
.cc-rel-ligne { display: flex; gap: 8px; align-items: baseline; padding: 3px 0; font-size: 13px; flex-wrap: wrap; }
.cc-rel-ligne.calcule { color: var(--ink-2); }
.cc-rel-sujet, .cc-rel-objet { font-weight: 600; }
.cc-rel-ligne.calcule .cc-rel-sujet, .cc-rel-ligne.calcule .cc-rel-objet { font-weight: 500; }
.cc-rel-pred { color: var(--accent-fonce); background: var(--accent-pale); padding: 1px 7px; border-radius: 10px; font-size: 12px; }
.cc-rel-pred.libre { color: var(--st-ouvert); background: transparent; border: 1px dashed var(--st-ouvert); }
.cc-rel-inverse { font-size: 11px; color: var(--ink-3); }
.cc-rel-comm { font-size: 12px; color: var(--ink-2); margin: 0 0 6px 14px; border-left: 2px solid var(--line-soft); padding-left: 8px; white-space: pre-wrap; }
.cc-rel-saisie { display: flex; gap: 6px; margin-top: 10px; flex-wrap: wrap; }
.cc-rel-saisie input { font: inherit; font-size: 13px; padding: 4px 6px; border: 1px solid var(--line); border-radius: var(--r); flex: 1; min-width: 90px; }

/* ============================================================
   V8.14.5 — LE BANDEAU DE MISE À JOUR, LISIBLE SUR TÉLÉPHONE.
   Il était dessiné comme une pastille centrée (`left:50%` + `translateX`),
   ce qui tient sur un écran large et devient illisible sur un téléphone : la
   phrase se casse en colonne de trois mots, et le ✕ finit collé au bouton
   principal, trop petit pour être visé au pouce. Sur mobile il devient donc une
   BARRE PLEINE LARGEUR, en bas, texte sur sa ligne et deux boutons de taille
   normale — et « Plus tard » est écrit en toutes lettres : un ✕ de dix pixels
   n'est pas une sortie, c'est une devinette.
   ============================================================ */
.bandeau-maj-texte { flex: 1; min-width: 0; }
.bandeau-maj-actions { display: flex; gap: 8px; align-items: center; flex: none; }
.bandeau-maj-fermer { background: transparent; }

@media (max-width: 640px) {
  .bandeau-maj, .bandeau-maj-haut, .bandeau-maj-bas {
    left: 8px; right: 8px; transform: none; bottom: 8px;
    /* iOS : ne pas se glisser sous la barre de gestes. */
    bottom: calc(8px + env(safe-area-inset-bottom, 0px));
    flex-direction: column; align-items: stretch; gap: 8px;
    border-radius: 12px; padding: 12px 14px; font-size: 14px; line-height: 1.35;
  }
  .bandeau-maj-actions { justify-content: flex-end; }
  /* 40 px : la cible minimale qu'un pouce vise sans réfléchir. */
  .bandeau-maj-actions .btn { min-height: 40px; padding: 0 16px; font-size: 14px; }
}

/* V8.15 — le panneau de relations déclarées, sous le graphe. */
.cc-rel-panneau { margin-top: 14px; border-top: 1px solid var(--line-soft); padding-top: 10px; }
.cc-rel-ligne.a-rattacher { opacity: .8; }
.cc-rel-select { font: inherit; font-size: 13px; padding: 4px 6px; border: 1px solid var(--line); border-radius: var(--r); }
.cc-gloss-neuf, .cc-gloss-complete { border-left-color: var(--accent); }

/* V8.15.4 — la description textuelle du graphe, repliée par défaut. */
.graphe-texte { margin-top: 8px; }
.graphe-texte-pre { font-family: var(--mono); font-size: 12px; color: var(--ink-2); white-space: pre-wrap;
  background: var(--line-soft); border-radius: var(--r); padding: 10px; margin: 0 0 8px; max-height: 320px; overflow: auto; }
.graphe-ecremage { font-size: 12px; color: var(--ink-2); margin: 0 0 8px; }
.graphe-ecremage .aide-modal { display: inline; }

/* -- V8.20 : bloc qui ne sort pas (exports / pages publiées) -- */
/* Un liseré discret plutôt qu'un bandeau : le bloc reste parfaitement lisible
   et modifiable — on signale une DESTINATION, on ne dégrade pas le contenu. */
.bloc-ligne.bloc-masque { position: relative; }
.bloc-ligne.bloc-masque::before {
  content: ''; position: absolute; left: -10px; top: 2px; bottom: 2px; width: 3px;
  border-radius: 2px; background: repeating-linear-gradient(
    180deg, var(--ink-3, #8795a0) 0 4px, transparent 4px 8px); opacity: .55;
}

/* -- V8.21 : la description courte ne saute plus au clic --
   Le textarea d'édition et la div de lecture doivent partager EXACTEMENT les
   mêmes métriques : sans cela, à contenu égal, les lignes se coupent
   différemment et la boîte change de hauteur au moment précis où l'on clique
   pour corriger un mot. La hauteur, elle, est ajustée au contenu en JS. */
/* ⚠ V11.9 — LES DEUX CHEMINS, DANS LE MÊME BLOC. Le champ n'est plus un
   enfant DIRECT de la carte : il vit dans `.cc-surlignage-boite`, qui porte son
   calque de surlignage. Un sélecteur `>` laissé seul aurait cessé de
   s'appliquer — et c'est très exactement la panne que ce bloc existe pour
   empêcher (V8.21) : lecture et écriture à des métriques différentes, donc la
   boîte qui saute au clic. Les deux sélecteurs partagent le même bloc, pour que
   la divergence reste impossible à réintroduire par distraction. */
.carte-texte > textarea,
.carte-texte > .cc-surlignage-boite > textarea { font-size: 14px; line-height: 1.6; padding: 8px; overflow-y: hidden; }

/* -- V8.21 : le sélecteur d'emoji ne saute plus pendant la recherche --
   La grille était en `max-height` : dès que les résultats devenaient assez peu
   nombreux (typiquement au 3e caractère), elle rétrécissait de ~180 px, le
   popover se repositionnait et changeait de branche — d'où le saut en haut à
   gauche. Une hauteur FIXE réserve la place ; `scrollbar-gutter` fige aussi la
   largeur, sans quoi la disparition de l'ascenseur élargissait les colonnes. */
.emoji-grille { height: 240px; max-height: none; scrollbar-gutter: stable; }

/* -- V8.21 : « cartes par ligne » veut dire cartes par ligne --
   `repeat(auto-fit, …)` laissait la LARGEUR décider du nombre de colonnes : le
   réglage n'agissait qu'indirectement, et 5 et 6 produisaient exactement le
   même CSS. On demande le nombre exact ; l'empilement mobile passe par une
   media query, qu'un style en ligne ne peut pas porter. */
.bloc-cartes-grille { display: grid; gap: 12px;
  grid-template-columns: repeat(var(--cartes-par-ligne, 3), minmax(0, 1fr)); }
@media (max-width: 720px) { .bloc-cartes-grille { grid-template-columns: 1fr; } }

/* -- V8.21 : la note en étoiles, d'un seul geste (façon Google Maps) --
   Cinq boutons côte à côte n'ont jamais ressemblé à une note : on lisait cinq
   commandes, pas un curseur. Ici une seule rangée, survolée d'un bout à
   l'autre, où l'on clique la énième étoile. Le survol montre le résultat AVANT
   le clic : personne ne doit deviner ce qu'il s'apprête à voter. */
.avis-etoiles { display: inline-flex; gap: 2px; cursor: pointer; user-select: none; line-height: 1; }
.avis-etoiles .et { font-size: 22px; color: var(--line); transition: color .08s, transform .08s; }
.avis-etoiles .et.pleine { color: var(--warn); } /* V8.57 — l'or de l'étoile n'avait pas de jeton : on prend l'ambre du produit plutôt qu'un or qui reste fluo en sombre. */
.avis-etoiles:hover .et { transform: scale(1.06); }
.avis-etoiles.lecture { cursor: default; }
.avis-etoiles.lecture:hover .et { transform: none; }
.avis-note-chiffre { font-weight: 600; margin-left: 8px; }
.avis-note-nb { color: var(--ink-3, #8795a0); font-size: 12px; margin-left: 4px; }
/* Les autres modes gardent des boutons, mais lisibles et alignés. */
.avis-choix .avis-btn { font-size: 17px; line-height: 1; padding: 4px 9px; }
.avis-emojis-aide { font-size: 12px; color: var(--ink-3, #8795a0); margin-top: 4px; }

/* -- V8.22 : pièces jointes dans les conversations -- */
.pj-zone { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 4px; }
.pj-liste { display: inline-flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.pj-jeton { display: inline-flex; align-items: center; gap: 4px; background: var(--sunk);
  border: 1px solid var(--line); border-radius: 999px; padding: 2px 9px; font-size: 12px;
  text-decoration: none; color: inherit; }
.pj-vignette { max-height: 120px; max-width: 200px; border-radius: 8px; display: block; }

/* -- V8.22 : le guichet -- */
.support-fil { display: grid; gap: 8px; margin: 10px 0; }
.support-msg { border: 1px solid var(--line); border-radius: 10px; padding: 8px 10px; background: var(--card, #fff); }
.support-msg-externe { border-left: 3px solid var(--accent); background: var(--sunk); }
.support-msg-tete { font-size: 12px; color: var(--ink-3, #8795a0); margin-bottom: 3px; }
.support-msg-corps { white-space: pre-wrap; }
.support-ligne { padding: 6px 4px; border-bottom: 1px solid var(--line); }
.pastille-nb { background: var(--accent-plein); color: var(--sur-accent); border-radius: 999px; /* blanc SUR l'accent : il reste foncé en sombre */
  font-size: 11px; padding: 1px 7px; margin-left: 6px; }

/* ---------- V8.23 : choix cherchable (longue liste) ---------- */
/* Une liste déroulante native est parfaite à vingt entrées et inutilisable à
   trois cents. Ici on tape, ça filtre. */
.choix-cherchable { position: relative; display: inline-block; min-width: 190px; }
.choix-cherchable-champ { width: 100%; border: 1px solid var(--line); border-radius: 4px; padding: 5px 7px; font-size: 13px; }
.choix-cherchable-menu {
  position: absolute; z-index: 120; top: calc(100% + 2px); left: 0; min-width: 100%; max-width: 340px;
  max-height: 240px; overflow-y: auto; background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r); box-shadow: var(--ombre-flot); padding: 4px;
}
.choix-cherchable-item {
  display: flex; justify-content: space-between; gap: 10px; align-items: baseline; width: 100%;
  text-align: left; border: 0; background: transparent; cursor: pointer;
  padding: 5px 7px; border-radius: 4px; font-size: 13px; color: var(--ink);
}
.choix-cherchable-item:hover, .choix-cherchable-item.survol { background: var(--accent-pale); }
.choix-cherchable-detail { font-size: 11px; color: var(--ink-3); white-space: nowrap; }
.choix-cherchable-menu .vide-etat { font-size: 12px; margin: 4px 6px; }

/* ---------- V8.24 : l'interrupteur ---------- */
/* Il remplace la case à cocher partout. Trois exigences : se voir même éteint
   (une valeur jamais renseignée est « éteint », pas « rien »), basculer sous le
   doigt sans attendre le réseau, et rester utilisable au clavier. */
.inter { display: inline-flex; align-items: center; gap: 7px; cursor: pointer; vertical-align: middle; }
.inter-case { position: absolute; opacity: 0; width: 0; height: 0; margin: 0; }
.inter-piste {
  position: relative; flex: none; width: 34px; height: 19px; border-radius: 19px;
  background: var(--line); border: 1px solid var(--line-soft);
  transition: background .14s;
}
.inter-pastille {
  position: absolute; top: 1px; left: 1px; width: 15px; height: 15px; border-radius: 50%;
  background: #fff; box-shadow: var(--ombre); transition: transform .14s; /* V8.57 — le blanc de la pastille est posé SUR la piste, pas sur la page : il reste blanc dans les deux thèmes. */
}
.inter-on .inter-piste { background: var(--accent); }
.inter-on .inter-pastille { transform: translateX(15px); }
.inter-case:focus-visible + .inter-piste { outline: 2px solid var(--accent); outline-offset: 2px; }
.inter-libelle { font-size: 13px; color: var(--ink); }
.inter-fige { opacity: .5; cursor: default; }
/* Pendant l'aller-retour serveur : discret, jamais bloquant — on a déjà
   basculé, on ne fait qu'annoncer que ce n'est pas encore confirmé. */
.inter-attente .inter-piste { opacity: .65; }
.inter-compact .inter-piste { width: 28px; height: 16px; }
.inter-compact .inter-pastille { width: 12px; height: 12px; }
.inter-compact.inter-on .inter-pastille { transform: translateX(12px); }
/* Dans une cellule de tableau : centré, sans marge parasite. */
td .inter { justify-content: flex-start; }

/* ---------- V8.24 : TOUTES les cases à cocher deviennent des interrupteurs ----------
   Il y a 97 cases à cocher dans le produit. Les réécrire une par une en
   composant aurait voulu dire 97 occasions de casser un comportement qui
   marche, pour un gain qui est d'abord VISUEL : une case native est petite,
   son état se lit mal d'un coup d'œil, et sur mobile on la vise mal.

   On style donc la case NATIVE. L'élément reste un <input type="checkbox"> :
   `.checked`, `change`, le clavier, les libellés associés, les tests — rien ne
   change côté code. Seul le dessin change, partout à la fois, y compris dans
   le code écrit demain.

   ⚠ `.inter-case` est EXCLUE : c'est la case cachée de ccInterrupteur, qui
   dessine déjà sa propre piste (elle sert aux cas où il faut en plus basculer
   avant la réponse du serveur et revenir en arrière si elle refuse). */
input[type="checkbox"]:not(.inter-case) {
  appearance: none; -webkit-appearance: none;
  flex: none; position: relative; width: 32px; height: 18px; margin: 0 2px 0 0;
  border-radius: 18px; background: var(--line); border: 1px solid var(--line-soft);
  cursor: pointer; transition: background .14s; vertical-align: middle;
}
input[type="checkbox"]:not(.inter-case)::after {
  content: ""; position: absolute; top: 1px; left: 1px; width: 14px; height: 14px;
  border-radius: 50%; background: #fff; box-shadow: var(--ombre);
  transition: transform .14s;
}
input[type="checkbox"]:not(.inter-case):checked { background: var(--accent); }
input[type="checkbox"]:not(.inter-case):checked::after { transform: translateX(14px); }
input[type="checkbox"]:not(.inter-case):disabled { opacity: .45; cursor: default; }
input[type="checkbox"]:not(.inter-case):focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Un interrupteur INDÉTERMINÉ (cocher-tout partiel) : pastille au milieu.
   Aucune case tri-état n'existe aujourd'hui dans le produit ; la règle est là
   pour que la première qui apparaîtra soit dessinée juste du premier coup,
   plutôt qu'en case native au milieu d'interrupteurs. */
input[type="checkbox"]:not(.inter-case):indeterminate { background: var(--ink-3); }
input[type="checkbox"]:not(.inter-case):indeterminate::after { transform: translateX(7px); }
/* Les libellés qui portaient une case sur la même ligne restent alignés. */
label.case, .case { align-items: center; }

/* V8.24 — le champ d'une valeur à copier : monospace, sélectionnable, jamais
   tronqué en silence. */
.copier-zone {
  width: 100%; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12.5px; padding: 7px 8px; border: 1px solid var(--line); border-radius: 4px;
  background: var(--sunk); color: var(--ink); word-break: break-all;
}

/* V8.24 — sélection AU BLOC (plusieurs blocs à la fois). Les navigateurs
   refusent qu'une sélection native traverse deux contenteditable distincts :
   dès que le geste dépasse le bloc de départ, on surligne les blocs entiers.
   Le surlignage doit se lire comme une sélection, pas comme un état de bloc :
   même teinte que la sélection du système, sur toute la largeur. */
.bloc-ligne.bloc-selectionne { background: var(--accent-pale); } /* V8.57 — l'accent à 14 % ne valait que sur du blanc ; le jeton pâle donne la même teinte et suit le thème. */
.blocs-selection-active .bloc-ligne.bloc-selectionne ::selection { background: transparent; }
.blocs-selection-active { caret-color: transparent; }

/* V8.24 — palette de couleur du TEXTE dans le tableur (distincte du fond). */
.tableur-palette { display: flex; flex-wrap: wrap; gap: 5px; max-width: 190px; }
.tableur-palette .bloc-pastille { width: 20px; height: 20px; border-radius: 4px; border: 1px solid var(--line); cursor: pointer; } /* V8.57 — le contour est du châssis ; les couleurs de la palette, elles, sont une donnée et restent en dur. */
.bloc-tableur td a { color: inherit; text-decoration: underline; }
/* ⚠ LA CELLULE EST UN <input>, ET UN <input> N'HÉRITE PAS. Le style est posé
   sur le <td> (c'est lui qui porte la mise en forme de la cellule) ; sans ces
   trois lignes, l'italique et le souligné étaient bien enregistrés et
   parfaitement invisibles — le pire des deux mondes. `text-decoration` ne
   s'hérite jamais d'office : on la reprend explicitement. */
.bloc-tableur td input {
  font: inherit; color: inherit; font-style: inherit; font-weight: inherit;
  text-decoration: inherit; background: transparent;
}

/* V8.25 — cellule de tableur commentée : une pastille dans le coin haut droit,
   comme la marque de commentaire d'un tableur du commerce. Elle ne recouvre pas
   la valeur et ne prend pas le clic de saisie — seul le coin ouvre le fil. */
td.td-commentee { position: relative; }
td.td-commentee::after {
  content: ""; position: absolute; top: 0; right: 0;
  /* ⚠ LA TAILLE DU TRIANGLE DOIT SUIVRE LA ZONE CLIQUABLE (14 px, posée en JS).
     Dessiné à 8 px, il promettait une cible deux fois plus petite que la vraie :
     on cliquait « à côté » du triangle et le fil s'ouvrait quand même. */
  border-width: 0 14px 14px 0; border-style: solid;
  border-color: transparent var(--com) transparent transparent;
  pointer-events: none;
}
/* ⚠ PAS DE GROSSISSEMENT AU SURVOL. Un triangle de 18 px sous un curseur qui
   est, par définition, en survol au moment du clic promettait de nouveau une
   cible plus grande que la zone réelle (14 px) : on cliquait sur le triangle
   sans rien ouvrir. La pastille garde donc sa taille ; c'est la couleur qui
   répond au survol. */
td.td-commentee:hover::after { filter: brightness(1.15); }

/* ============================================================
   V8.26 — POINT 1 : UNE ACTION QU'ON NE PEUT PAS FAIRE
   ------------------------------------------------------------
   L'enveloppe porte l'infobulle (un <button disabled> ne reçoit pas les
   événements de souris sous Chrome : son `title` ne s'afficherait jamais) et
   le curseur « interdit ». Le bouton reste visible, lisible et manifestement
   inopérant : le retirer aurait fait croire à un bug là où il y a une règle.
   ============================================================ */
.action-impossible { display: inline-flex; cursor: not-allowed; }
.action-impossible > .btn:disabled {
  opacity: .45; pointer-events: none;
  /* Le rouge du danger s'éteint aussi : un bouton désactivé qui reste alarmant
     attire l'œil pour rien. */
  filter: grayscale(.6);
}

/* ============================================================
   V8.26 — POINT 5 : BROUILLON LOCAL DE SAISIE
   ------------------------------------------------------------
   Un brouillon qui ressemble à une valeur enregistrée est PIRE que le texte
   perdu : on croit son travail à l'abri. D'où un liseré à gauche de la cellule
   (visible sans être criard, y compris en lecture rapide d'une colonne) et une
   pastille ✎ qui porte l'explication et l'abandon.
   ============================================================ */
:root {
  /* Ocre : ni l'accent du produit (turquoise), ni le privé (violet), ni le
     commentaire (ambre profond), ni le danger (rouge brique). « En cours, pas
     encore posé » — la teinte d'une note au crayon. */
  --brouillon: #b8863a;
  --brouillon-fond: rgba(184, 134, 58, .07);
}
td.cellule-brouillon, td.cellule-brouillon-conflit {
  box-shadow: inset 3px 0 0 var(--brouillon, #b8863a);
  background: var(--brouillon-fond, rgba(184, 134, 58, .07));
}
td.cellule-brouillon-conflit {
  box-shadow: inset 3px 0 0 var(--danger); background: var(--danger-pale);
}
.brouillon-pastille {
  display: inline-block; margin-left: 5px; padding: 0 3px; border-radius: 3px;
  font-size: 11px; line-height: 1.5; cursor: pointer; vertical-align: baseline;
  color: var(--brouillon, #b8863a); background: rgba(184, 134, 58, .16);
  /* La pastille ne doit jamais être coupée par le troncage de la cellule :
     c'est elle qui porte l'information « ce n'est pas enregistré ». */
  white-space: nowrap;
}
.brouillon-pastille:hover { background: rgba(184, 134, 58, .3); }
.brouillon-pastille.conflit { color: var(--danger); background: var(--danger-pale); }
.brouillon-pastille:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Les deux textes du conflit, côte à côte : on ne tranche pas sans les voir. */
.brouillon-diff { border-left: 3px solid var(--line); padding: 4px 0 4px 8px; margin: 8px 0; }
.brouillon-diff.base { border-left-color: var(--ink-3); }
.brouillon-diff.mien { border-left-color: var(--brouillon, #b8863a); }
.brouillon-diff-txt { font-size: 13px; white-space: pre-wrap; word-break: break-word; max-height: 120px; overflow: auto; }

/* ============================================================
   V8.26 — POINT 10 : LA LIGNE « LECTURE » DES MÉTADONNÉES
   ------------------------------------------------------------
   Un PRÉSENT et un PASSANT ne sont pas la même information : le premier peut
   répondre maintenant, le second est un souvenir. L'anneau vert (posé par
   js/presence.js) dit « ici » ; on désature et on adoucit tout le reste, pour
   que la différence se lise d'un coup d'œil et non en survolant chaque rond.
   ============================================================ */
.carte-meta .meta-ligne.meta-lecture { align-items: flex-start; }
.meta-lecture-corps { display: flex; flex-direction: column; gap: 4px; min-height: 24px; }
.meta-lecture-note {
  margin: 0; font-size: 11.5px; line-height: 1.35; color: var(--ink-3); max-width: 34ch;
}
/* Passant : gris, sans halo. La spécificité (deux classes) passe devant la
   règle de base injectée par js/presence.js, qui n'en a qu'une. */
.meta-lecture .presence-rond:not(.presence-rond-ici) { filter: grayscale(1); opacity: .72; }
.meta-lecture .presence-rond:not(.presence-rond-ici):hover { opacity: 1; }
/* Présent : l'anneau vert respire un peu plus que dans les pastilles
   flottantes — ici la ligne est lue posément, pas du coin de l'œil. */
.meta-lecture .presence-rond.presence-rond-ici { box-shadow: 0 0 0 2px var(--card), 0 0 0 3.5px var(--ok); }
.meta-lecture .presence-ronds-vide .presence-ronds-txt { font-style: italic; }

/* V8.57 — même correction que pour le privé (voir plus haut) : ce bloc
   écoutait le SYSTÈME et non le réglage du produit, donc il repeignait le
   brouillon même quand on avait demandé le clair, et se taisait quand on avait
   demandé le sombre sur une machine claire.
   Sur fond sombre l'ocre s'éteint : on l'éclaircit, et le voile de fond passe
   en clair sur sombre plutôt que l'inverse. */
html[data-ui="v2"][data-theme="sombre"] {
  --brouillon: #d9ad63; --brouillon-fond: rgba(217, 173, 99, .12);
}
html[data-ui="v2"][data-theme="sombre"] .brouillon-pastille { background: rgba(217, 173, 99, .18); }
html[data-ui="v2"][data-theme="sombre"] .brouillon-pastille:hover { background: rgba(217, 173, 99, .32); }

/* ============================================================
   V8.26 — POINT 9 : ANNULER / RÉTABLIR (pile locale à l'onglet)
   ------------------------------------------------------------
   Deux boutons dans la barre de la vue, qui PORTENT L'INTITULÉ de ce qu'ils
   vont défaire (« Annuler : Prix de « Chaise » »). D'où deux contraintes de
   dessin qui n'existent pas pour un bouton ordinaire :

   · l'intitulé est long et variable — le bouton ne doit ni faire sauter la
     barre à chaque saisie, ni déborder : largeur plafonnée, texte tronqué en
     « … », phrase complète dans l'infobulle ;
   · désactivé, il doit rester LISIBLE (« Annuler », grisé) plutôt que de
     disparaître : un bouton qui s'efface quand la pile se vide et réapparaît
     à la première saisie fait bouger toute la barre sous la souris.
   ============================================================ */
.annul-groupe { display: inline-flex; gap: 4px; align-self: center; }
.btn.btn-annul {
  max-width: 250px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.btn.btn-annul:disabled { opacity: .45; cursor: not-allowed; }
/* Une BARRIÈRE (geste non annulable en tête de pile) : le bouton reste actif —
   il ouvre l'explication et propose d'aller chercher derrière — mais il ne se
   présente pas comme une annulation ordinaire. Ocre, comme le brouillon : la
   même famille de « ce n'est pas ce que vous croyez ». */
.btn.btn-annul.annul-barriere {
  border-color: var(--brouillon, #b8863a); color: var(--brouillon, #b8863a);
}

/* Les trois valeurs d'un conflit d'annulation, l'une sous l'autre. Même
   grammaire que .brouillon-diff (point 5) : on ne tranche pas sans voir. */
.annul-diff { border-left: 3px solid var(--line); padding: 4px 0 4px 8px; margin: 8px 0; }
.annul-diff.autre { border-left-color: var(--danger); }
.annul-diff.mien { border-left-color: var(--ink-3); }
.annul-diff.cible { border-left-color: var(--accent); }
.annul-diff-txt { font-size: 13px; white-space: pre-wrap; word-break: break-word; max-height: 110px; overflow: auto; }

/* RETOUR VISUEL SUR CE QUI A ÉTÉ RESTAURÉ.
   Une annulation au clavier sur un tableau de cinquante lignes est, sans ça,
   un changement strictement invisible : on ne sait pas ce qui a bougé, donc on
   ne sait pas si ça a marché. Le surlignage est bref (1,6 s) et s'éteint tout
   seul — il signale, il ne décore pas.
   `box-shadow` plutôt que `background` : le fond des cellules porte déjà le
   brouillon, le commentaire et la sélection ; on ne veut pas les écraser. */
@keyframes cc-annul-flash {
  0%   { box-shadow: inset 0 0 0 2px var(--accent); background: var(--accent-pale); }
  70%  { box-shadow: inset 0 0 0 2px var(--accent); background: var(--accent-pale); }
  100% { box-shadow: inset 0 0 0 2px transparent; background: transparent; }
}
.cc-annul-flash { animation: cc-annul-flash 1.6s ease-out 1; }
/* Une carte de kanban n'est pas une cellule : le liseré fait le tour. */
.kanban-carte.cc-annul-flash { border-radius: var(--r); }
/* ⚠ V9.26 — ET UNE LIGNE ENTIÈRE N'EST NI L'UN NI L'AUTRE. Un `box-shadow`
   posé sur un <tr> ne se peint pas de façon fiable quand la table est en
   `border-collapse` : la classe était bien ajoutée, l'animation bien lancée,
   et l'on ne voyait rien. Ce sont donc les CELLULES de la ligne qui jouent
   l'animation — le même keyframes, pas un second. (Le sélecteur `tr[data-fid]`
   d'`ccAnnulSurligner` n'ayant jamais rien trouvé jusqu'à cette version, ce
   défaut-là n'avait encore jamais eu l'occasion de se voir.) */
tr.cc-annul-flash > td { animation: cc-annul-flash 1.6s ease-out 1; }
/* ⚠ `prefers-reduced-motion` coupe déjà toutes les animations (règle globale du
   haut de fichier) : sans repli, la personne qui l'a activé n'aurait AUCUN
   retour visuel. On lui laisse donc un surlignage fixe, retiré par le même
   minuteur JS. */
@media (prefers-reduced-motion: reduce) {
  .cc-annul-flash { box-shadow: inset 0 0 0 2px var(--accent) !important; background: var(--accent-pale) !important; }
  /* V9.26 — et la ligne entière suit la même règle, par ses cellules. */
  tr.cc-annul-flash > td { background: var(--accent-pale) !important; }
}

/* -- V8.52 : les réglages du compte, dans « Mon profil » -- */
.cc-compte-email {
  font-family: var(--mono); font-size: 12px; color: var(--ink-2);
  margin: 0 0 10px; display: flex; align-items: center; gap: 5px;
}
.cc-compte-reglages > label {
  display: block; font-size: 12px; font-weight: 600; color: var(--ink-2); margin: 12px 0 4px;
}
.cc-compte-reglages > label:first-child { margin-top: 0; }
.cc-compte-reglages .champ-mdp { max-width: 320px; }
.cc-compte-langue {
  margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--line-soft);
}
/* ⚠ `.cc-compte-reglages > label` NE L'ATTRAPE PAS : ce libellé-ci descend de
   `.cc-compte-langue`, il n'est pas enfant direct du bloc. Sans cette ligne,
   « 🌐 Langue » restait `display:inline`, collé à son menu déroulant — le
   défaut exact que la V8.51 vient de corriger dans les modales, reproduit une
   heure plus tard par un sélecteur en enfant direct posé trop vite. */
.cc-compte-langue > label {
  display: block; font-size: 12px; font-weight: 600; color: var(--ink-2); margin: 0 0 4px;
}
.cc-compte-reglages select { max-width: 320px; }
/* Le trait du menu du compte : deux gestes irréversibles en dessous. */
.cc-menu-trait { border: 0; border-top: 1px solid var(--line-soft); margin: 10px 0 2px; }

/* ============================================================
   V8.56 — LA PASTILLE D'ÉTAT, EN CSS ET NON EN ÉMOJI.
   ------------------------------------------------------------
   « 🟢 en ligne », « ⚪ dépubliée », « 🔴 critique » : trois ronds de couleur
   posés dans une chaîne de caractères. Ils avaient trois défauts, et le
   troisième est celui qui a décidé :

     1. Ils ne sont pas dessinés par nous. 🟢 est un glyphe de la police
        système : vert pomme sur un Mac, vert sapin sur Android, cerclé de
        noir sur certains Windows. Aucun rapport avec `--ok` ni avec le reste
        de l'écran.
     2. Ils ne suivent pas la taille du texte, ni son alignement : un émoji
        rond posé sur une ligne de 12 px déborde vers le bas.
     3. ILS NE CONNAISSENT PAS LE THÈME SOMBRE. Un ⚪ « éteint » disparaît sur
        fond marine ; un 🟢 y devient fluorescent. Une valeur d'état doit
        pâlir et s'assombrir avec le reste, et seule la CSS sait le faire.

   Ce n'est pas une icône non plus — un rond n'est pas un pictogramme, c'est
   une VALEUR. D'où une classe, et non un dessin de plus dans icones.js :

       el('span', { class: 'cc-pastille ok' }, 'en ligne')

   Variantes : `ok` (actif, en ligne), `eteint` (dépublié, épuisé), `alerte`
   (attention), `danger` (critique), `neutre` (par défaut si rien n'est posé).
   ============================================================ */
.cc-pastille { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.cc-pastille::before {
  content: ''; flex: none; width: 8px; height: 8px; border-radius: 50%;
  background: var(--ink-3);
  /* Un anneau très pâle décolle la pastille du fond : sans lui, le point
     « éteint » (gris clair sur blanc) devient invisible — exactement le
     défaut qu'on reproche au ⚪. */
  box-shadow: 0 0 0 2px color-mix(in srgb, currentColor 12%, transparent);
}
.cc-pastille.ok::before { background: var(--ok); }
.cc-pastille.eteint::before { background: var(--ink-3); opacity: .55; }
.cc-pastille.alerte::before { background: var(--warn, #b8860b); }
.cc-pastille.danger::before { background: var(--danger); }

/* ============================================================
   V8.57 — LE BOUTON DE THÈME DU BANDEAU
   ------------------------------------------------------------
   Il vit sur le bandeau, pas sur le papier : ses couleurs sont donc celles du
   bandeau (`--topbar-*`), qui restent foncées dans les deux thèmes. C'est
   d'ailleurs la seule raison pour laquelle il n'a pas eu besoin d'être repensé
   pour le sombre — un bouton posé sur une surface qui ne s'inverse pas ne
   s'inverse pas non plus.
   ============================================================ */
.btn-theme {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; flex: none;
  background: none; border: 1px solid transparent; border-radius: var(--r);
  color: var(--topbar-attenue); padding: 0;
}
.btn-theme:hover:not(:disabled) { color: var(--topbar-txt); border-color: var(--topbar-ligne); }
/* ⚠ DÉSACTIVÉ = GRISÉ, PAS EFFACÉ. En interface v1 le thème sombre n'existe
   pas et le bouton est inerte ; le faire DISPARAÎTRE laisserait croire que le
   réglage n'existe pas du tout, alors qu'il est à un Ctrl+Alt+D. Il reste
   visible, éteint, et son `title` dit quoi faire. */
.btn-theme:disabled { opacity: .4; cursor: not-allowed; }

/* Quand le thème SUIT LE SYSTÈME, un point discret sous l'icône. Sans lui,
   « clair parce que je l'ai demandé » et « clair parce que ma machine l'est »
   sont indiscernables — et ce sont deux réglages différents, dont un seul
   basculera tout seul ce soir. */
.btn-theme.suit-systeme { position: relative; }
.btn-theme.suit-systeme::after {
  content: ''; position: absolute; bottom: 3px; left: 50%; transform: translateX(-50%);
  width: 4px; height: 4px; border-radius: 50%; background: currentColor; opacity: .7;
}

.cc-theme-pop .cc-menu-item.actif { color: var(--accent-fonce); font-weight: 700; }
.cc-theme-pop .cc-menu-item small { display: block; font-weight: 400; color: var(--ink-3); font-size: 11.5px; }
.cc-theme-pop .cc-menu-item strong { font-weight: inherit; }


/* ============================================================
   V8.57 — LES TROIS THÈMES EN UNE RANGÉE (menu du bandeau étroit)
   Trois boutons plutôt qu'un sous-menu : au doigt, un menu qui ouvre un menu
   est un menu qu'on referme. Ils se serrent sur trois colonnes égales, ce qui
   les rend cliquables sans viser.
   ============================================================ */
.cc-theme-rang { display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px; }
.cc-theme-rang .btn { justify-content: center; padding: 6px 4px; font-size: 12px; gap: 4px; }

/* ============================================================
   V9.6 — FENÊTRE PAYWALL ET PASTILLES DE QUOTA

   ⚠ CE BLOC RÉPARE UNE FENÊTRE QUI NE S'AFFICHAIT PAS. `afficherPaywall()`
   (js/api.js) posait `class="modal-fond"` depuis la V4.5. Cette classe n'a
   jamais existé ici : le voile des modales du produit s'appelle
   `.modal-backdrop` (ligne 1218). Résultat, la boîte tombait dans le flux
   normal en bas du <body>, sans position fixe ni z-index — invisible. Le JS
   utilise désormais les classes communes ; il ne reste à habiller que ce qui
   est propre au paywall.
   ============================================================ */

/* Un peu plus large qu'une modale de saisie : elle porte un texte, une jauge
   et deux boutons, et on ne veut pas que la phrase se casse en cinq lignes. */
.paywall-modal { max-width: 480px; }
.paywall-modal .modal-tete h3 { display: flex; align-items: center; gap: 6px; }
.paywall-modal .modal-corps > p { margin: 0 0 12px; font-size: 13.5px; line-height: 1.5; }
.paywall-modal .actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 14px; }

/* La jauge reprend TRAIT POUR TRAIT celle de l'onglet Abonnement
   (js/equipe.js, styles en ligne). C'est volontaire : le client doit
   reconnaître au premier coup d'œil le chiffre qu'il retrouvera là-bas, et
   comprendre que les deux écrans parlent de la même chose. Le rouge est le
   même `#a03d2c` — ici il est toujours à 100 %, puisqu'on n'ouvre cette
   fenêtre que lorsque la limite est atteinte. */
.paywall-jauge { border: 1px solid var(--line); border-radius: 8px; padding: 8px 10px; margin: 0 0 12px; }
.paywall-jauge-tete { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; font-size: 12px; }
.paywall-jauge-tete strong { font-family: var(--mono); font-size: 12.5px; }
.paywall-jauge-piste { height: 5px; border-radius: 3px; background: var(--sunk); margin-top: 6px; overflow: hidden; }
/* ⚠ `var(--danger)` et non le `#a03d2c` en dur de l'onglet Abonnement. Ce rouge
   sombre est lisible sur le fond clair du thème par défaut ; sur `--sunk`
   version sombre (#1a2229) il disparaît presque. Le jeton, lui, vaut #e2907c
   en thème sombre — c'est tout l'intérêt d'en avoir un. La jauge de l'onglet
   Abonnement, dessinée en styles EN LIGNE, garde son défaut : la corriger
   demande de sortir ses styles du JavaScript, ce qui n'est pas ce chantier. */
.paywall-jauge-barre { height: 100%; background: var(--danger); }

/* ------------------------------------------------------------
   PASTILLE « IL VOUS RESTE N » — l'avertissement AVANT le mur.

   Elle vit à côté des boutons de création (collection, fiche, utilisateur).
   Discrète tant qu'il reste de la marge, ambrée quand on approche, rouge
   quand c'est plein. Elle n'est JAMAIS un blocage : le bouton reste cliquable
   et c'est le serveur qui refuse — une seule autorité, et pas deux avis qui
   peuvent diverger. Voir ccPastilleQuota(), js/api.js.
   ------------------------------------------------------------ */
/* ⚠ `white-space: normal` et une largeur souple : la barre latérale fait
   292 px moins ses marges, et « COLLECTIONS » + « + racine » + « Il reste 25
   sur 250 » n'y tient pas sur une ligne. En `nowrap`, la pastille poussait
   l'eyebrow hors du cadre. Elle se replie donc plutôt que de déborder. */
.cc-quota-pastille {
  display: inline-flex; align-items: center; gap: 4px; vertical-align: middle;
  margin-left: 6px; padding: 1px 7px; border-radius: 999px;
  font-size: 11px; line-height: 1.4; white-space: normal; text-align: center;
  color: var(--ink-2); background: var(--sunk); border: 1px solid var(--line-soft);
}
.cc-quota-pastille.proche { color: var(--warn); background: var(--warn-pale); border-color: var(--warn); }
.cc-quota-pastille.pleine { color: var(--danger); background: var(--danger-pale); border-color: var(--danger); font-weight: 600; }

/* ============================================================
   V9.7 — JEUX D'HYPOTHÈSES ET SUIVI DANS LE TEMPS
   ============================================================ */

/* Les jeux d'hypothèses, en tête du bandeau ⚗. Volontairement dessinés comme
   la zone des vues nommées : c'est le même geste, il doit se reconnaître. */
.hyp-zone { padding: 4px 0 8px; border-bottom: 1px solid var(--line-soft); margin-bottom: 8px; }
.hyp-titre { display: block; font-size: 12px; color: var(--ink-2); margin-bottom: 4px; }

/* La cellule d'une colonne « série » : la valeur retenue, et ce qui dit qu'il
   y en a d'autres derrière. Sans ce second signe, une série de trente relevés
   ressemble à une cellule ordinaire — et personne ne clique. */
.serie-cellule { display: inline-flex; align-items: baseline; gap: 5px; cursor: pointer; }
.serie-valeur { font-variant-numeric: tabular-nums; }
.serie-date { font-size: 11px; color: var(--ink-3); font-variant-numeric: tabular-nums; }
.serie-nb {
  font-size: 10.5px; line-height: 1.5; padding: 0 5px; border-radius: 999px;
  color: var(--ink-2); background: var(--sunk); border: 1px solid var(--line-soft);
}
.serie-cellule:hover .serie-nb { border-color: var(--accent); color: var(--accent); }
.serie-vide { color: var(--ink-3); font-style: italic; }

/* Le popover des relevés : une ligne par date, la plus récente en haut. */
.serie-liste { display: flex; flex-direction: column; gap: 3px; max-height: 46vh; overflow-y: auto; margin-bottom: 8px; }
.serie-ligne { display: flex; align-items: center; gap: 6px; font-size: 13px; }
.serie-ligne input[type="date"] { width: 132px; }
.serie-ligne .serie-v { flex: 1; min-width: 70px; }
.serie-ligne.retenue { font-weight: 600; }
.serie-ligne.retenue .serie-marque { color: var(--accent); }
.serie-marque { width: 12px; flex: none; font-size: 11px; }
