/* =========================================================================
   Design system « verre dosé » · intranet Romain Gaboriaud
   Calé sur la maquette tableau_de_bord_verre.html : compact, sobre, halos,
   verre sur le chrome / opacité franche sur les données. Icônes = SVG Lucide.
   ========================================================================= */

/* -----------------------------------------------------------------------
   Polices AUTO-HÉBERGÉES. Aucun appel à fonts.googleapis.com / gstatic.com :
   plus aucune IP de visiteur n'est envoyée à Google. Fichiers variables
   (woff2) dans static/fonts/rg/, sous-ensembles latin et latin-ext.
   ----------------------------------------------------------------------- */
@font-face{font-family:"Inter";font-style:normal;font-weight:100 900;font-display:swap;
  src:url("fonts/rg/Inter-latin-ext.01ba6c2a184b.woff2") format("woff2");
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
@font-face{font-family:"Inter";font-style:normal;font-weight:100 900;font-display:swap;
  src:url("fonts/rg/Inter-latin.65850a373e25.woff2") format("woff2");
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
/* Fraunces / Newsreader (serif) retirées : l'intranet est tout en sans-serif. */
:root{
  color-scheme: dark;

  /* Accent NEUTRE chaud (taupe/sable). Ne s'applique QU'AUX éléments actifs.
     (Le nom --teal* est conservé en interne = jeton d'accent unique du système.) */
  --teal:#c9bdaa; --teal-bright:#e0d6c6; --teal-fg:#e0d6c6; --teal-ink:#1a1610;
  --teal-bg:rgba(201,189,170,.10);
  /* La couleur ne sert qu'à SIGNALER : vert=validé/ok, ambre=en attente, doré=partagé. */
  --amber:#d9b66a; --blue:#5ca8f0; --red:#f08a8a; --ok:#6fbf8a; --gold:#c9a86a;
  /* AMBRE D'ENCRE. `--amber` est réglé pour un fond d'encre : posé en TEXTE sur
     le thème clair il tombe à 1,57:1 sur sa propre pastille, illisible. On ne
     peut pas simplement l'assombrir, il sert aussi de bordure (21 emplois) et
     d'aplat (10) : ces aplats-là s'inverseraient. D'où un jeton séparé, sur le
     modèle de --teal-fg, à employer pour tout TEXTE ambre. */
  --amber-fg:#d9b66a;
  /* Même raisonnement pour le ROUGE et l'OR, mesuré : `--red` (#f08a8a) tombe à
     2,41:1 sur le blanc du thème clair, `--gold` à 2,0. Ils servent tous deux
     d'aplat et de bordure ailleurs, on ne peut donc pas les assombrir en place.
     À employer pour tout TEXTE rouge ou or. */
  --red-fg:#f08a8a; --gold-fg:#c9a86a;
  --ok-fg:#6fbf8a;
  --amber-bg:rgba(217,182,106,.13); --blue-bg:rgba(92,168,240,.13);
  --red-bg:rgba(240,138,138,.12); --ok-bg:rgba(111,191,138,.13); --gold-bg:rgba(201,168,106,.12);
  /* FILET DE SÉCURITÉ. Ces quatre-là n'existaient QUE dans les deux blocs de
     thème ci-dessous. Hors de leur portée, `var(--solid)` ne résout rien : la
     déclaration devient invalide, la propriété est ABANDONNÉE, et une couleur
     de texte retombe alors sur celle héritée du parent. C'est ainsi qu'un
     libellé blanc sur un aplat sombre redevenait sombre sur sombre, illisible,
     sans qu'aucune règle ne paraisse en cause. Les valeurs reprennent la
     palette sombre, comme le reste de ce bloc. */
  --solid:#1d1c25; --chip:#24222e; --ink:#f2f0f6; --muted:#a49dba;
  --radius:13px; --radius-sm:9px; --pill:999px;
  --ctl-h:36px;                         /* hauteur unique des champs/boutons */
  --side-w:228px;
  /* DEUX ÉTAGES depuis le 16/09/2026 · les six menus transverses demandent
     770 px et se faisaient rogner sur la même ligne que les jauges.
     `--top-h` reste LE TOTAL : douze règles s'y calent pour coller sous
     l'en-tête (en-têtes de tableau, colonne d'indicateurs, panneau des
     rappels, messages flottants, espaces pleine hauteur). En faire la
     hauteur d'un seul étage les décalerait toutes de 42 px, sans erreur
     visible ailleurs que dans le rendu. */
  --top-h1:52px; --top-h2:42px;
  /* LA MARGE HAUTE DE L'APPAREIL (encoche, barre d'état) entre DANS le total,
     et c'est ce qui fait que les douze règles calées sur « --top-h » suivent
     sans être touchées une à une : en-têtes de tableau collants, colonne
     d'indicateurs, panneau des rappels, messages flottants, espaces pleine
     hauteur. Sur un écran sans marge - tout le bureau - « env() » rend zéro et
     rien ne bouge. */
  --safe-haut:env(safe-area-inset-top, 0px);
  --safe-bas:env(safe-area-inset-bottom, 0px);
  /* DE L'AIR SOUS LA BARRE D'ÉTAT · la marge de l'appareil suffit à ne pas
     passer DESSOUS, elle ne suffit pas à s'en détacher : collé à elle, le
     contenu de l'en-tête se fait encore mordre par le flou d'iOS (relevé par
     Romain le 19/09/2026). Zéro par défaut - sur un écran d'ordinateur, il n'y
     a ni encoche ni flou à éviter, et huit pixels de barre en plus ne se
     justifieraient pas. Le mode téléphone la relève, plus bas. */
  --marge-barre:0px;
  /* Les marges LATÉRALES ne valent zéro qu'en portrait : couché, l'encoche
     mange un bord et le repère d'accueil l'autre. */
  --safe-gauche:env(safe-area-inset-left, 0px);
  --safe-droite:env(safe-area-inset-right, 0px);
  --top-h:calc(var(--top-h1) + var(--top-h2) + var(--safe-haut) + var(--marge-barre));
  --font:"Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
  /* Teintes de FAMILLE des tuiles (Production, Documents, Outils…). Elles
     étaient écrites en dur avec des rgba() calculés pour un fond charbon : sur
     un fond clair elles viraient au rose délavé, et ne suivaient donc aucun
     thème. Passées en jetons, redéfinis plus bas pour le mode clair. */
  /* FRANCHES, et c'est tout l'objet du 06/09/2026. Sourdes, elles étaient
     réglées pour ne pas concurrencer un laiton lui-même désaturé : sur la page
     « Outils », sept tuiles censées porter cinq familles différentes rendaient
     sept rectangles bruns identiques. Une teinte de famille qui ne distingue
     rien ne sert à rien. Écartées en TON et en VALEUR, comme leurs équivalents
     du thème clair.

     `--tu-teal` ne renvoie plus à `--teal-bg` : la famille reprenait alors la
     couleur de l'ACCENT, si bien qu'une tuile ordinaire se lisait comme un
     élément actif. */
  --tu-terracotta:#c4877f; --tu-sable:#9d90a9; --tu-sauge:#7fa389;
  --tu-teal:#8199b0;       --tu-gold:#bf9a68;
  --tu-terracotta-bg:var(--glass);
  --tu-sable-bg:var(--glass);
  --tu-sauge-bg:var(--glass);
  --tu-teal-bg:var(--glass);
  --tu-gold-bg:var(--glass);
  /* ── COUCHE D'INTERACTION ────────────────────────────────────────────────
     Le rythme, la profondeur et l'anneau de focus. Aucune couleur nouvelle :
     tout se déduit de l'accent et du thème. Voir le bloc en fin de fichier.

     Les durées étaient tirées au hasard : NEUF valeurs différentes pour 25
     transitions, et treize d'entre elles sans aucune courbe. Trois durées
     suffisent, et une seule courbe pour presque tout. */
  --sortie:cubic-bezier(.2,.7,.3,1);   /* part vite, arrive doucement */
  --ressort:cubic-bezier(.2,.9,.25,1.06); /* DÉPASSE la cible, puis revient */
  --t-doigt:110ms;                     /* le retour sous le doigt */
  --t:190ms;                           /* le survol */
  --t-carte:320ms;                     /* le soulèvement d'une carte */
  --t-lent:300ms;                      /* une apparition */
  /* L'échelle d'ombres, redéfinie par thème plus bas : une ombre d'encre n'est
     pas une ombre de craie. Elles étaient écrites en dur, calées sur le sombre. */
  --ombre-1:0 1px 2px rgba(0,0,0,.22), 0 2px 6px rgba(0,0,0,.14);
  --ombre-2:0 2px 4px rgba(0,0,0,.20), 0 10px 26px rgba(0,0,0,.26);
  --ombre-creuse:inset 0 1px 2px rgba(0,0,0,.30);
  --anneau:0 0 0 3px color-mix(in srgb, var(--teal) 42%, transparent);
  /* LA LUEUR QUI TRAVERSE · le reflet qui balaie un bouton au survol.
     Plus discrète sur l'encre : un blanc à .26 y ferait un éclair. */
  --lueur:rgba(255,255,255,.2);
  /* Employé par .minitile:hover depuis toujours, défini nulle part : la
     déclaration était invalide et la bordure ne changeait pas. */
  --tile:var(--teal);
  /* ── L'ÉCHELLE DES TABLEAUX ───────────────────────────────────────────────
     Mesuré sur les 25 pages de menu qui portent un tableau : la taille de base
     des cellules valait 12, 12,5, 13 OU 13,5 px selon la page - QUATRE valeurs
     pour la même chose - et les puces 10 ou 11,5. Le socle disait 13 px, et
     une page sur deux le redéclarait à 12,5 dans son propre style.
     Trois échelons, et un seul endroit pour en changer. */
  --t-tab:12.5px;      /* le texte d'une cellule */
  --t-puce:11px;       /* une puce, une étiquette, un état */
  --t-micro:10.5px;    /* un en-tête de colonne, une légende */
}
html[data-theme="sombre"]{
  color-scheme: dark;

  /* « ATELIER ». L'encre tire vers le VIOLET · c'est la teinte que Romain avait
     retenue le 04/09/2026, et qu'il a redemandée le 06/09. Le laiton, lui, ne
     bouge pas : c'est l'indigo qui avait été rejeté, pas le fond.

     Le changement de fond est MINCE à l'œil — #131110 contre #141318, deux
     encres presque aussi sombres. Ce qu'on venait chercher n'est pas là : c'est
     que sur une encre neutre-froide, les teintes de famille des tuiles peuvent
     enfin être FRANCHES sans jurer avec le fond. Sur le brun, elles devaient
     rester sourdes pour ne pas hurler, et sept tuiles se ressemblaient toutes.

     Le laiton reste RÉSERVÉ aux éléments actifs : il ne teinte jamais une
     surface, c'est ce qui salissait. */
  --bg:#16150f;
  /* LE VRAI FOND DE LA PAGE, et il était écrit EN DUR. Ces trois jetons
     existaient sans que rien ne les lise : le vignettage du body portait cinq
     bruns en clair, si bien que changer `--bg` ne changeait presque rien à
     l'écran. C'est exactement le piège de app.css — cinq jetons modifiés, mille
     déclarations en dur inchangées. Le dégradé les lit désormais.
     Cinq paliers : le centre éclairé, puis l'encre, puis le vignettage. */
  --halo1:#201f16; --halo2:#1b1a12; --halo3:#16150f;
  --halo4:#110f0b; --halo5:#0b0a07;
  --glass:#1c1a14; --glass-2:#211f18; --glass-line:rgba(127,163,137,.16);
  --solid:#1c1a14; --solid-2:#19170f; --panel-2:#1c1a14;
  /* Surface opaque derrière un contenu inséré : cadre d'éditeur, iframe de
     messagerie, vignette en attente d'image, panneau flottant. Le jeton était
     EMPLOYÉ dans huit gabarits mais défini nulle part : « background:var(--data) »
     ne rendait donc aucun fond, et les panneaux flottants laissaient voir le
     contenu au travers. */
  --data:#1c1a14;
  --line:#2a2720; --hair:rgba(127,163,137,.12);
  /* Les gris suivent l'encre : chauds sur un fond brun, ils viraient au jaune
     sale sur une encre froide. Le contraste est conservé (#f2f0f6 sur #141318
     tient à 16,4:1, contre 16,1:1 auparavant). */
  --ink:#ece8df; --muted:#918b80; --soft:#c3bdb0; --dim:#6f6a62;
  --teal:#7fa389; --teal-bright:#93b49b; --teal-fg:#8fb097; --teal-ink:#12200f;
  --teal-bg:rgba(127,163,137,.14);
  --chip:#211f18; --shadow:none;
  --radius:12px; --radius-sm:9px;
  --ctl-bg:rgba(0,0,0,.22);
  /* Les états d'argent, code couleur de Romain : rouge = rien n'est parti et
     il y a un geste à faire · orange = c'est parti, on attend · vert = c'est
     arrivé · bleu = ça n'existe pas encore vraiment · gris = ça n'existera pas.
     Ici plutôt que dans une page : ils servent au tableau de bord ET à la page
     Factures, et un jeton défini dans une seule page retombe silencieusement
     sur la couleur du texte partout ailleurs. Le même mot doit garder la même
     teinte d'un écran à l'autre. */
  --fr-red:#c4877f; --fr-orange:#bf9a68; --fr-green:#7fa389;
  --fr-bleu:#8199b0; --fr-gris:#8d877c;
}
html[data-theme="clair"]{
  /* CE QUE LE NAVIGATEUR PEINT LUI-MÊME · listes déroulantes de
     `datalist`, ascenseurs, sélecteurs, curseur de saisie. Sans ce
     réglage il suit le mode du SYSTÈME, pas celui de la page : sur le
     Mac de Romain, en sombre, les suggestions d'un `datalist`
     s'écrivaient en BLANC sur le fond clair de « Craie », donc
     illisibles (signalé le 26/08/2026). */
  color-scheme: light;

  /* « Craie » : gris très clair légèrement chaud, cartes blanches SANS bordure
     détachées par une ombre douce, angles généreux. Le texte reste anthracite
     (#15140F) : le confort visuel ne coûte rien au contraste, qui était le
     reproche fait à l'ancien mode clair bleuté. Accent achromatique. */
  --bg:#f4f2ec;
  --halo1:#efede6; --halo2:#f2f0ea; --halo3:#f6f4ef;
  --halo4:#f9f8f4; --halo5:#fbfaf8;
  --glass:#ffffff; --glass-2:#f7f5f0; --glass-line:rgba(33,31,28,.07);
  --solid:#ffffff; --solid-2:#f7f5f0; --panel-2:#f7f5f0;
  --data:#ffffff;
  --line:#e6e2da; --hair:rgba(33,31,28,.06);
  --ink:#211f1c; --muted:#6f6a63; --soft:#3a3630; --dim:#948d84;
  --teal:#4f7059; --teal-bright:#5c8166; --teal-fg:#42604b; --teal-ink:#ffffff;
  --teal-bg:rgba(79,112,89,.12);
  /* Ambre d'encre pour « Craie » : même famille (teinte 43° contre 41°), assez
     foncé pour se lire. Mesuré : 5,05:1 sur la pastille ambrée, 5,37:1 sur le
     fond de page, 6,24:1 sur une carte blanche. `--amber` lui-même NE CHANGE
     PAS : il reste la couleur des aplats et des bordures. */
  --amber-fg:#8a6a42;
  --red-fg:#8f5750; --gold-fg:#8a6a42;
  /* --red et --red-bg MANQUAIENT ici : faute de valeur claire, ils
     retombaient sur celle du thème sombre - un rose pâle - et les boutons
     « danger » du thème clair étaient donc peints avec une couleur de nuit.
     Constaté le 25/08/2026. */
  --red:#8f5750; --red-bg:rgba(143,87,80,.10);
  /* --ok et --ok-bg manquaient de la même façon, et la pastille « terminée »
     du thème clair sortait donc en vert menthe de nuit sur fond blanc. Le
     `--ok-fg` clair existait déjà : c'est sa valeur qu'on reprend, pour que
     l'aplat et le texte soient enfin de la même famille. */
  --ok:#4f7059; --ok-bg:rgba(79,112,89,.11);
  --ok-fg:#4f7059;
  --chip:#f2f0ea; --shadow:0 2px 4px rgba(30,28,24,.04),0 14px 32px -18px rgba(30,28,24,.22);
  --radius:12px; --radius-sm:9px;
  --ctl-bg:#ffffff;
  --lueur:rgba(255,255,255,.3);
  /* Tuiles : aucun aplat, la couleur passe dans la pastille d'icône. Les cinq
     teintes sont écartées en TON et en VALEUR · sable était auparavant un
     second ocre indistinguable de gold, ce qui aplatissait toute la grille. */
  --tu-terracotta:#8f5750; --tu-gold:#8a6a42; --tu-sauge:#4f7059;
  --tu-sable:#665a72;      --tu-teal:#4a6076;
  --tu-terracotta-bg:var(--glass); --tu-sable-bg:var(--glass);
  --tu-sauge-bg:var(--glass);      --tu-teal-bg:var(--glass);
  --tu-gold-bg:var(--glass);
  /* Les états d'argent, code couleur de Romain : rouge = rien n'est parti et
     il y a un geste à faire · orange = c'est parti, on attend · vert = c'est
     arrivé · bleu = ça n'existe pas encore vraiment · gris = ça n'existera pas.
     Ici plutôt que dans une page : ils servent au tableau de bord ET à la page
     Factures, et un jeton défini dans une seule page retombe silencieusement
     sur la couleur du texte partout ailleurs. Le même mot doit garder la même
     teinte d'un écran à l'autre. */
  --fr-red:#8f5750; --fr-orange:#8a6a42; --fr-green:#4f7059;
  --fr-bleu:#4a6076; --fr-gris:#7e786e;
  /* Craie : les ombres de l'encre y feraient des taches. Plus courtes, moins
     opaques, et teintées du gris chaud du fond plutôt que du noir. */
  --ombre-1:0 1px 2px rgba(38,36,31,.09), 0 2px 6px rgba(38,36,31,.07);
  --ombre-2:0 2px 4px rgba(38,36,31,.10), 0 10px 26px rgba(38,36,31,.13);
  --ombre-creuse:inset 0 1px 2px rgba(38,36,31,.16);
}
*{box-sizing:border-box;margin:0;padding:0}
html{-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}
body{
  color:var(--ink); font-family:var(--font); font-size:14px; line-height:1.5;
  font-variant-numeric:tabular-nums; min-height:100vh; background:var(--bg);
}
/* Mode sombre : vignettage CENTRÉ monochrome + lueur blanche discrète au milieu.
   Une seule nappe, une seule teinte : deux lumières de couleurs différentes qui
   se recouvrent produisent toujours une zone trouble (c'était le défaut de la
   première version, trois nappes teintées posées hors centre). Le blanc éclaire
   sans teinter · un jaune dilué sur fond brun vire au kaki. Attaché à la
   fenêtre : le halo éclaire, il ne défile pas. */
html[data-theme="sombre"] body{
  /* Interpolation en OKLAB : plus régulière perceptivement que le sRGB, qui
     traverse une zone morte sur les bruns désaturés.
     Couche 1 = TRAMAGE. Un dégradé aussi étalé ne dispose que d'une douzaine de
     niveaux sur 8 bits par canal : les paliers atteignaient 34 px de large et se
     lisaient comme des anneaux. Le bruit fractal est comprimé dans [0,36;0,64],
     donc centré sur 0,5 : sous « soft-light » une source à 0,5 laisse le fond
     STRICTEMENT inchangé, seul l'écart module (±1 à 2 valeurs). Mesuré : paliers
     34 px → 5 px, 12 → 21 niveaux distincts, couleurs identiques au pixel près.
     NE PAS monter baseFrequency à 1.0 : le bruit de Perlin s'annule sur les
     nœuds entiers de son réseau et la couche devient un gris uni sans effet. */
  background:
    url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%27240%27%20height%3D%27240%27%3E%3Cfilter%20id%3D%27n%27%20color-interpolation-filters%3D%27sRGB%27%3E%3CfeTurbulence%20type%3D%27fractalNoise%27%20baseFrequency%3D%27.75%27%20numOctaves%3D%273%27%20stitchTiles%3D%27stitch%27%2F%3E%3CfeColorMatrix%20type%3D%27saturate%27%20values%3D%270%27%2F%3E%3CfeComponentTransfer%3E%3CfeFuncR%20type%3D%27linear%27%20slope%3D%270.28%27%20intercept%3D%270.36%27%2F%3E%3CfeFuncG%20type%3D%27linear%27%20slope%3D%270.28%27%20intercept%3D%270.36%27%2F%3E%3CfeFuncB%20type%3D%27linear%27%20slope%3D%270.28%27%20intercept%3D%270.36%27%2F%3E%3CfeFuncA%20type%3D%27discrete%27%20tableValues%3D%271%27%2F%3E%3C%2FfeComponentTransfer%3E%3C%2Ffilter%3E%3Crect%20width%3D%27240%27%20height%3D%27240%27%20filter%3D%27url%28%23n%29%27%2F%3E%3C%2Fsvg%3E"),
    radial-gradient(ellipse 72% 58% at 50% 40% in oklab, rgba(255,255,255,.05), transparent 100%),
    radial-gradient(ellipse 105% 92% at 50% 44% in oklab,
      var(--halo1) 0%, var(--halo2) 38%, var(--halo3) 66%,
      var(--halo4) 88%, var(--halo5) 100%);
  background-size:240px 240px, auto, auto;
  background-repeat:repeat, no-repeat, no-repeat;
  background-blend-mode:soft-light, normal, normal;
  background-attachment:fixed, fixed, fixed;
}
/* Titres : la MÊME sans-serif que le reste du texte (pas de serif). */
h1,h2,h3{
  font-family:var(--font);font-weight:650;letter-spacing:-.015em}
/* Mode clair : cartes sans bordure, l'ombre douce suffit à les détacher.
   `.carte-douce` est le MARQUEUR à poser sur une carte définie dans une page.
   Sans lui, ce traitement ne profitait qu'aux quatre classes du socle : une
   carte maison restait blanche sur le sable, séparée par un filet à 6 %
   d'opacité, soit 1,17:1 d'écart de luminance. On lisait ça comme un fond de
   page manquant, alors que le fond était bien là et que c'était la carte qui
   ne se détachait pas. Constaté sur les tuiles de Veille le 29/07/2026. */
html[data-theme="clair"] .panel,html[data-theme="clair"] .tool,
html[data-theme="clair"] .tile,html[data-theme="clair"] .dash-tuile,
html[data-theme="clair"] .carte-douce,html[data-theme="clair"] .iw{
  border:0;box-shadow:var(--shadow)}
/* Mode sombre : l'inverse, filet net et aucune ombre. */
html[data-theme="sombre"] .panel,html[data-theme="sombre"] .tool,
html[data-theme="sombre"] .tile,html[data-theme="sombre"] .dash-tuile,
html[data-theme="sombre"] .carte-douce,html[data-theme="sombre"] .iw{
  border:1px solid var(--line);box-shadow:none}
a{color:var(--teal);text-decoration:none} a:hover{text-decoration:underline}
/* Mode clair : liens et libellés « ok » sur l'accent. Mode sombre inchangé.
   On exclut les liens déguisés en boutons (.btn) pour garder leur texte blanc,
   ET tout lien portant un ÉTAT (.on / .active / .actif) : ces pastilles se
   donnent un aplat plein `background:var(--teal-fg)` avec un texte `--teal-ink`,
   mais cette règle-ci est plus spécifique (0,2,2 contre 0,2,1) et écrasait leur
   couleur de texte par la couleur de leur propre fond → noir sur noir. Le
   symptôme existait déjà en cuivre-sur-cuivre, il était simplement moins visible.
   Concerne les sélecteurs de plage d'Instagram, Santé et Portefeuille. */
/* RÉCIDIVE, 29/07/2026 : le même noir-sur-noir a frappé les pilules « Projeter »
   et « Modifier » des cours. La liste d'exceptions ci-dessous ne connaît que des
   marqueurs d'ÉTAT (.on, .active…), or le problème n'est pas l'état : c'est
   qu'un composant habille déjà son lien. D'où `.lien-habille`, à poser sur tout
   lien qui pose lui-même sa couleur. Ajouter un `:not()` ne fait que RESTREINDRE
   ce que la règle attrape : rien d'existant ne change. */
html[data-theme="clair"] a:not(.btn):not(.on):not(.active):not(.actif):not(.lien-habille),
html[data-theme="clair"] .mrow .mdot{color:var(--teal-fg)}
/* Mode clair : ces quatre règles portaient encore le cuivre de l'ancien thème
   écrit en dur (#6a5f49, rgba(143,128,100,…)). L'accent de « Craie » étant
   achromatique, elles réintroduisaient une teinte parasite. Passées aux jetons. */
/* LE LOGO NE SUIT PLUS L'ACCENT · il empruntait « --teal », si bien que la
   refonte Sauge & ardoise du 16/09/2026 l'a repeint en vert sans qu'une seule
   ligne le nomme. Un logo est une identité arrêtée : il ne fait pas partie du
   périmètre d'un travail de mise en page. Les valeurs sont donc écrites ici,
   et elles ne bougeront plus quoi qu'il arrive à la palette · ce sont celles
   de « Craie », #2e2c27 et #403d36. Ne pas les repasser en jetons. */
html[data-theme="clair"] .logo{
  background:linear-gradient(150deg,#2e2c27,#403d36);
  box-shadow:0 0 0 1px rgba(38,36,31,.20),0 6px 18px -6px rgba(38,36,31,.38)}
html[data-theme="clair"] .nav a.active{box-shadow:inset 0 0 0 1px rgba(38,36,31,.18)}
html[data-theme="clair"] .chip.teal{border-color:rgba(38,36,31,.26)}
html[data-theme="clair"] .tile:hover{border-color:rgba(38,36,31,.26)}
h1{font-size:21px;font-weight:680;letter-spacing:-.02em;margin:.1em 0 .4em}
h2{font-size:15px;font-weight:600;margin:.2em 0 .5em}
h3{font-size:14px;font-weight:600}
.muted{color:var(--muted)} .small{font-size:12px} .dim{color:var(--dim)}
.ok{color:var(--ok-fg)} .warn{color:var(--amber-fg)} .danger{color:var(--red-fg)}

/* ---- Icônes SVG (Lucide) ---------------------------------------------- */
.icon{width:1em;height:1em;display:inline-block;vertical-align:-.13em;flex:none;
  fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.icon.lg{width:1.25em;height:1.25em}

/* ---- Coquille --------------------------------------------------------- */
.app{display:grid;grid-template-columns:var(--side-w) 1fr;min-height:100vh}
.sidebar{position:sticky;top:0;height:100vh;display:flex;flex-direction:column;z-index:30;
  padding-top:var(--safe-haut);padding-bottom:var(--safe-bas);box-sizing:border-box;
  background:var(--glass);backdrop-filter:blur(22px) saturate(140%);
  -webkit-backdrop-filter:blur(22px) saturate(140%);border-right:1px solid var(--glass-line)}
.brandbox{display:flex;align-items:center;gap:12px;padding:18px 18px 16px}
.logo{width:42px;height:42px;border-radius:11px;flex:none;
  display:flex;align-items:center;justify-content:center;overflow:hidden;
  font-weight:800;font-size:15px;color:#fff;line-height:1;
  background:linear-gradient(150deg,#c9bdaa,#9a8e78);
  box-shadow:0 0 0 1px rgba(201,189,170,.25),0 6px 18px -6px rgba(0,0,0,.45)}
.logo img{width:78%;height:78%;object-fit:contain;display:block}
.brandbox .txt{display:flex;flex-direction:column;min-width:0}
.brandbox b{font-weight:680;font-size:14px;letter-spacing:-.01em;line-height:1.2}
.brandbox .sub{font-size:10.5px;color:var(--dim);letter-spacing:.04em}
.nav{display:flex;flex-direction:column;gap:2px;padding:6px 12px;overflow-y:auto}
.nav .section{font-size:9.5px;text-transform:uppercase;letter-spacing:.12em;color:var(--dim);
  padding:12px 9px 5px}
.nav a{display:flex;align-items:center;gap:9px;padding:6px 9px;border-radius:7px;
  color:var(--muted);font-size:12px;position:relative}
.nav a .icon{width:14px;height:14px;opacity:.85}
/* LE REMPLISSAGE AU SURVOL · il était écrit « rgba(255,255,255,.045) »,
   c'est-à-dire du blanc translucide : visible sur l'encre, INVISIBLE sur la
   craie, où il peignait du blanc sur du blanc. Une ligne de menu ne répondait
   donc pas du tout au survol en thème clair. Passé à l'encre du thème, il
   marche des deux côtés (signalé par Romain le 16/09/2026). */
.nav a:hover{background:color-mix(in srgb,var(--ink) 6%,transparent);
  color:var(--ink);text-decoration:none}
.nav a.active{background:var(--teal-bg);color:var(--teal-fg);font-weight:550;
  box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--teal) 24%,transparent)}
.nav a.active::before{content:"";position:absolute;left:-12px;top:8px;bottom:8px;width:3px;
  border-radius:0 3px 3px 0;background:var(--teal)}
.nav a .nbadge{margin-left:auto}
.nbadge{background:var(--teal);color:var(--teal-ink);font-weight:700;font-size:10.5px;
  border-radius:var(--pill);padding:1px 7px;min-width:18px;text-align:center;line-height:1.5}
.sidebar .foot{margin-top:auto;padding:13px;border-top:1px solid var(--glass-line);
  display:flex;flex-direction:column;gap:8px}
.who{font-size:12px;color:var(--dim);padding:0 4px}

/* ---- Menu 3 niveaux repliable (refonte charbon) ----------------------- */
.fam,.grp{display:flex;flex-direction:column}
.fam-head,.grp-head{display:flex;align-items:stretch;border-radius:7px}
.fam-head:hover,.grp-head:hover{background:color-mix(in srgb,var(--ink) 6%,transparent)}
.nav .fam-label,.nav .grp-label{flex:1;min-width:0;display:flex;align-items:center;gap:10px;
  padding:6px 4px 6px 9px;background:none;border:0;cursor:pointer;text-align:left;
  font-family:inherit;text-decoration:none}
/* Niveau 1 (familles + Tableau de bord) : CAPITALES, grande, icône 20px */
.nav .fam-label,.nav>a.n1{text-transform:uppercase;letter-spacing:.11em;font-size:11px;font-weight:700}
/* UNE FAMILLE EST UNE FAMILLE, lien ou bouton. Trois d'entre elles ont une
   page d'accueil et sont donc des liens · la règle qui colore les liens en
   thème clair les prenait, et « Personnel », qui n'en a pas et n'est qu'un
   bouton, restait en encre au milieu de trois titres verts. Le rang d'une
   entrée ne doit pas dépendre de la balise qu'elle a fini par être
   (constaté par Romain le 16/09/2026). */
.nav .fam-label{color:var(--soft)}
html[data-theme="clair"] .nav .fam-label{color:var(--teal-fg)}
.nav .fam-label .icon,.nav>a.n1 .icon{width:15px;height:15px;opacity:.9}
/* Niveau 2 (groupes + pages des familles plates) : minuscule capitalisée, moyenne, icône 16px */
.nav .grp-label{text-transform:none;letter-spacing:0;font-size:12px;font-weight:500;
  color:var(--muted);gap:9px;padding:6px 4px 6px 9px}
.nav .grp-label .icon{width:14px;height:14px}
.nav .fam-kids>a{font-size:12px;font-weight:500}
.nav .fam-kids>a .icon{width:14px;height:14px}
.nav .fam-label:hover,.nav .grp-label:hover{color:var(--ink);background:none;text-decoration:none}
.nav .fam-label .hint{text-transform:none;letter-spacing:.01em;font-weight:400;font-size:10px;
  color:var(--dim);opacity:.85;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.chev{flex:none;display:flex;align-items:center;justify-content:center;width:30px;
  background:none;border:0;color:var(--dim);cursor:pointer;border-radius:8px}
.chev:hover{color:var(--soft);background:rgba(255,255,255,.05)}
.chev .icon{width:14px;height:14px;transition:transform .15s ease}
.fam.open>.fam-head .chev .icon,.grp.open>.grp-head .chev .icon{transform:rotate(90deg)}
.fam-kids,.grp-kids{display:none;flex-direction:column;gap:2px}
.fam.open>.fam-kids,.grp.open>.grp-kids{display:flex}
/* Petit retrait du niveau 2 sous le niveau 1 (le niveau 3 garde son retrait propre) */
.nav .fam-kids{margin-left:14px}
.grp-kids{margin:1px 0 4px 13px;padding-left:10px;border-left:1px solid var(--glass-line)}
/* Niveau 3 (pages des groupes) : petite, capitalisée, SANS icône */
.nav a.lvl3{font-size:11.5px;font-weight:450;color:var(--muted);padding-top:6px;padding-bottom:6px}
.nav a.lvl3.active::before{left:-11px}

.content{display:flex;flex-direction:column;min-width:0}
/* L'en-tête GRANDIT de la marge haute au lieu de passer dessous · son contenu
   reste à 52 px de haut, et la barre d'état se pose sur son verre. */
.topbar{position:sticky;top:0;z-index:20;display:flex;align-items:center;gap:14px;
  padding:calc(var(--safe-haut) + var(--marge-barre)) 24px 0;
  height:calc(var(--top-h1) + var(--safe-haut) + var(--marge-barre));
  box-sizing:border-box;background:var(--glass);
  backdrop-filter:blur(22px) saturate(140%);-webkit-backdrop-filter:blur(22px) saturate(140%);
  border-bottom:1px solid var(--glass-line)}
.topbar .title{font-weight:600;font-size:14.5px}
.topbar .spacer{flex:1 1 auto;min-width:8px}

/* ── LE SECOND ÉTAGE ──────────────────────────────────────────────────────
   Posé sur le FOND et non sur le panneau : une seconde barre en verre
   par-dessus la première fait un bandeau de 94 px qui pèse sur chaque page.
   Sans « backdrop-filter » ici · le sous-menu est en « fixed » et prend pour
   repère le premier ancêtre filtré ; le JS mesure cette origine, donc les
   deux cas marchent, mais un filtre de moins est un piège de moins. */
.topbar2{position:sticky;z-index:19;
  top:calc(var(--top-h1) + var(--safe-haut) + var(--marge-barre));
  height:var(--top-h2);
  padding:0 20px;gap:4px;background:var(--bg);backdrop-filter:none;
  -webkit-backdrop-filter:none}
.topbar2 .htools{flex:1 1 auto}
/* Barre d'outils transverses (header) */
/* La pastille de notification dépasse de ~5 px au-dessus de la puce. Or `overflow-x:auto`
   force le navigateur à rogner AUSSI verticalement (dès qu'un axe n'est plus « visible »,
   l'autre passe à « auto ») : la pastille était donc coupée en haut. On lui ménage la
   place dans le padding, annulé par une marge négative pour ne rien décaler. */
.htools{display:flex;align-items:center;gap:2px;flex:0 1 auto;min-width:0;overflow-x:auto;overscroll-behavior-x:contain;scrollbar-width:none;
  padding-block:8px;margin-block:-8px}
.htools::-webkit-scrollbar{display:none}
.htool{display:inline-flex;align-items:center;gap:6px;padding:6px 9px;border-radius:8px;
  color:var(--muted);font-size:12px;font-weight:500;white-space:nowrap;text-decoration:none;
  position:relative;flex:0 0 auto;transition:background .12s ease,color .12s ease}
.htool:hover{color:var(--ink);background:var(--ctl-bg);text-decoration:none}
.htool.active{color:var(--teal-fg);background:var(--teal-bg)}
.htool .icon{width:15px;height:15px;flex:0 0 auto}
.htool .hcount{top:-3px;right:-2px}

/* ── Regroupements de la barre du header ────────────────────────────────────
   Un lien qui abrite d'autres pages les déploie au survol. UN SEUL étage : la
   barre n'a pas la place d'un troisième niveau, et un menu en cascade se
   referme au premier écart de souris. */
.htool-grp{position:relative;flex:0 0 auto}
.htool-grp > button.htool{border:0;background:none;font:inherit;cursor:pointer}
.htool-grp .chev{width:12px;height:12px;opacity:.6;margin-left:-2px}
/* Le sous-menu est SORTI du flux de la barre.
   Mesuré : « .htools » défile horizontalement (overflow-x:auto), et un
   conteneur qui coupe sur un axe coupe sur les deux - le menu en position
   absolue s'y faisait rogner et n'apparaissait qu'en sliver sous l'en-tête.
   Aucun z-index n'y change rien : ce n'est pas un problème d'empilement.

   ATTENTION au second piège, qui a suivi : « fixed » ne signifie pas « repéré
   sur l'écran ». Le « backdrop-filter » de « .topbar » (juste au-dessus) fait
   de la barre le REPÈRE des éléments fixés qu'elle contient - un « transform »
   ou un « filter » auraient le même effet. Les coordonnées sont donc comptées
   depuis le bord de la barre, pas depuis le bord de la fenêtre. Le JS de
   base.html mesure cette origine au lieu de la supposer : ne pas y toucher
   sans relire ce commentaire. */
.hsous{position:fixed;z-index:60;display:none;
  min-width:190px;padding:5px;background:var(--solid);
  border:1px solid var(--glass-line);border-radius:11px;
  box-shadow:0 14px 34px rgba(0,0,0,.28)}
/* Le survol ET le focus : un menu qui ne s'ouvre qu'à la souris est
   inatteignable au clavier. */
.htool-grp:hover .hsous,.htool-grp:focus-within .hsous{display:block}
/* Un pont invisible entre le parent et son menu : sans lui, les 6 px d'écart
   suffisent à faire sortir la souris du survol, et le menu se referme avant
   qu'on l'atteigne. */
.htool-grp::after{content:"";position:absolute;top:100%;left:0;right:0;height:10px}
.hsous-l{display:flex;align-items:center;gap:9px;padding:7px 10px;border-radius:8px;
  color:var(--muted);font-size:12px;white-space:nowrap;text-decoration:none;
  position:relative}
/* LE SURVOL SE VOIT ENFIN · il était peint avec « --ctl-bg », qui vaut
   BLANC en thème clair, sur un panneau lui-même blanc : rien n'apparaissait.
   Même défaut que le rail, même correction · on peint avec l'encre du thème.
   Et le libellé avance de 3 px, comme dans le rail, pour que les deux menus
   répondent de la même façon (demande de Romain, 16/09/2026). */
.hsous-l{transition:color var(--t) var(--sortie),background var(--t) var(--sortie),
  transform var(--t) var(--ressort)}
.hsous-l:hover{color:var(--ink);text-decoration:none;
  background:color-mix(in srgb,var(--ink) 7%,transparent)}
@media (hover: hover){.hsous-l:hover{transform:translateX(3px)}}
@media (prefers-reduced-motion:reduce){.hsous-l:hover{transform:none}}
.hsous-l.active{color:var(--teal-fg);background:var(--teal-bg)}
.hsous-l .icon{width:15px;height:15px;flex:0 0 auto}
.hsous-l .hcount{position:static;margin-left:auto}
@media (max-width:760px){
  /* LES CATÉGORIES SONT LES MÊMES QU'AU LARGE. Elles étaient auparavant
     APLATIES : le sous-menu se dépliait dans la barre et l'étiquette de
     catégorie était masquée, si bien qu'on voyait toutes les pages en vrac,
     sans le rangement qu'on avait défini. Elles s'ouvrent maintenant AU
     TOUCHER, par la classe `ouvert` posée en JavaScript - le survol n'existe
     pas sous un doigt.

     Le menu reste borné à l'écran : sur un téléphone de 390 px, un panneau de
     largeur libre sortait par la droite et devenait inatteignable. */
  .hsous{max-width:min(320px, calc(100vw - 16px))}
  .hsous-l{white-space:normal}
}

/* L'OUVERTURE AU CLIC, en plus du survol et du focus. Sur un écran tactile il
   n'y a pas de survol : sans elle, une catégorie serait une étiquette morte.
   Elle sert aussi aux portables à écran tactile, où le survol est capricieux. */
.htool-grp.ouvert .hsous{display:block}
.clockbox{text-align:right;line-height:1;flex:0 0 auto}
.clock{font-size:15px;font-weight:600}
.cdate{font-size:10px;color:var(--dim);margin-top:3px;text-transform:capitalize}
.menu-btn{display:none;background:var(--ctl-bg);border:1px solid var(--glass-line);
  color:var(--ink);border-radius:9px;width:38px;height:38px;cursor:pointer;
  align-items:center;justify-content:center;flex:0 0 auto}
.menu-btn .icon{width:20px;height:20px}
.page{padding:24px 32px;width:100%;max-width:none;margin:0}
.scrim{display:none}

/* ---- Panneaux : verre sur le chrome, opacité sur les données ---------- */
.panel{background:var(--glass-2);backdrop-filter:blur(18px) saturate(135%);
  -webkit-backdrop-filter:blur(18px) saturate(135%);border:1px solid var(--glass-line);
  border-radius:16px;padding:17px 18px;margin-bottom:14px;box-shadow:var(--shadow)}
.panel.glass{}
.data{background:var(--solid);border:1px solid var(--line);border-radius:12px;overflow-x:auto}
.big{font-size:21px;font-weight:680;line-height:1}

/* ---- Contrôles : hauteur UNIQUE (--ctl-h) ----------------------------- */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:7px;height:var(--ctl-h);
  padding:0 14px;border-radius:var(--radius-sm);border:1px solid var(--teal);
  background:var(--teal);color:var(--teal-ink);font-weight:600;font-size:12.5px;
  cursor:pointer;white-space:nowrap;transition:filter .15s;position:relative;isolation:isolate}
.btn:hover{filter:brightness(1.08);text-decoration:none}
/* ── LA LUEUR QUI TRAVERSE ────────────────────────────────────────────────
   Le reflet balaie le bouton une fois au survol · mécanique retenue par
   Romain le 15/09/2026, avec les gestes qui entrent par la droite.
   Elle se déplace par la POSITION DU FOND et non par un « transform » : un
   décalage sortirait du bouton et obligerait à poser « overflow:hidden », qui
   rognerait la pastille du bouton occupé (`.btn[aria-busy]::after`).
   `::before` et non `::after` pour la même raison : la pastille tient déjà
   l'autre. */
.btn::before{content:"";position:absolute;inset:0;border-radius:inherit;
  pointer-events:none;z-index:-1;opacity:0;
  background:linear-gradient(100deg,transparent 22%,var(--lueur) 50%,transparent 78%);
  background-size:260% 100%;background-position:160% 0}
.btn:hover::before{opacity:1;background-position:-60% 0;
  transition:background-position .55s cubic-bezier(.3,.7,.3,1),opacity .1s}
.btn:not(:hover)::before{transition:opacity .2s}
@media (prefers-reduced-motion:reduce){
  .btn::before,.btn:hover::before{transition:none;opacity:0}}
.btn.ghost{background:var(--glass-2);border-color:var(--glass-line);color:var(--ink)}
.btn.ghost:hover{border-color:var(--teal);color:var(--teal-fg)}
.btn.danger{background:var(--red);border-color:var(--red);color:#2a0c0c}
/* DEUX POIDS POUR DÉTRUIRE, comme il y en a quatre pour agir.
     .btn.danger        rouge PLEIN · ce qui VALIDE une destruction, dans la
                        boîte de confirmation : c'est le dernier geste.
     .btn.ghost.danger  rouge en CADRE · une action destructrice parmi
                        d'autres, sur une ligne ou dans une barre d'outils.
   Sans ce second poids, une liste où chaque ligne porte « Supprimer » devient
   un mur rouge, et le rouge cesse de vouloir dire quoi que ce soit. Le
   sélecteur pèse (0,3,0), il passe donc après `.btn.danger` sans surenchère.
   On s'appuie sur `--red-fg`, présent dans les DEUX thèmes, et non sur
   `--red`, qui a longtemps manqué au thème clair. */
.btn.ghost.danger{background:var(--glass-2);border-color:var(--red-fg);color:var(--red-fg)}
.btn.ghost.danger:hover{border-color:var(--red-fg);color:var(--red-fg);
  background:color-mix(in srgb, var(--red-fg) 11%, var(--glass-2))}
/* QUATRE POIDS, ET NON TROIS. Il n'existait que « plein », « fantôme » et
   « danger » : sur une barre à quatre actions, tout ce qui n'était pas
   l'action principale se ressemblait, et l'ordre d'importance ne se lisait
   pas. Deux niveaux intermédiaires suffisent à ranger la barre -

     .btn            l'action de la page      (plein, accentué)
     .btn.second     ce qu'on fait souvent    (plein, neutre)
     .btn.ghost      ce qu'on fait parfois    (cadre seul)
     .btn.tertiaire  se retirer, annuler      (ni fond ni cadre)

   Aucune couleur nouvelle : « second » emprunte le fond des puces, et
   « tertiaire » ne prend son fond qu'au survol - au repos il ne pèse rien. */
.btn.second{background:var(--chip);border-color:var(--line);color:var(--ink)}
.btn.second:hover{border-color:var(--teal);color:var(--teal-fg);background:var(--teal-bg)}
.btn.tertiaire{background:transparent;border-color:transparent;color:var(--muted);padding:0 9px}
.btn.tertiaire:hover{color:var(--ink);background:var(--chip)}
.btn.sm{height:30px;padding:0 11px;font-size:12px;border-radius:8px}

/* ── LE CHAMP, ET SON BORD D'ÉTAT ─────────────────────────────────────────
   Le bord GAUCHE, épais de 3 px, dit dans quel état est le champ : gris au
   repos, sauge au focus, terracotta en erreur. C'est exactement le bord
   d'état des lignes de tableau, appliqué au champ · les deux se répondent au
   lieu de vivre chacun leur vie (écriture C, retenue le 16/09/2026).
   Le rembourrage gauche perd les 2 px que le bord a pris, pour que le texte
   ne se décale pas d'un champ à l'autre. */
input,select,textarea{font-family:inherit;font-size:13px;color:var(--ink);
  background:var(--ctl-bg);border:1px solid var(--line);border-left-width:3px;
  border-radius:var(--radius-sm);padding:0 13px 0 11px;height:var(--ctl-h)}
textarea{height:auto;min-height:64px;padding:9px 13px 9px 11px;line-height:1.5}

/* MENUS DÉROULANTS. La boîte vient de la règle du dessus, comme pour les
   autres champs ; ce qui détonnait, c'est l'habillage du NAVIGATEUR · flèche
   grise en relief sur macOS, gros bouton système sur iPhone · posé par-dessus
   un intranet entièrement arrondi et plat.
   `appearance:none` le retire et on redessine la flèche. L'OUVERTURE reste
   native : sur téléphone c'est le sélecteur du système qui apparaît, bien plus
   confortable qu'une liste maison, et tout reste utilisable au clavier sans
   une ligne de JavaScript.
   La géométrie n'est PAS touchée (hauteur, rayon, bordure) : 81 gabarits
   contiennent un menu, ce n'est pas le moment de déplacer les boîtes. */
select{
  appearance:none;-webkit-appearance:none;
  padding-right:29px;cursor:pointer;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6' fill='none' stroke='%23999' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 11px center;background-size:9px 6px;
}
select:hover{border-color:var(--teal)}
/* Le menu ouvert est rendu par le système : on force des couleurs lisibles,
   sans quoi le thème sombre donne du noir sur noir sur certains navigateurs. */
select option{background:var(--solid);color:var(--ink)}
/* Un menu à choix multiple n'a pas de flèche : elle se poserait sur la liste. */
select[multiple],select[size]:not([size="1"]){
  background-image:none;padding-right:11px;height:auto}
input[type=file]{padding:6px 10px;height:auto}
input[type=checkbox],input[type=radio]{height:auto;width:auto;accent-color:var(--teal)}
input:hover:not(:disabled):not([readonly]),
select:hover:not(:disabled),
textarea:hover:not(:disabled):not([readonly]){
  border-left-color:color-mix(in srgb,var(--teal) 55%,var(--line))}
input:focus,select:focus,textarea:focus{outline:none;
  border-color:var(--line);border-left-color:var(--teal)}
/* Les cases, boutons radio, curseurs et sélecteurs de couleur ne sont pas des
   boîtes de saisie : le bord d'état n'a rien à y dire, et il les déformerait. */
input[type=checkbox],input[type=radio],input[type=range],input[type=color]{
  border-left-width:1px;padding:0}
label{display:block;font-size:12px;color:var(--muted);margin-bottom:3px}
label input,label select,label textarea{margin-top:3px}

.chip{display:inline-flex;align-items:center;gap:5px;padding:2px 10px;border-radius:var(--pill);
  background:var(--chip);border:1px solid var(--line);font-size:var(--t-puce);color:var(--soft);
  margin:2px 4px 2px 0}
.chip.teal{border-color:rgba(201,189,170,.3);color:var(--teal-fg);background:var(--teal-bg)}
.chip.gold,.chip.share{border-color:rgba(201,168,106,.32);color:var(--gold-fg);background:var(--gold-bg)}
.chip.warn,.chip.amber{border-color:rgba(217,182,106,.32);color:var(--amber-fg);background:var(--amber-bg)}
.chip.green{border-color:rgba(45,142,80,.3);color:#2f8e50;background:rgba(45,142,80,.10)}
.chip.red{border-color:rgba(190,70,70,.32);color:var(--red-fg);background:var(--red-bg)}

table{width:100%;border-collapse:collapse;font-size:var(--t-tab)}
th,td{padding:9px 12px;text-align:left}
thead th{font-size:var(--t-micro);text-transform:uppercase;letter-spacing:.05em;color:var(--dim);
  border-bottom:1px solid var(--line)}
/* L'EN-TÊTE RESTE. Passé dix lignes, on ne sait plus quelle colonne on lit -
   sur Devis, Contacts ou l'emploi du temps, c'est tous les jours.
   Trois choses à ne pas manquer, et chacune est un piège :
   · l'en-tête de PAGE est lui aussi collant (`.topbar`, --top-h) ; coller à 0
     ferait passer l'en-tête du tableau DESSOUS, invisible ;
   · avec `border-collapse:collapse`, la bordure d'une cellule collante n'est
     pas rendue - elle reste avec le tableau et l'en-tête flotte sans trait.
     L'ombre intérieure, elle, suit la cellule ;
   · sans fond opaque, les lignes défilent À TRAVERS l'en-tête. */
thead th{position:sticky;top:var(--top-h);z-index:3;background:var(--solid);
  border-bottom:0;box-shadow:inset 0 -1px 0 var(--line)}
/* ET VOICI LE PIÈGE, mesuré plutôt que supposé. La plupart des tableaux sont
   dans une enveloppe à `overflow-x:auto` pour le défilement horizontal. Or dès
   qu'un axe n'est plus `visible`, l'AUTRE devient `auto` : l'enveloppe est donc
   un défileur vertical - qui ne défile jamais, sa hauteur épousant son contenu.
   L'en-tête collait à elle, et repartait donc avec la page. Vérifié sur la page
   Devis : après 700 px de défilement, l'en-tête était à -23 px.
   La parade est de borner la hauteur de l'enveloppe pour qu'elle défile
   VRAIMENT. C'est `initTableauxCollants` (base.html) qui la pose, parce qu'elle
   dépend de la place réellement disponible sous le tableau - 353 px de chrome
   au-dessus sur Devis, bien moins ailleurs. Aucune valeur ne convient partout,
   donc on mesure. La classe est posée par ce script, jamais dans un gabarit. */
/* `.tab-cadre` et `.tab-borne` ont été retirés le 16/09/2026 avec le script
   qui bornait la hauteur : les tableaux s'affichent en entier et c'est la page
   qui défile.

   `.tab-plat` les remplace, et ne fait qu'une chose : remettre l'en-tête de
   colonnes DANS LE FLUX quand le tableau vit dans une enveloppe qui défile.
   Là, « sticky » s'accroche à l'enveloppe et non à la page, et l'en-tête se
   décalait de la hauteur de la barre du haut à l'intérieur du cadre, par-dessus
   les premières lignes. Sans enveloppe, il colle à la page et c'est très bien
   ainsi · le calendrier de la loge en profite. */
.tab-plat thead th{position:static}
tbody tr{border-bottom:1px solid var(--hair)} tbody tr:last-child{border-bottom:0}

code,pre{background:var(--chip);border:1px solid var(--line);border-radius:7px;padding:2px 6px;
  font-family:ui-monospace,Menlo,Consolas,monospace;font-size:.86em}
pre{padding:12px;overflow:auto}

nav.tabs{display:flex;gap:6px;flex-wrap:wrap;margin-top:8px}
nav.tabs a{display:inline-flex;align-items:center;gap:6px;height:30px;padding:0 11px;
  border-radius:8px;border:1px solid var(--glass-line);color:var(--soft);font-size:12.5px;
  background:var(--glass-2)}
nav.tabs a:hover{border-color:var(--teal);color:var(--teal-fg);text-decoration:none}

.msg{border-radius:10px;padding:10px 13px;margin-bottom:8px;border:1px solid var(--line);
  background:var(--solid);font-size:13px;display:flex;align-items:center;gap:10px;justify-content:space-between}
.msg.ok{border-color:rgba(111,191,138,.4)} .msg.err{border-color:rgba(240,138,138,.45)}
.msg.hide{opacity:0;transform:translateY(-4px);transition:opacity .3s,transform .3s}
/* Bandeau « connexion externe à rétablir ». Persistant, contrairement aux messages
   flash : laisser expirer un jeton oblige à tout réautoriser à la main. La couleur
   ne fait que SIGNALER · ambre = à surveiller, rouge = action requise. */
#cnx-alerte{margin-bottom:10px}
.cnx-bar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;border-radius:10px;
         padding:10px 13px;margin-bottom:8px;font-size:13px;
         border:1px solid rgba(217,182,106,.4);background:var(--amber-bg)}
.cnx-bar.critique{border-color:rgba(240,138,138,.45);background:var(--red-bg)}
.cnx-bar .icon{flex:none;width:17px;height:17px;color:var(--amber-fg)}
.cnx-bar.critique .icon{color:var(--red)}
.cnx-bar span{flex:1;min-width:220px}
.cnx-bar .btn{flex:none}
.msg-x{background:none;border:0;color:var(--muted);cursor:pointer;padding:2px;border-radius:6px;flex:none}
.msg-x:hover{color:var(--ink);background:rgba(127,127,127,.12)}

/* ---- Hub personnalisable : cartes-catégories + mini-tuiles ------------- */
.hubcats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:14px;margin-bottom:14px}
@media(max-width:1200px){.hubcats{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(max-width:620px){.hubcats{grid-template-columns:1fr}}
.hubcat{background:var(--glass);border:1px solid var(--glass-line);border-radius:var(--radius);
  padding:14px 16px 14px 18px;box-shadow:var(--shadow);position:relative;overflow:hidden}
.hubcat::before{content:"";position:absolute;left:0;top:0;bottom:0;width:4px;background:var(--cat)}
.hubcat-head{display:flex;align-items:center;gap:8px;margin-bottom:11px}
.hubcat-head h3{margin:0;font-size:14px;letter-spacing:-.01em}
.hubcat-dot{width:9px;height:9px;border-radius:50%;background:var(--cat);flex:none;
  box-shadow:0 0 8px var(--cat)}
.minitiles{display:flex;flex-direction:column;gap:6px}
.minitile{display:flex;align-items:center;gap:10px;padding:7px 10px;border-radius:var(--radius-sm);
  border:1px solid var(--hair);background:var(--ctl-bg);color:var(--ink);font-size:13px;
  text-decoration:none;transition:border-color .12s,transform .12s}
.minitile:hover{border-color:var(--tile);transform:translateX(2px);text-decoration:none}
.mt-ic{width:30px;height:30px;border-radius:8px;flex:none;display:inline-flex;align-items:center;
  justify-content:center;color:var(--tile);background:color-mix(in srgb,var(--tile) 15%,transparent)}
.minitile .mt-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:500}
.minitile .mt-ext{width:14px;height:14px;opacity:.35;flex:none}
.minitile:hover .mt-ext{opacity:.75}

/* ---- Liste de contacts (cartes) --------------------------------------- */
.clist{display:flex;flex-direction:column}
.crow{display:flex;flex-wrap:wrap;gap:4px 12px;align-items:baseline;padding:10px 13px;
  border-bottom:1px solid var(--hair)}
.crow:last-child{border-bottom:0}
.crow .nm{font-weight:550;flex:1 1 190px;min-width:150px}
.crow .nm a{color:var(--ink)} .crow .nm a:hover{color:var(--teal-fg)}
.crow .meta{flex:2 1 240px;display:flex;flex-wrap:wrap;gap:4px;align-items:baseline}

/* ---- Tuiles (pills compactes façon maquette) -------------------------- */
.tiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:12px;margin-bottom:14px}
.tile{display:block;background:var(--solid);border:1px solid var(--line);border-radius:var(--radius);
  padding:15px 16px;color:var(--ink);transition:transform .15s,border-color .15s,box-shadow .15s}
.tile:hover{transform:translateY(-2px);border-color:rgba(163,155,196,.34);
  box-shadow:0 14px 30px -16px #000;text-decoration:none}
.tile .ic{display:flex;align-items:center;justify-content:center;width:38px;height:38px;
  border-radius:10px;background:var(--teal-bg);color:var(--teal-bright);font-size:18px;margin-bottom:9px}
.tile .ic .icon{width:19px;height:19px}
.tile h3{margin:.2em 0 .15em;font-size:14px}
.tile .big{font-size:21px;margin:2px 0}

/* ---- Tuiles OUTILS (dashboard) : aplats doux désaturés, 1 teinte par outil --- */
.toolrow{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:16px;margin-top:18px}
.tool{display:block;position:relative;overflow:hidden;border-radius:var(--radius);border:1px solid var(--line);
  padding:18px;min-height:116px;color:var(--ink);transition:transform .15s ease,border-color .15s ease}
.tool:hover{transform:translateY(-2px);text-decoration:none;border-color:var(--glass-line)}
.tool .ic{width:34px;height:34px;display:flex;align-items:center;justify-content:center;color:var(--soft)}
.tool .ic .icon{width:22px;height:22px}
.tool h3{margin:14px 0 3px;font-size:15px}
.tool .sub{color:var(--muted);font-size:12.5px}
.tool.terracotta,.dash-tuile.terracotta{background:var(--tu-terracotta-bg)}
.tool.sable,.dash-tuile.sable{background:var(--tu-sable-bg)}
.tool.sauge,.dash-tuile.sauge{background:var(--tu-sauge-bg)}
.tool.teal,.dash-tuile.teal{background:var(--tu-teal-bg)}
.tool.gold,.dash-tuile.gold{background:var(--tu-gold-bg)}

/* ── LE LISERÉ DE FAMILLE ──────────────────────────────────────────────────
   La couleur de famille ne s'étale plus sur la surface : elle touche la tuile
   par une arête de 3 px, et nulle part ailleurs. Quatre degrés ont été essayés
   le 16/09/2026 (rien · liseré · dégradé · aplat) ; c'est le liseré qui donne
   une identité lisible d'un coup d'œil sans reprendre le reproche fait aux
   aplats · « la couleur signale, elle ne décore pas ».

   AU SURVOL sur pointeur fin, PERMANENT au doigt : sans le second bloc, la
   couleur ne s'afficherait JAMAIS sur iPhone, où il n'y a pas de survol, et la
   moitié des usages perdrait l'information. C'est la même raison qui fait que
   rien d'important ne doit dépendre du seul survol. */
.tool,.dash-tuile{--liz:transparent}
.tool.terracotta,.dash-tuile.terracotta{--liz:var(--tu-terracotta)}
.tool.sable,.dash-tuile.sable{--liz:var(--tu-sable)}
.tool.sauge,.dash-tuile.sauge{--liz:var(--tu-sauge)}
.tool.teal,.dash-tuile.teal{--liz:var(--tu-teal)}
.tool.gold,.dash-tuile.gold{--liz:var(--tu-gold)}
@media (hover:hover){
  .tool:hover,.dash-tuile:hover{box-shadow:inset 0 3px 0 var(--liz),var(--ombre-2)}
}
@media (hover:none){
  .tool,.dash-tuile{box-shadow:inset 0 3px 0 var(--liz)}
}
/* LA SURFACE RESTE L'ENCRE · la couleur va dans la pastille, et nulle part
   ailleurs. C'est la règle du thème clair, et elle vaut aussi la nuit : un
   aplat teinté étalé sur une large tuile ne se lit pas comme un signal, il se
   lit comme une TACHE — et il rompt l'encre juste là où le regard se pose, au
   centre de la page entre le rail et le bandeau (Romain, 06/09/2026).
   La couleur ne décore pas, elle signale. */
html[data-theme="sombre"] .tool.terracotta,html[data-theme="sombre"] .dash-tuile.terracotta,
html[data-theme="sombre"] .tool.sable,html[data-theme="sombre"] .dash-tuile.sable,
html[data-theme="sombre"] .tool.sauge,html[data-theme="sombre"] .dash-tuile.sauge,
html[data-theme="sombre"] .tool.teal,html[data-theme="sombre"] .dash-tuile.teal,
html[data-theme="sombre"] .tool.gold,html[data-theme="sombre"] .dash-tuile.gold{
  background:var(--solid)}
/* Mode clair : la carte reste blanche et la couleur se concentre dans une
   pastille pleine à glyphe blanc. Signal aussi fort qu'un aplat, sans teinter
   la surface · conforme à la règle « la couleur signale, elle ne décore pas ». */
/* Mode sombre : l'icône était en `--soft`, un gris. Cinq familles, cinq
   icônes GRISES identiques · la couleur de la tuile ne se retrouvait nulle
   part dans son contenu. La pastille est TEINTÉE et non pleine : sur une
   encre, un aplat plein arrive comme une lampe. */
html[data-theme="sombre"] .tool .ic,html[data-theme="sombre"] .dash-tuile .ic{
  width:34px;height:34px;border-radius:9px}
html[data-theme="sombre"] .tool.terracotta .ic,html[data-theme="sombre"] .dash-tuile.terracotta .ic{color:var(--tu-terracotta);background:rgba(255,138,92,.14)}
html[data-theme="sombre"] .tool.sable .ic,html[data-theme="sombre"] .dash-tuile.sable .ic{color:var(--tu-sable);background:rgba(182,164,255,.14)}
html[data-theme="sombre"] .tool.sauge .ic,html[data-theme="sombre"] .dash-tuile.sauge .ic{color:var(--tu-sauge);background:rgba(74,214,160,.14)}
html[data-theme="sombre"] .tool.teal .ic,html[data-theme="sombre"] .dash-tuile.teal .ic{color:var(--tu-teal);background:rgba(94,200,255,.14)}
html[data-theme="sombre"] .tool.gold .ic,html[data-theme="sombre"] .dash-tuile.gold .ic{color:var(--tu-gold);background:rgba(255,207,92,.14)}

html[data-theme="clair"] .tool.terracotta .ic,html[data-theme="clair"] .dash-tuile.terracotta .ic{color:#fff;background:var(--tu-terracotta);border-radius:9px}
html[data-theme="clair"] .tool.sable .ic,html[data-theme="clair"] .dash-tuile.sable .ic{color:#fff;background:var(--tu-sable);border-radius:9px}
html[data-theme="clair"] .tool.sauge .ic,html[data-theme="clair"] .dash-tuile.sauge .ic{color:#fff;background:var(--tu-sauge);border-radius:9px}
html[data-theme="clair"] .tool.teal .ic,html[data-theme="clair"] .dash-tuile.teal .ic{color:#fff;background:var(--tu-teal);border-radius:9px}
html[data-theme="clair"] .tool.gold .ic,html[data-theme="clair"] .dash-tuile.gold .ic{color:#fff;background:var(--tu-gold);border-radius:9px}

/* ---- Tableau de bord personnalisable : grille 12 colonnes ------------- */
/* minmax(0,1fr) et non 1fr : « 1fr » vaut « minmax(auto,1fr) », et une colonne
   ne descend alors JAMAIS sous la largeur minimale de son contenu. Les tuiles
   dont le titre est long s'élargissaient donc en écrasant les autres, au lieu
   de se partager la place à égalité. Le 0 autorise la colonne à passer sous
   son contenu, ce qui laisse la troncature faire son travail. */
.dash-grid{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:16px;align-items:start;margin-top:4px}
.dash-cell{min-width:0}
.dash-cell>*{margin:0}
@media(max-width:900px){
  .dash-grid{grid-template-columns:minmax(0,1fr)}
  .dash-cell{grid-column:1 / -1 !important}
  /* L'ESPACEUR EST BORNÉ, PLUS SUPPRIMÉ. Il était masqué en une seule colonne
     parce qu'un espaceur réglé pour la grille - 120 px, parfois davantage -
     n'y produisait qu'un vide qui repoussait la barre de recherche hors de
     l'écran. Mais le supprimer tassait TOUT : Romain place ces séparateurs
     pour distinguer ses composants, et sur téléphone on n'en voyait plus la
     trace (signalé le 26/08/2026).
     Il garde donc sa fonction, à une hauteur fixe et modeste. Avec les 16 px
     de gouttière de part et d'autre, cela fait 52 px de respiration entre
     deux blocs : la coupure se voit, et rien n'est chassé de l'écran. */
  .dash-cell:has(.dash-espaceur){display:block}
  .dash-espaceur{height:20px !important}
}
/* L'ÉDITEUR COMMANDE, LA FEUILLE N'ARBITRE PAS.

   Il y a eu ici, deux jours durant, une règle qui reposait la largeur des
   tuiles selon la place disponible - d'abord d'après la fenêtre, puis d'après
   la grille elle-même. Elle rendait service à une configuration écrite pour
   une grille de 1 190 px, restée telle quelle quand la colonne d'indicateurs
   lui en a pris 368.

   Mais elle portait un `!important`, et écrasait donc AUSSI la largeur et la
   colonne choisies dans /administration/tableau-de-bord/. Régler une tuile sur
   6/12 ne changeait plus rien : la page rendait 4/12. L'éditeur était devenu
   décoratif (signalé par Romain le 30/08/2026).

   Une page qui décide à la place de celui qui l'a réglée est plus gênante
   qu'une tuile trop étroite : on ne sait plus qui ment. La feuille ne touche
   donc plus aux largeurs · l'éditeur montre la vraie proportion, et c'est là
   qu'on décide. Il ne reste que le repli à UNE colonne sur téléphone, plus
   haut, qui n'est pas un choix de mise en page mais une contrainte d'écran. */
/* Tuile compacte (~ moitié de l'ancienne .tool en largeur et hauteur) */
/* ── LA TUILE D'OUTIL ─────────────────────────────────────────────────────
   VERTICALE depuis le 16/09/2026 · elle était horizontale, icône à gauche et
   texte à droite sur 62 px de haut, ce qui donnait une rangée de barres
   identiques où rien ne se distinguait. La pastille passe au-dessus, le nom
   dessous, et la tuile porte DEUX FOIS son icône : la petite qui la nomme, et
   une grande fantôme rognée par le coin, à cinq pour cent d'encre. De loin,
   chaque tuile a une silhouette reconnaissable avant même qu'on la lise. */
.dash-tuile{display:block;position:relative;overflow:hidden;border-radius:var(--radius);
  border:1px solid var(--line);background:var(--glass);padding:14px 15px;min-height:98px;
  height:100%;color:var(--ink);
  transition:transform .18s cubic-bezier(.2,.8,.3,1),border-color .18s,box-shadow .18s}
.dash-tuile:hover{transform:translateY(-2px);text-decoration:none;border-color:var(--glass-line)}
.dash-tuile .ic{width:32px;height:32px;flex:0 0 auto;display:flex;align-items:center;
  justify-content:center;color:var(--soft);background:var(--chip);border-radius:9px;
  margin-bottom:11px}
.dash-tuile .ic .icon{width:18px;height:18px}
/* L'EMPREINTE. Décorative et sourde : elle ne doit jamais concurrencer le nom,
   qui reste l'élément le plus contrasté de la tuile. */
.dash-emp{position:absolute;right:-20px;bottom:-24px;width:96px;height:96px;
  color:var(--ink);opacity:.055;pointer-events:none;stroke-width:1.1;
  transition:transform .4s cubic-bezier(.2,.8,.3,1),opacity .3s}
.dash-tuile:hover .dash-emp{transform:rotate(-8deg) scale(1.1);opacity:.1}
html[data-theme="sombre"] .dash-emp{opacity:.07}
html[data-theme="sombre"] .dash-tuile:hover .dash-emp{opacity:.13}
@media (prefers-reduced-motion:reduce){.dash-emp{transition:none}}

/* ── LA CARTE DE COLLECTION ───────────────────────────────────────────────
   Une fiche qui en contient d'autres le MONTRE : elle est posée sur une pile
   de deux feuilles, qui s'écartent en éventail au survol. La couleur de
   famille arrive par le liseré, comme sur les tuiles. */
.dash-coll{position:relative;display:flex;flex-direction:column;height:100%;
  padding:7px 7px 0 0;color:var(--ink);text-decoration:none}
.dash-coll:hover{text-decoration:none}
.dc-f{position:absolute;border-radius:var(--radius-sm);background:var(--glass);
  border:1px solid var(--line);transition:transform .26s cubic-bezier(.2,.8,.3,1)}
.dc-f1{inset:0 0 14px 14px;opacity:.5}
.dc-f2{inset:3.5px 3.5px 10.5px 10.5px;opacity:.75}
.dash-coll:hover .dc-f1{transform:translate(4px,-4px) rotate(1.6deg)}
.dash-coll:hover .dc-f2{transform:translate(2px,-2px) rotate(.8deg)}
.dc-haut{position:relative;display:flex;flex-direction:column;flex:1 1 auto;min-height:0;
  border-radius:var(--radius-sm);
  background:var(--glass);border:1px solid var(--line);overflow:hidden;
  margin:7px 14px 0 0;
  transition:transform .26s cubic-bezier(.2,.8,.3,1),border-color .2s,box-shadow .2s}
.dash-coll:hover .dc-haut{transform:translateY(-2px);border-color:var(--glass-line);
  box-shadow:inset 0 3px 0 var(--liz),var(--ombre-2)}
/* LA VIGNETTE PORTE LES VISUELS · le plus récent en grand, les deux suivants
   en bandes. Trois images suffisent à dire « collection » ; au-delà on ne lit
   plus que des timbres-poste. « object-fit:cover » parce que pochettes,
   couvertures et affiches n'ont pas le même format : sans lui, une affiche de
   film étirée à la largeur d'une pochette carrée devient une bouillie. */
/* TOUTES LES CARTES FONT LA MÊME HAUTEUR · la vignette absorbe la place qui
   reste au lieu d'imposer sa propre proportion. Sans cela, une collection sans
   visuel, ou dont le titre passe à la ligne, était plus courte que ses voisines
   et la rangée partait en escalier (signalé par Romain, 16/09/2026). */
.dc-vign{display:flex;align-items:stretch;justify-content:center;gap:1px;
  flex:1 1 auto;min-height:104px;overflow:hidden;
  background:linear-gradient(145deg,var(--chip),var(--glass))}
.dc-vign img{display:block;min-width:0;height:100%;object-fit:cover;flex:1 1 0}
.dc-vign img:first-child{flex:2 1 0}
.dc-vign .icon{width:26px;height:26px;stroke-width:1.2;margin:auto;
  color:color-mix(in srgb,var(--muted) 45%,transparent)}
.dc-bas{display:block;flex:0 0 auto;padding:8px 11px 10px;border-top:1px solid var(--line)}
.dc-titre{display:block;font-size:12.5px;font-weight:600;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.dc-sub{display:block;font-size:10.5px;color:var(--dim);margin-top:1px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dc-nb{position:absolute;top:0;right:0;z-index:2;min-width:22px;height:22px;padding:0 6px;
  border-radius:var(--pill);background:var(--teal);color:var(--teal-ink);font-size:11px;
  font-weight:700;display:grid;place-items:center;font-variant-numeric:tabular-nums;
  box-shadow:0 2px 6px rgba(0,0,0,.2)}
/* La collection emprunte le même jeu de liserés que les tuiles. */
.dash-coll{--liz:transparent}
.dash-coll.terracotta{--liz:var(--tu-terracotta)}
.dash-coll.sable{--liz:var(--tu-sable)}
.dash-coll.sauge{--liz:var(--tu-sauge)}
.dash-coll.teal{--liz:var(--tu-teal)}
.dash-coll.gold{--liz:var(--tu-gold)}
@media (hover:none){.dc-haut{box-shadow:inset 0 3px 0 var(--liz)}}
@media (prefers-reduced-motion:reduce){.dc-f,.dc-haut{transition:none}}

/* ── LE CADRAN ────────────────────────────────────────────────────────────
   Pour une valeur qui a un plafond connu. La pastille devient l'anneau de sa
   propre valeur et le chiffre s'installe au centre. La couleur ne bascule
   qu'au seuil : c'est ce qui en fait un signal · si trois cadrans sur quatre
   viraient au rouge, le rouge cesserait de vouloir dire quelque chose. */
.dash-cad{display:flex;align-items:center;gap:12px;height:100%;
  border-radius:var(--radius);background:var(--glass);border:1px solid var(--line);
  padding:13px 14px;color:var(--ink);text-decoration:none}
.dash-cad:hover{text-decoration:none;border-color:var(--glass-line)}
.dcad-box{position:relative;width:44px;height:44px;flex:0 0 auto;display:grid;place-items:center}
.dcad-anneau{position:absolute;inset:0;border-radius:50%;
  background:conic-gradient(var(--teal) calc(var(--v,0) * 1%),var(--line) 0);
  -webkit-mask:radial-gradient(circle,transparent 14px,#000 14.5px);
  mask:radial-gradient(circle,transparent 14px,#000 14.5px)}
.dash-cad.alerte .dcad-anneau{background:conic-gradient(var(--red) calc(var(--v,0) * 1%),var(--line) 0)}
.dcad-v{position:relative;font-size:12px;font-weight:700;font-variant-numeric:tabular-nums;
  letter-spacing:-.03em}
.dcad-muet{color:var(--dim);font-weight:500}
.dash-cad.alerte .dcad-v{color:var(--red-fg)}
.dcad-txt{min-width:0;display:flex;flex-direction:column}
.dcad-titre{font-size:13px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dcad-sub{font-size:11px;color:var(--muted);margin-top:1px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* ── LE CHIFFRE ───────────────────────────────────────────────────────────
   Pour un nombre SANS plafond · un anneau y mentirait, faute d'un tout
   auquel le rapporter. Le nombre devient le sujet, en grand et à plat, au
   lieu d'être une note en bas de tuile. Au survol, un bord sauge pousse la
   ligne vers la droite · le même bord d'état que le tableau et le champ. */
.dash-chif{position:relative;display:flex;align-items:center;gap:14px;height:100%;
  border-radius:var(--radius);background:var(--glass);border:1px solid var(--line);
  padding:13px 15px;color:var(--ink);text-decoration:none;overflow:hidden;
  transition:padding-left var(--t) var(--sortie),border-color var(--t) var(--sortie)}
.dash-chif::before{content:"";position:absolute;left:0;top:0;bottom:0;width:0;
  background:var(--teal);transition:width var(--t) var(--sortie)}
.dash-chif:hover{text-decoration:none;padding-left:19px;border-color:var(--glass-line)}
.dash-chif:hover::before{width:3px}
.dchif-n{font-size:23px;font-weight:700;letter-spacing:-.045em;line-height:1;
  font-variant-numeric:tabular-nums;flex:0 0 auto;white-space:nowrap}
.dchif-txt{min-width:0;display:flex;flex-direction:column;flex:1}
.dchif-titre{font-size:12.5px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dchif-sub{font-size:10.5px;color:var(--dim);margin-top:1px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dchif-fl{width:15px;height:15px;flex:0 0 auto;color:var(--dim);opacity:.45}
.dash-chif:hover .dchif-fl{opacity:.9;color:var(--teal-fg)}
@media (prefers-reduced-motion:reduce){
  .dash-chif,.dash-chif::before{transition:none}}
.dash-tuile-txt{min-width:0}
/* Le titre tient sur UNE ligne et se termine par des points de suspension s'il
   manque de place. Surtout pas de coupure de mot : « Portefeuille » doit devenir
   « Portefeui… », jamais « Portefeuill / e ». Le titre complet reste lisible au
   survol et pour les lecteurs d'écran, via l'attribut title de la tuile. */
.dash-tuile h3{margin:0;font-size:14px;line-height:1.2;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.dash-tuile .sub{color:var(--muted);font-size:11.5px;margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dash-espaceur{width:100%}

/* ---- ACCUEIL · le tableau de bord et sa colonne d'indicateurs ---------- */
/* Deux colonnes : le tableau de bord personnalisable à gauche, les
   indicateurs à droite. La largeur de la colonne est FIXE · en fraction, elle
   se serait élargie sur grand écran pour montrer les mêmes lignes courtes,
   et rétrécie sur portable là où elles ont besoin de place. */
.accueil{display:grid;grid-template-columns:minmax(0,1fr) 348px;gap:20px;align-items:start}

/* LA COLONNE NE POUSSE PAS LA HAUTEUR DE LA PAGE (demande de Romain,
   30/08/2026). Neuf widgets font près de deux écrans : en flux normal, la page
   entière prenait leur hauteur et le tableau de bord de gauche se retrouvait
   suivi d'un kilomètre de vide. La colonne est donc collée sous la barre du
   haut, bornée à la hauteur de la fenêtre, et DÉROULE SON PROPRE CONTENU.
   `overscroll-behavior` empêche la molette de repartir sur la page quand on
   arrive en bout de colonne : sans lui, on dépasse le dernier widget et c'est
   l'accueil qui saute. */
.ind-col{position:sticky;top:calc(var(--top-h) + 14px);
  max-height:calc(100vh - var(--top-h) - 28px);
  overflow-y:auto;overscroll-behavior:contain;
  /* La marge négative annule le retrait : l'ascenseur se pose SUR le vide de
     droite au lieu de rogner 6 px sur toutes les cartes. */
  padding-right:6px;margin-right:-6px;
  scrollbar-width:thin;scrollbar-color:var(--line) transparent}
.ind-col::-webkit-scrollbar{width:8px}
.ind-col::-webkit-scrollbar-thumb{background:var(--line);border-radius:99px}
.ind-col::-webkit-scrollbar-track{background:transparent}
.ind{display:flex;flex-direction:column;gap:14px}
.ind-sec{font-size:var(--t-micro);letter-spacing:.14em;text-transform:uppercase;
  color:var(--dim);padding:2px 4px 0}

/* LA CARTE. Le filet et l'ombre viennent des règles de thème plus haut, avec
   les autres cartes : deux cartes voisines qui ne se détacheraient pas de la
   même façon se remarquent tout de suite. */
.iw{background:var(--glass);border-radius:var(--radius);overflow:hidden}
/* L'EN-TÊTE · le titre est le lien. Une flèche seule obligerait à viser 12 px,
   et le titre, lui, fait toute la largeur de la carte. */
.iw-t{display:flex;align-items:center;gap:8px;padding:10px 12px 8px}
.iw-t a,.iw-t .iw-titre{color:var(--ink);font-size:12.5px;font-weight:600;letter-spacing:.01em;
  display:inline-flex;align-items:center;gap:6px;flex:1;min-width:0}
.iw-t a:hover{color:var(--teal-fg);text-decoration:none}
.iw-t .fl{opacity:.35;font-size:11px;transition:opacity var(--t) var(--sortie),transform var(--t) var(--sortie)}
.iw-t a:hover .fl{opacity:.9;transform:translateX(2px)}
.iw-t .tot{font-size:12px;font-weight:700;color:var(--soft);flex:0 0 auto}
.iw-t .n{flex:0 0 auto;min-width:19px;height:19px;padding:0 6px;border-radius:var(--pill);
  background:var(--chip);color:var(--muted);font-size:var(--t-puce);font-weight:700;
  display:inline-flex;align-items:center;justify-content:center}
.iw-t .n.chaud{background:var(--red-bg);color:var(--fr-red)}
.iw-t .n.tiede{background:var(--amber-bg);color:var(--amber-fg)}
.iw-corps{padding:0 12px 10px}
.iw-vide{padding:6px 0 4px;font-size:12px;color:var(--dim)}
.iw-pied{border-top:1px solid var(--hair);padding:7px 12px;font-size:11.5px;color:var(--dim);
  display:flex;align-items:center;gap:8px}
.iw-pied a{color:var(--muted)} .iw-pied a:hover{color:var(--teal-fg)}

/* LA LIGNE · deux étages seulement, le fait puis sa précision. Trois niveaux
   de gris dans 340 px de large ne se distinguent plus. */
.ili{display:flex;align-items:flex-start;gap:9px;padding:6px 0;border-top:1px solid var(--hair)}
.ili:first-child{border-top:0}
.ili .c{flex:1;min-width:0}
.ili .t{display:block;font-size:12.5px;color:var(--ink);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ili .s{display:block;font-size:11px;color:var(--dim);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ili .m{flex:0 0 auto;margin-top:1px;font-size:12.5px;font-weight:600;color:var(--soft)}
.ind-pastille{width:6px;height:6px;border-radius:50%;flex:0 0 auto;margin-top:6px}
.ih{flex:0 0 auto;width:38px;font-size:12px;font-weight:600;color:var(--muted)}
.ih.retard{color:var(--fr-red)}
/* LE COMPTE À REBOURS · une seule graduation pour toute la colonne. « J-17 »
   c'est dans dix-sept jours, « J+14 » c'est quatorze jours de retard, et le
   rouge n'apparaît QUE du côté du retard. Un badge qui changerait de sens d'un
   widget à l'autre coûterait plus qu'il n'apporterait. */
.ij{flex:0 0 auto;margin-top:1px;font-size:var(--t-puce);font-weight:700;padding:1px 6px;
  border-radius:var(--pill);background:var(--chip);color:var(--muted);letter-spacing:.02em;
  white-space:nowrap}
.ij.retard{background:var(--red-bg);color:var(--fr-red)}
.ij.proche{background:var(--amber-bg);color:var(--amber-fg)}
.ij.jour{background:var(--teal-bg);color:var(--teal-fg)}

/* LES TROIS BOÎTES · Mail, Messagerie, Chat. Elles étaient dans la barre du
   haut. Le filet qui les sépare est le fond de la grille, pas une bordure :
   trois bordures adjacentes font un trait double. */
.ind-boites{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;background:var(--hair)}
.ind-boite{background:var(--glass);padding:11px 8px;text-align:center;color:var(--muted);
  transition:background var(--t) var(--sortie)}
.ind-boite:hover{background:var(--chip);text-decoration:none}
.ind-boite .icon{width:16px;height:16px;display:block;margin:0 auto 5px;opacity:.55}
.ind-boite .l{font-size:var(--t-puce);display:block}
.ind-boite b{font-size:16px;font-weight:700;color:var(--dim);display:block;margin-top:2px}
.ind-boite.a-lire b,.ind-boite.a-lire .l{color:var(--teal-fg)}
.ind-boite.a-lire .icon{opacity:1;color:var(--teal-fg)}

/* LES SOLDES · masqués par défaut, démasqués le temps qu'on regarde. */
.ind-solde{display:flex;align-items:center;gap:10px;padding:8px 0;
  border-top:1px solid var(--hair);color:var(--ink)}
.ind-solde:first-child{border-top:0}
.ind-solde:hover{text-decoration:none}
.ind-solde:hover .t{color:var(--teal-fg)}
.ind-solde .c{flex:1;min-width:0}
.ind-solde .t{display:block;font-size:12.5px}
.ind-solde .s{display:block;font-size:11px;color:var(--dim)}
.ind-solde .v{font-size:15px;font-weight:700;letter-spacing:.01em;flex:0 0 auto}
.ind-solde .v.cache{color:var(--dim);letter-spacing:.12em}
.ind-oeil{flex:0 0 auto;width:26px;height:26px;padding:0;border:1px solid var(--line);
  background:transparent;color:var(--muted);border-radius:var(--radius-sm);cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;
  transition:color var(--t) var(--sortie),border-color var(--t) var(--sortie)}
.ind-oeil .icon{width:14px;height:14px}
.ind-oeil:hover{color:var(--ink);border-color:var(--teal)}
.ind-oeil[aria-pressed="true"]{color:var(--teal-fg);border-color:var(--teal)}

/* SOUS 1180 px la colonne passe SOUS le tableau de bord et redevient du flux
   normal : collée et bornée, elle offrirait un second ascenseur dans une page
   qui n'a plus qu'une colonne, et deux molettes imbriquées sur un téléphone
   sont une plaie. */
@media(max-width:1180px){
  .accueil{grid-template-columns:minmax(0,1fr)}
  .ind-col{position:static;max-height:none;overflow:visible;
    padding-right:0;margin-right:0}
}

/* ---- En-tête tableau de bord : œil-de-bœuf + bandeau de stats compact -- */
.eyebrow{text-transform:uppercase;letter-spacing:.14em;font-size:10.5px;color:var(--dim)}
/* LES COMPTEURS DE L'ACCUEIL · exactement la pastille « Village » d'à côté.
   Ils affichaient trois zéros en 23 px : sous un « Bonjour », trois grands
   chiffres qui ne disent rien pèsent plus lourd que ce qu'ils annoncent.

   Ils reprennent donc la pastille du bouton discret AU PIXEL - 30 px de haut,
   coin à 8 px, même fond, même filet, même ombre - parce qu'une pastille
   presque pareille est pire qu'une pastille franchement différente : l'œil voit
   l'écart sans pouvoir le nommer. C'est ce que donnait mon premier essai, tout
   rond quand son voisin ne l'est pas.

   Le nombre ne crie que s'il a quelque chose à dire : à zéro il s'efface, et
   c'est la seule chose qui distingue un compteur d'un simple raccourci. */
.statbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
/* « flex-direction » est écrit EXPRÈS. La règle générique « .stat » plus bas
   dans cette feuille empile en colonne ; elle pèse moins que celle-ci, mais une
   propriété qu'on ne redresse pas n'est pas écrasée pour autant - elle passe.
   C'est ce qui empilait icône, libellé et nombre sur mobile, le nombre finissant
   hors de la pastille. */
.statbar .stat{display:inline-flex;flex-direction:row;align-items:center;
  justify-content:flex-start;gap:7px;height:30px;padding:0 11px;
  border-radius:8px;border:1px solid var(--glass-line);background:var(--glass-2);
  color:var(--ink);font-size:12px;font-weight:600;white-space:nowrap;
  box-shadow:var(--ombre-1);
  transition:transform var(--t-doigt) var(--sortie),box-shadow var(--t) var(--sortie),
             border-color var(--t) var(--sortie),color var(--t) var(--sortie)}
.statbar .stat .icon{width:14px;height:14px;opacity:.7;flex:0 0 auto}
.statbar .stat b{font-size:12px;font-weight:700;color:var(--dim);
  font-variant-numeric:tabular-nums}
.statbar .stat.a-lire b{color:var(--teal-fg)}
.statbar .stat.a-lire .icon{opacity:1;color:var(--teal-fg)}
@media (hover:hover){
  .statbar .stat:hover{transform:translateY(-1px);box-shadow:var(--ombre-2);
    border-color:var(--teal);color:var(--teal-fg);text-decoration:none}
}
.statbar .stat:active{transform:translateY(0) scale(.985);box-shadow:var(--ombre-creuse)}

/* ---- Assistant IA : avatar + suggestions cliquables ------------------- */
.ia-avatar{width:42px;height:42px;border-radius:12px;flex:none;display:flex;align-items:center;
  justify-content:center;background:linear-gradient(150deg,#e0d6c6,#c9bdaa);color:#1a1610;
  box-shadow:0 0 0 1px rgba(201,189,170,.25)}
.ia-avatar .icon{width:21px;height:21px}
.ia-sugg{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}
.ia-sugg button{display:inline-flex;align-items:center;gap:7px;height:34px;padding:0 13px;
  border-radius:var(--pill);background:var(--ctl-bg);border:1px solid var(--glass-line);
  color:var(--soft);font-size:12.5px;font-family:inherit;cursor:pointer}
.ia-sugg button:hover{border-color:var(--teal);color:var(--teal-fg)}

/* ---- Arborescence de dossiers ----------------------------------------- */
.dnode{border:1px solid var(--hair);border-radius:10px;margin:6px 0;background:var(--solid-2)}
.dnode>summary{cursor:pointer;padding:9px 12px;display:flex;gap:8px;align-items:center;
  flex-wrap:wrap;list-style:none;border-radius:10px}
.dnode>summary::-webkit-details-marker{display:none}
.dnode>summary:hover{background:var(--hair)}
.dnode .dname{font-weight:550;display:inline-flex;align-items:center;gap:7px}
.dnode>summary .ren{cursor:pointer;color:var(--dim);display:inline-flex;align-items:center}
.dnode>summary .ren:hover{color:var(--teal)}
.dnode[open]>summary{border-bottom:1px solid var(--hair)}
.dbody{padding:6px 10px 10px 22px}
.docrow{padding:6px 10px;border-radius:8px;display:flex;align-items:center;gap:8px}
.docrow:hover{background:rgba(127,127,127,.07)} .docrow.root{border-bottom:1px solid var(--hair)}
.docrow[draggable]{cursor:grab} .docrow.dragging{opacity:.45}
.docrow a{display:inline-flex;align-items:center;gap:7px}
.docrow .grip{color:var(--dim);cursor:grab;display:inline-flex}
.dnode>summary.dragover,.droproot.dragover{outline:2px dashed var(--teal);outline-offset:-2px;
  background:var(--teal-bg)}
.droproot{padding:8px 12px;margin:2px 0 8px;border:1px dashed var(--line);border-radius:9px;
  color:var(--muted);font-size:12.5px;display:flex;align-items:center;gap:7px}
.dactions{margin-top:8px;padding-top:8px;border-top:1px dashed var(--line);
  display:flex;gap:8px;flex-wrap:wrap;align-items:center}
.dmove{margin-top:8px}

/* ---- Formulaires de création REPLIÉS (bouton « + Nouveau X » → formulaire) */
.cform>summary{list-style:none;cursor:pointer;display:flex;align-items:center;justify-content:center;
  gap:8px;height:46px;border-radius:12px;border:1px dashed var(--line);background:var(--solid);
  color:var(--ink);font-weight:600;font-size:13px}
.cform>summary::-webkit-details-marker{display:none}
.cform>summary:hover{border-color:var(--teal)}
.cform[open]>summary{border-style:solid;margin-bottom:10px}
.cform.dropping>summary{border-style:solid;border-color:var(--teal);background:var(--teal-bg);color:var(--teal-fg)}
.cform .panel{margin:0}

/* ---- Actions de ligne : action principale labellisée + « … » + méta à droite */
.rowname{flex:0 1 auto;min-width:0;font-weight:550;display:inline-flex;align-items:center;gap:7px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rowmenu{display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;
  border-radius:8px;color:var(--dim);flex:none}
.rowmenu:hover{color:var(--teal);background:var(--hair)}

/* ---- Listes denses façon Drive : survol de ligne + actions au survol ---- */
.crow:hover{background:rgba(127,127,127,.07)}
tbody tr:hover{background:rgba(127,127,127,.06)}
/* Actions de ligne : masquées par défaut, révélées au survol de la ligne.
   L'espace reste réservé (opacity, pas display) → aucun décalage au survol. */
.rowact{margin-left:auto;display:inline-flex;align-items:center;gap:6px;
  opacity:0;transition:opacity .12s ease}
.docrow:hover .rowact,.crow:hover .rowact,.trow:hover .rowact,
tbody tr:hover .rowact,.rowact:focus-within{opacity:1}
/* Écrans tactiles (pas de survol possible) : toujours visibles */
@media (hover:none){.rowact{opacity:1}}

/* ---- La barre latérale repliable --------------------------------------
   Toute la mécanique tient dans « --side-w » : la grille de « .app » lit cette
   variable, et il n'y a donc qu'UN nombre à changer. Le reste n'est que du
   rangement pour que le liseré reste lisible.

   POURQUOI PAS UNE BARRE D'ICÔNES · c'est ce à quoi on pense d'abord, mais 75
   des 139 entrées du menu n'ont aucune icône : la barre repliée serait une
   colonne de lignes vides. On replie donc sur un liseré qui garde le logo et
   le bouton, et l'on gagne les 170 px qui comptent. */
.barre-bt{margin-left:auto;flex:none;width:26px;height:26px;border-radius:8px;
  display:flex;align-items:center;justify-content:center;cursor:pointer;
  background:none;border:1px solid var(--glass-line);color:var(--dim)}
.barre-bt:hover{color:var(--ink);border-color:var(--hair)}
.barre-bt .icon{width:14px;height:14px;transform:rotate(180deg);transition:transform .18s ease}

html[data-barre="replie"]{--side-w:62px}
html[data-barre="replie"] .brandbox{flex-direction:column;gap:8px;padding:14px 6px 12px}
html[data-barre="replie"] .brandbox .txt{display:none}
html[data-barre="replie"] .logo{width:32px;height:32px;border-radius:9px}
html[data-barre="replie"] .barre-bt{margin-left:0}
html[data-barre="replie"] .barre-bt .icon{transform:none}

/* LE TEXTE DISPARAÎT PAR « font-size:0 », et ce n'est pas un pis-aller.
   Les libellés du menu sont des NŒUDS DE TEXTE nus, pas des éléments : aucun
   sélecteur ne les atteint. Les envelopper d'un « span » demanderait de
   toucher six endroits du gabarit ET le menu de repli codé en dur, qui en
   compte trente - et la moindre entrée oubliée réapparaîtrait en toutes
   lettres dans un rail de 62 px. Les icônes sont dimensionnées en pixels,
   elles ne bougent pas. */
/* ON CACHE LE LIBELLÉ, ON NE LE RÉDUIT PLUS.
   Deux tentatives ont échoué avant celle-ci, et chacune pour une raison
   propre à un moteur :
     · « font-size:0 » laisse sous Safari un résidu de largeur PROPORTIONNEL à
       la longueur du mot, qui décalait l'icône · invisible sous Chrome ;
     · centrer l'icône en absolu avec un « transform » l'a fait DISPARAÎTRE
       sous Safari, qui ne résout pas un translate en pourcentage sur un
       élément SVG comme Chrome · combiné à « overflow:hidden », les icônes
       sortaient de leur case.
   Le libellé est donc dans un « span » qu'on éteint franchement. L'icône
   reste dans le flux, exactement comme lorsqu'elle s'affichait correctement,
   et plus aucun calcul ne dépend d'un texte caché. */
html[data-barre="replie"] .nav .lbl{display:none}
html[data-barre="replie"] .nav a,
html[data-barre="replie"] .nav button{justify-content:center;gap:0;
  padding-left:0;padding-right:0;position:relative}
html[data-barre="replie"] .nav .icon{width:19px;height:19px}
/* LE POINT REMPLACE L'ICÔNE ABSENTE, et UNIQUEMENT une fois repliée · la barre
   dépliée doit rester exactement ce qu'elle était. D'où un élément qui ne
   paraît que là : forcer une icône partout aurait changé les 139 entrées du
   menu déplié pour résoudre un problème qui n'existe qu'à 62 px. */
.nav .pip{display:none}
html[data-barre="replie"] .nav .pip{display:block;width:7px;height:7px;
  border-radius:50%;background:currentColor;opacity:.55;flex:none;margin:0}
html[data-barre="replie"] .nav a.active .pip,
html[data-barre="replie"] .nav .active .pip{opacity:1}
/* Ce qui n'a QUE du texte n'a plus rien à montrer · les garder laisserait des
   trous réguliers dont on ne comprendrait pas l'origine. */
html[data-barre="replie"] .nav .section,
html[data-barre="replie"] .nav .hint,
html[data-barre="replie"] .nav .chev{display:none}
/* Les niveaux s'alignent : replié, un rail n'a plus de hiérarchie à montrer,
   et l'indentation ne ferait que décentrer les icônes. */
html[data-barre="replie"] .nav .fam-kids,
html[data-barre="replie"] .nav .grp-kids{padding-left:0;margin-left:0;border-left:0}
/* LE FILET DES SOUS-GROUPES · je l'avais oublié en remettant les marges à
   zéro. Un seul pixel de bordure à gauche rétrécit la largeur disponible et
   décale de moitié TOUT ce qu'elle contient · mesuré à 31 au lieu de 30,5.
   Replié, ce filet n'indique plus aucune hiérarchie de toute façon.

   LA FLÈCHE « LIEN EXTERNE » DISPARAÎT AUSSI, et c'est le gros morceau : les
   quatre entrées qui s'ouvrent en nouvel onglet rendent DEUX icônes, qui se
   partagent alors les 37 px utiles. La vraie icône tombait à 25 et la flèche à
   40,5, soit jusqu'à dix pixels d'écart · c'est ce que Romain voyait. À 62 px,
   un marqueur de 12 px ne dit rien que l'infobulle ne dise mieux. */
html[data-barre="replie"] .nav a>svg.icon:last-child:not(:first-child){display:none}
html[data-barre="replie"] .nav .fam-head,
html[data-barre="replie"] .nav .grp-head{border-radius:9px}
/* PAS DE BARRE DE DÉFILEMENT DANS LE RAIL · c'est elle qui décentrait les
   icônes, et de façon INTERMITTENTE : elle n'apparaît que lorsque le menu
   dépasse la hauteur de l'écran, donc le défaut se voyait chez Romain et pas
   sur un banc à fenêtre haute. Dans 62 px elle mange un quart de la largeur
   utile pour ne rien montrer de lisible ; la molette et le clavier défilent
   toujours. */
html[data-barre="replie"] .nav{scrollbar-width:none}
html[data-barre="replie"] .nav::-webkit-scrollbar{width:0;height:0}
/* LE LISERÉ DE L'ENTRÉE ACTIVE DÉBORDE DU RAIL · replié, le fond arrondi dit
   déjà laquelle est active, et le trait ne fait plus qu'une barre noire collée
   au bord de l'écran. */
html[data-barre="replie"] .nav a.active::before{display:none}
/* LA PASTILLE DEVIENT UN POINT · son chiffre ne tient pas, mais un menu qui
   cesse de signaler le courrier non lu quand on le replie serait un menu qu'on
   n'ose plus replier. */
html[data-barre="replie"] .nbadge{position:absolute;top:4px;right:6px;
  min-width:8px;width:8px;height:8px;padding:0;border-radius:50%;
  font-size:0;line-height:0}
html[data-barre="replie"] .nav a,
html[data-barre="replie"] .nav .fam-label,
html[data-barre="replie"] .nav .grp-label{position:relative}

/* LE PIED SUIT LA MÊME RÈGLE · ses quatre commandes portent toutes une icône,
   donc « font-size:0 » les rend muettes sans les faire disparaître. Le nom et
   « Mes sessions » n'ont, eux, rien à montrer une fois le texte parti : on les
   range plutôt que de laisser deux lignes vides. Les deux boutons passent en
   colonne · côte à côte dans 62 px, ils se chevauchaient. */
html[data-barre="replie"] .sidebar .foot{padding:10px 6px;gap:6px}
html[data-barre="replie"] .sidebar .foot .who{display:none}
html[data-barre="replie"] .sidebar .foot a,
html[data-barre="replie"] .sidebar .foot button{font-size:0;gap:0;
  justify-content:center;padding-left:0;padding-right:0}
html[data-barre="replie"] .sidebar .foot .icon{width:17px;height:17px}
html[data-barre="replie"] .sidebar .foot>div{flex-direction:column;gap:6px}

/* L'INFOBULLE DU RAIL · posée sur le BODY, jamais dans la barre.
   Deux raisons, et chacune suffirait : « .nav » a un « overflow-y:auto » qui
   découperait net une bulle débordant à droite ; et « .sidebar » porte un
   « backdrop-filter », lequel fait d'elle le bloc conteneur de tout enfant en
   « position:fixed » - la bulle s'y retrouverait enfermée dans 62 px.
   Elle paraît SANS DÉLAI : l'infobulle du navigateur met une seconde, ce qui
   rend un rail d'icônes inutilisable. */
.rail-tip{position:fixed;z-index:80;pointer-events:none;opacity:0;
  transform:translateX(-4px);transition:opacity .09s ease,transform .09s ease;
  background:var(--solid);color:var(--ink);border:1px solid var(--hair);
  border-radius:8px;padding:5px 10px;font-size:12.5px;white-space:nowrap;
  box-shadow:0 8px 22px -10px rgba(0,0,0,.55);max-width:min(46vw,340px);
  overflow:hidden;text-overflow:ellipsis}
.rail-tip.on{opacity:1;transform:none}
@media (prefers-reduced-motion:reduce){.rail-tip{transition:none}}

.app{transition:grid-template-columns .18s ease}
@media (prefers-reduced-motion:reduce){.app{transition:none}}

/* ---- Mobile ----------------------------------------------------------- */
@media (max-width:860px){
  .app{grid-template-columns:1fr}
  .sidebar{position:fixed;left:0;top:0;width:262px;transform:translateX(-100%);
    transition:transform .22s ease}
  .sidebar.open{transform:none}
  /* SOUS 860 px LA BARRE EST UN TIROIR, pas une colonne · le repli n'a plus de
     sens, et laisser ses règles s'appliquer donnerait un tiroir de 62 px qui
     ne montre rien. On les neutralise toutes. */
  html[data-barre="replie"]{--side-w:0px}
  html[data-barre="replie"] .sidebar .nav,
  html[data-barre="replie"] .sidebar .foot{display:flex}
  html[data-barre="replie"] .brandbox{flex-direction:row;gap:12px;padding:18px 18px 16px}
  html[data-barre="replie"] .brandbox .txt{display:flex}
  html[data-barre="replie"] .logo{width:42px;height:42px;border-radius:11px}
  html[data-barre="replie"] .barre-bt{display:none}
  .barre-bt{display:none}
  .menu-btn{display:inline-flex}
  .scrim.show{display:block;position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:25}
  /* LE CALAGE DU HAUT SE REDIT ICI · cette règle écrasait « padding-top:
     var(--safe-haut) » de la règle générale (même sélecteur, plus bas dans le
     fichier), et le contenu de la barre remontait sous la barre d'état de
     l'iPhone pendant que la barre, elle, gardait sa hauteur augmentée.
     Signalé par Romain le 19/09/2026, capture à l'appui. Un raccourci
     « padding » n'est jamais anodin : il repose les quatre côtés. */
  .topbar{padding:calc(var(--safe-haut) + var(--marge-barre)) 14px 0;gap:8px}
  .topbar .title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .page{padding:16px}
  .clockbox{display:none}
  .hpill.hia{display:none}        /* jauges IA/RAM masquées sur mobile (place + lisibilité) */
  .hpills{gap:6px}
  .mailhead .mh-actions{width:100%}   /* actions d'en-tête sur leur propre ligne, sans déborder */
  [style*="grid-template-columns"]{grid-template-columns:1fr !important}
}

/* ---- Catalogue médias (chantier NAS/DAS) ----------------------------- */
.srcgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px;margin-top:12px}
.srccard{background:var(--solid-2);border:1px solid var(--line);border-radius:12px;padding:13px}
.srctop{display:flex;align-items:center;gap:8px;margin-bottom:6px;flex-wrap:wrap}
.srctop b{font-size:13.5px}
.srcdisk{margin-top:8px}
.srcdisk-bar{height:7px;border-radius:6px;background:var(--ctl-bg);border:1px solid var(--hair);overflow:hidden}
.srcdisk-fill{display:block;height:100%;border-radius:6px;background:var(--teal);transition:width .3s}
.srcdisk-fill.warn{background:var(--amber)}
.srcdisk-fill.full{background:var(--red)}
.srcdisk-txt{display:flex;justify-content:space-between;gap:8px;margin-top:4px}
label.inline{display:inline-flex;align-items:center;gap:6px;font-size:13px;white-space:nowrap}
tr.dim td{opacity:.5}

/* ---- Catalogue médias : bascule + arborescence (explorateur) ---------- */
.segmented{display:inline-flex;border:1px solid var(--line);border-radius:9px;overflow:hidden}
.segmented .seg{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;font-size:12.5px;
  color:var(--muted);background:var(--ctl-bg)}
.segmented .seg:hover{color:var(--ink);text-decoration:none}
.segmented .seg.on{background:var(--teal-bg);color:var(--teal-fg);font-weight:550}
.segmented .seg+.seg{border-left:1px solid var(--line)}

.tree{min-width:600px;padding:2px 0}
.trow{display:grid;grid-template-columns:minmax(200px,1fr) 58px 92px 180px 84px;align-items:center;
  gap:8px;padding:5px 12px;border-bottom:1px solid var(--hair)}
.trow:last-child{border-bottom:0}
.trow.thead{font-size:10.5px;text-transform:uppercase;letter-spacing:.06em;color:var(--dim);
  font-weight:600;position:sticky;top:0;background:var(--solid);z-index:1}
.trow.folder{cursor:pointer}
.trow:not(.thead):hover{background:rgba(127,127,127,.07)}
.trow.dim{opacity:.5}
.tname{display:flex;align-items:center;gap:7px;min-width:0}
.tname .ell{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tname .icon{flex:none;opacity:.92}
.caret{display:inline-flex;width:15px;flex:none;color:var(--dim);transition:transform .12s}
.caret.empty{visibility:hidden}
.trow.folder.open>.tname>.caret{transform:rotate(90deg)}
.tkids[hidden]{display:none}
.srccard.inactive{opacity:.5}

/* ---- Actions fichier du catalogue (lot 2a) ---------------------------- */
.fopen{cursor:pointer;color:var(--ink)}
.fopen:hover{color:var(--teal-fg);text-decoration:none}
.femport{flex:none;opacity:.6;color:var(--teal-fg)}
.femport:hover{opacity:1;color:var(--teal-fg);text-decoration:none}
.trow:hover .femport,tr:hover .femport{opacity:1}
.spin{animation:spin 1s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}

/* ---- Barre de progression (import en lot) ----------------------------- */
.progbar{height:10px;border-radius:6px;background:var(--line);overflow:hidden}
.progfill{height:100%;background:linear-gradient(90deg,var(--teal),var(--teal-bright));
  border-radius:6px;transition:width .4s ease}

/* ---- Pastilles de notification du header (mail / messagerie) ---------- */
.hpills{display:flex;gap:6px;align-items:center;flex:0 0 auto}
/* `flex:0 0 auto` · sans lui, ces pastilles sont des ENFANTS DE FLEX et se
   laissent comprimer quand la barre manque de place : sur mobile, la cloche
   des rappels sortait ovale (relevé par Romain le 27/08/2026). Une largeur
   fixe ne suffit pas à un enfant de flex, il faut lui interdire de rétrécir. */
.hpill{position:relative;display:inline-flex;align-items:center;justify-content:center;
  flex:0 0 auto;width:30px;height:30px;border-radius:8px;color:var(--muted);
  background:var(--ctl-bg);border:1px solid var(--glass-line);cursor:default}
a.hpill{cursor:pointer}
.hpill:hover{color:var(--ink);text-decoration:none}
.hpill .icon{width:15px;height:15px}
.hpill.alert{color:var(--red);border-color:rgba(240,138,138,.55);background:var(--red-bg)}
.hpill.hia{width:auto;padding:0 9px;gap:6px;flex:0 1 auto}
.hlab{font-weight:700;font-size:.64rem;letter-spacing:.02em}
.hpill .hbar{display:inline-block;width:42px;height:6px;border-radius:4px;
  background:rgba(127,127,127,.3);overflow:hidden;vertical-align:middle}
.hpill .hbar>span{display:block;height:100%;background:currentColor}
/* Barre MULTI-FOURNISSEUR : une seule jauge IA, segmentée par la part de chacun
   dans la dépense du mois. Le partage se fait en flex-grow et non en largeurs
   calculées : le navigateur répartit au pixel près, là où des pourcentages
   arrondis laisseraient un liseré vide au bout. Les segments gardent leur
   couleur propre, d'où le `background:none` qui neutralise le currentColor
   hérité de la règle ci-dessus. */
.hpill .hbar.hmix{display:flex;width:54px;background:rgba(127,127,127,.22)}
/* Un plancher d'1 px, pas davantage : un fournisseur qui a consommé ne doit pas
   disparaître, mais la partie colorée reste une MESURE. La gonfler pour rendre
   le code couleur joli mentirait sur le remplissage, qui est justement ce que la
   jauge sert à lire. */
.hpill .hbar.hmix>span{flex-basis:0;min-width:1px;background:none}
/* Le reste à consommer : transparent, il laisse voir la piste grise. Aucune
   largeur minimale · c'est un vide, pas une valeur à rendre visible. */
.hpill .hbar.hmix>.hvide{min-width:0;background:none}
/* En alerte la pastille vire au rouge ; les segments, eux, gardent leur teinte
   pour qu'on voie TOUJOURS quel fournisseur pèse. */
.hpill.hia.alert .hbar.hmix{background:rgba(240,138,138,.22)}
.hcount{position:absolute;top:-5px;right:-5px;min-width:16px;height:16px;padding:0 4px;
  border-radius:8px;background:var(--red);color:#fff;font-size:10px;font-weight:700;
  display:flex;align-items:center;justify-content:center;line-height:1;
  box-shadow:0 0 0 2px var(--glass)}
/* L'attribut `hidden` est de spécificité NULLE : le `display:flex` ci-dessus le
   neutralise en silence, et la pastille rouge des rappels restait affichée avec
   un « 0 » dedans - une alerte pour rien. Quatrième rencontre avec ce piège sur
   ce projet. La règle vaut pour TOUTES les pastilles de l'en-tête (mail,
   messagerie, RDV, rappels), qui partagent cette classe. */
.hcount[hidden]{display:none}

/* --- Client mail (courrier) : en-tête + barre + arbo + liste messages --- */
.mailhead{display:flex;align-items:flex-end;justify-content:space-between;gap:14px;
  flex-wrap:wrap;margin-bottom:14px}
.mailhead .mh-actions{display:flex;gap:6px;align-items:center;flex-wrap:wrap}
.mailbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:14px}
.mailsearch{flex:1 1 240px;min-width:200px;display:flex;align-items:center;gap:8px;
  height:var(--ctl-h);padding:0 11px;background:var(--ctl-bg);border:1px solid var(--line);
  border-radius:var(--radius-sm)}
.mailsearch:focus-within{border-color:var(--teal)}
.mailsearch .icon{width:16px;height:16px;color:var(--dim);flex:none}
.mailsearch input{flex:1;min-width:0;border:0;background:none;padding:0;height:auto}
.mailsearch input:focus{outline:none}
.frow{display:flex;align-items:center;gap:6px;padding:4px 6px;border-radius:8px;
  text-decoration:none;color:var(--ink);font-size:13px}
.frow:hover{background:rgba(127,127,127,.09)}
.frow.on{background:var(--teal-bg);color:var(--teal-fg);font-weight:550}
.frow .tname{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.frow .icon{flex:none;opacity:.85;width:15px;height:15px}
.mlist{display:flex;flex-direction:column}
.mrow{display:grid;grid-template-columns:14px minmax(120px,1.1fr) minmax(160px,2fr) 116px;
  align-items:center;gap:10px;padding:8px 6px;border-bottom:1px solid var(--hair);
  text-decoration:none;color:var(--ink)}
.mrow:last-child{border-bottom:0}
.mrow:hover{background:rgba(127,127,127,.07)}
.mrow.unread{font-weight:650}
.mrow .mdot{display:flex;align-items:center;justify-content:center}
.mrow .mdot .dot{width:7px;height:7px;border-radius:50%;background:var(--teal);flex:none}
.mrow .mfrom,.mrow .msubj{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mrow .mdate{text-align:right}

/* --- Mail : ligne dossier (lien + badge + bouton sourdine) --- */
.frow .fname{flex:1;display:flex;align-items:center;gap:6px;min-width:0;
  text-decoration:none;color:inherit;overflow:hidden}
.frow .fmute{margin:0;display:flex;flex:none}
.frow .fmute button{background:none;border:0;cursor:pointer;color:var(--dim);
  padding:2px;display:flex;opacity:0;transition:opacity .12s,color .12s}
.frow:hover .fmute button{opacity:.7}
.frow .fmute.on button{opacity:.5}
.frow .fmute button:hover{opacity:1;color:var(--ink)}

/* --- Barres de défilement discrètes (thème sombre, toutes pages) --- */
*{scrollbar-width:thin;scrollbar-color:rgba(130,145,165,.32) transparent}
::-webkit-scrollbar{width:11px;height:11px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:rgba(130,145,165,.28);border-radius:10px;
  border:3px solid transparent;background-clip:content-box}
::-webkit-scrollbar-thumb:hover{background:rgba(130,145,165,.5);background-clip:content-box}
::-webkit-scrollbar-corner{background:transparent}

/* --- Production : actions inline sur une ligne d'arbre --- */
.frow .facts{display:flex;gap:1px;flex:none;align-items:center}
.frow .facts form{margin:0;display:flex}
.frow .facts button{background:none;border:0;cursor:pointer;color:var(--dim);
  padding:2px 4px;display:flex;opacity:.4;transition:opacity .12s,color .12s}
.frow:hover .facts button{opacity:.8}
.frow .facts button:hover{opacity:1;color:var(--teal-fg)}

/* --- Mode embarqué (page dans un onglet/iframe) : masquer la coquille --- */
html.embarque .sidebar,html.embarque .topbar,html.embarque .scrim,html.embarque .menu-btn{display:none!important}
html.embarque .app{grid-template-columns:1fr}
html.embarque body{background:transparent}
html.embarque .page{padding:14px 16px}

/* --- Espace de travail à onglets --- */
.espace{display:flex;flex-direction:column;height:calc(100vh - var(--top-h))}
.ongletbar{display:flex;align-items:center;gap:4px;overflow-x:auto;padding:8px 10px 0;flex:none;
  background:var(--glass-2);border-bottom:1px solid var(--glass-line);border-radius:14px 14px 0 0}
.onglet{display:flex;align-items:center;gap:7px;padding:8px 11px;border-radius:10px 10px 0 0;
  background:var(--ctl-bg);border:1px solid var(--glass-line);border-bottom:0;font-size:12.5px;
  color:var(--muted);cursor:pointer;white-space:nowrap;user-select:none;position:relative;top:1px}
.onglet:hover{color:var(--ink)}
.onglet.actif{background:var(--solid);color:var(--ink);font-weight:550;box-shadow:0 -2px 0 var(--teal) inset}
.onglet.dans-split{box-shadow:0 -2px 0 var(--blue) inset}
.onglet .icon{width:14px;height:14px;flex:none}
.onglet .o-act{opacity:.45;display:flex;padding:1px;border-radius:5px}
.onglet .o-act:hover{opacity:1;background:rgba(127,127,127,.18)}
/* UN ÉPINGLÉ SE VOIT, ET PAS SEULEMENT À LA COULEUR. La règle ne posait
   qu'`opacity:1` et `--teal-fg` · or ce jeton vaut #26241f en thème clair,
   c'est-à-dire presque noir, sur un glyphe déjà presque noir. La seule
   différence avec un onglet libre était donc une opacité de .45 à 1 : invisible.
   Romain en a conclu que la croix de fermeture avait disparu (06/09/2026), alors
   que ses onglets étaient simplement épinglés — et un épinglé n'a pas de croix.
   La distinction porte maintenant sur une FORME (pastille pleine), qui tient
   dans les deux thèmes. Voir la leçon « jamais par la seule couleur ». */
.onglet.epingle .o-epingle{opacity:1;color:var(--teal-ink);background:var(--teal);
  padding:2px;border-radius:5px}
.onglet.epingle .o-epingle:hover{background:var(--teal-bright)}
.onglet.glisse{opacity:.4}
.onglet.survol{outline:2px dashed var(--teal);outline-offset:-1px}
.onglet-add{display:flex;align-items:center;gap:5px;padding:7px 9px;border-radius:8px;color:var(--muted);
  background:none;border:1px solid var(--glass-line);cursor:pointer;font-size:12.5px}
.onglet-add:hover{color:var(--teal-fg);border-color:var(--teal)}
.onglet-menu{position:fixed;z-index:200;background:var(--solid);border:1px solid var(--line);
  border-radius:10px;box-shadow:var(--shadow);padding:6px;min-width:200px;display:none}
.onglet-menu.on{display:block}
.onglet-menu a{display:flex;align-items:center;gap:8px;padding:7px 9px;border-radius:7px;
  color:var(--ink);font-size:13px;cursor:pointer}
.onglet-menu a:hover{background:var(--teal-bg);color:var(--teal-fg);text-decoration:none}
.onglet-panes{display:flex;flex:1;min-height:0;background:var(--solid);
  border:1px solid var(--glass-line);border-top:0;border-radius:0 0 14px 14px;overflow:hidden}
.onglet-pane{flex:1;min-width:0;border:0;width:100%;height:100%}
.onglet-pane+.onglet-pane{border-left:3px solid var(--glass-line)}
.espace-vide{flex:1;display:flex;align-items:center;justify-content:center;color:var(--dim)}

/* Poignée glissable entre 2 panneaux splités */
.onglet-split{flex:none;width:8px;cursor:col-resize;background:var(--glass-line);
  display:flex;align-items:center;justify-content:center;touch-action:none;transition:background .12s}
.onglet-split:hover,.onglet-split:active{background:var(--teal)}
.onglet-split::after{content:"";width:3px;height:34px;border-radius:3px;background:rgba(255,255,255,.3)}

/* Stripboard (plan de travail, jalon 5) */
.board{display:flex;flex-direction:column;gap:12px}
.zone{border:1px solid var(--glass-line);border-radius:12px;background:var(--glass-2);padding:10px}
.zone.backlog{border-style:dashed}
.zonehead{display:flex;align-items:center;gap:8px;margin-bottom:8px;flex-wrap:wrap}
.zonebody{display:flex;flex-direction:column;gap:6px;min-height:38px;border-radius:8px;padding:2px}
.zonebody.survol{outline:2px dashed var(--teal);outline-offset:1px;background:var(--teal-bg)}
.seg-edit{display:none}.seg-edit.on{display:block}
.strip{display:grid;grid-template-columns:48px 52px 1fr auto;gap:10px;align-items:center;
  padding:7px 10px;border-radius:8px;border:1px solid var(--glass-line);border-left:5px solid var(--glass-line);
  background:var(--solid);cursor:grab;user-select:none}
.strip:active{cursor:grabbing}
.strip.glisse{opacity:.35}
.strip.archive{opacity:.6;filter:grayscale(.4)}
.strip .snum{font-weight:700;font-variant-numeric:tabular-nums;color:var(--muted);text-align:center}
.strip .seffet{font-size:11px;font-weight:700;letter-spacing:.04em;color:var(--muted)}
.strip .sbody{min-width:0;display:flex;flex-direction:column;gap:1px}
.strip .sbody b{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.strip .smeta{display:flex;align-items:center;gap:8px}
.strip .smtags{display:flex;align-items:center;gap:6px;flex-wrap:wrap;justify-content:flex-end}
.strip .o-strip{padding:3px 6px}
.strip.estplan{border-style:dashed;margin-left:18px}
.strip.estplan .seffet{color:var(--teal-fg);font-size:10px}
.strip .psuf{color:var(--teal-fg);font-weight:600}
.strip.m-jour{border-left-color:#f4c430}
.strip.m-nuit{border-left-color:#4263eb}
.strip.m-aube{border-left-color:#e8709b}
.strip.m-crepuscule{border-left-color:#9c6ade}
.zone.type-jour_off{opacity:.75}
.zone.type-banner{background:var(--teal-bg)}

/* Rapports one-line + DOOD (jalon 6) */
.rtable{width:100%;border-collapse:collapse;font-size:var(--t-tab)}
.rtable th,.rtable td{border:1px solid var(--glass-line);padding:5px 8px;text-align:left;vertical-align:top}
.rtable thead th{background:var(--glass-2)}
.rtable .mono{font-variant-numeric:tabular-nums;white-space:nowrap}
/* En-têtes triables (tri serveur asc/desc) */
.rtable th.th-sort{padding:0}
.rtable th.th-sort>a{display:flex;align-items:center;gap:4px;padding:5px 8px;color:var(--ink);text-decoration:none;white-space:nowrap;font-weight:600}
.rtable th.th-sort>a:hover{background:rgba(255,255,255,.05)}
.rtable th.th-sort.right>a{justify-content:flex-end}
.th-arrow{width:12px;height:12px;transform:rotate(-90deg);opacity:0;transition:opacity .1s,transform .1s;flex:none}
.rtable th.th-sort>a:hover .th-arrow{opacity:.4}
.th-arrow.on{opacity:.95}
.th-arrow.rev{transform:rotate(90deg)}
/* Responsive : sous 760px, le tableau défile horizontalement au lieu de rogner */
@media (max-width:760px){
  .rtable{display:block;overflow-x:auto;-webkit-overflow-scrolling:touch;white-space:nowrap}
  .rtable thead th{position:static}
}
.ol .ol-jour td{background:var(--teal-bg);font-weight:700;color:var(--teal-fg)}
.ol .ol-plan td{color:var(--muted);font-style:italic}
.dood .dcol{text-align:center;min-width:42px}
.dood .sticky{position:sticky;left:0;background:var(--solid);z-index:1}
.dood thead .sticky{z-index:2}
.dood .dcell{text-align:center;font-weight:700;font-size:12px}
.dood .d-D{background:#d3f9d8;color:#1b5e20}
.dood .d-T{background:#e7f5ff;color:#1864ab}
.dood .d-A{background:#fff3bf;color:#856404}
.dood .d-F{background:#ffe3e3;color:#a61e1e}
.dood .d-DF{background:#d0bfff;color:#5f3dc4}
.dood .d-none{background:transparent}
.fgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:10px}
.fgrid label{display:flex;flex-direction:column;gap:3px;font-size:12px;color:var(--muted)}
.fgrid input{font-size:13px}
.fgrid .pv{background:var(--glass-2);color:var(--ink)}
/* Budget : arbre de devis dépliable (lot 2) */
#btree{display:none}
#btree.pret{display:table-row-group}
.budget td{padding:5px 8px}
.budget .tnode.parent{cursor:pointer}
.budget .tnode.parent:hover td{background:var(--glass-2)}
.budget .caret,.budget .caret-all{background:none;border:0;cursor:pointer;color:var(--muted);padding:0 1px;vertical-align:-2px}
.budget .caret .icon{width:13px;height:13px;transition:transform .12s}
.budget .tnode.ouvert .caret .icon{transform:rotate(90deg)}
.budget .caret-all{font-size:11px;opacity:.4}
.budget .tnode:hover .caret-all{opacity:1}
.budget .caret-vide{display:inline-block;width:16px}
.budget .tl td{background:var(--glass-2)}
/* Distinction visuelle : agrégats (postes) vs feuilles éditables vs charges */
.budget .tnode.agregat>td:nth-child(2){font-weight:700}
.budget .tnode.agregat>td:first-child{font-weight:700}
.budget .tnode.saisie>td:first-child{box-shadow:inset 3px 0 0 var(--teal)}
.budget .tnode.saisie>td:nth-child(2){color:var(--teal-fg)}
.budget .tnode.saisie .edit-hint{width:12px;height:12px;opacity:.55;vertical-align:-1px;margin-left:4px}
.budget .tnode.charge_sociale>td:nth-child(2){opacity:.6;font-style:italic}
.budget .ligne-form input,.budget .ligne-form select{font-size:12px;height:28px;border:1px solid var(--glass-line);background:var(--solid)}
.budget .ligne-form.ajout input,.budget .ligne-form.ajout select{background:var(--teal-bg)}
.budget .qcell{display:flex;gap:2px;min-width:0}
.budget .qcell .gsel{width:46px;font-size:10.5px;flex:none;padding:0 2px}
.budget .qcell input{flex:1;min-width:0;width:100%}
.budget .qcell input:disabled{opacity:.6;font-style:italic;color:var(--teal-fg)}
.budget .lf-fields{display:grid;grid-template-columns:26px 1fr 100px 150px 88px 140px 80px 100px 32px;gap:6px;align-items:center}
.budget .lf-groupes{display:flex;flex-wrap:wrap;gap:8px;align-items:center;padding:3px 0 1px 32px}
.budget .aff-sel{font-size:11px;height:24px;max-width:240px}
.gchip{display:inline-flex;align-items:center;gap:3px;font-size:11.5px;color:var(--muted);cursor:pointer}
.gchip input{height:auto!important;width:auto!important;margin:0;border:0;background:none}
.code-pick{max-width:150px;font-size:11px}
.frow{padding:8px 4px;border-top:1px solid var(--glass-line)}
.frow:first-of-type{border-top:0}
.fres{margin-top:5px}
.recap{max-width:540px}
.recap-tbl{width:100%;border-collapse:collapse}
.recap-tbl td{padding:5px 8px}
.recap-tbl td.r{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap;width:150px}
.recap-tbl td.tx{white-space:nowrap;color:var(--muted)}
.recap-tbl td.tx input{width:56px;text-align:right;height:26px}
.recap-tbl tr.sep td{border-top:1px solid var(--glass-line);font-weight:600}
.recap-tbl tr.ttc td{border-top:2px solid var(--teal);font-size:15px;color:var(--teal-fg)}
.only-print{display:none}
.print-logo{height:46px;width:auto;margin-bottom:6px}
@media print{
  .print-hide,.sidebar,.topbar,.scrim,.menu-btn{display:none!important}
  .only-print{display:block}
  .app{grid-template-columns:1fr}
  .page,.content{padding:0;margin:0}
  .panel{box-shadow:none;border:0;background:#fff;color:#000;padding:0}
  .rtable th,.rtable td{border-color:#999}
  .rtable thead th{background:#eee!important;color:#000}
  .ol .ol-jour td{background:#e8e8e8!important;color:#000}
  /* !important obligatoire : le halo du mode sombre est posé par un sélecteur
     plus spécifique (html[data-theme] body) et l'emporterait sur `body{}`. */
  body,html[data-theme="sombre"] body,html[data-theme="clair"] body{background:#fff!important}
}

/* Revenus : stats de vérification + ligne case à cocher */
.stat{display:flex;flex-direction:column;gap:2px;padding:10px 14px;background:var(--glass);border:1px solid var(--glass-line);border-radius:10px}
.stat b{font-size:22px;font-weight:800;line-height:1}
.chkline{display:flex;align-items:center;gap:8px;flex-direction:row !important}
.chkline input{width:auto;margin:0}

/* Revenus/annexes : en-tête de colonnes au-dessus des lignes éditables (aligné gap:6px,
   padding horizontal 6px comme .mrow). Largeurs de colonnes posées en inline par champ. */
.rev-head{display:flex;gap:6px;flex-wrap:wrap;align-items:center;padding:2px 6px 6px;margin-bottom:2px;border-bottom:1px solid var(--glass-line)}
.rev-head span{font-size:10px;text-transform:uppercase;letter-spacing:.04em;color:var(--dim);font-weight:600}

/* Veille : filtre Forme (dropdown à cases, décocher = exclure) */
.fdrop{position:relative}
.fdrop>summary{list-style:none;cursor:pointer}
.fdrop>summary::-webkit-details-marker{display:none}
.fdrop-panel{position:absolute;z-index:40;top:calc(100% + 4px);left:0;min-width:250px;max-height:320px;overflow:auto;
  background:var(--solid);border:1px solid var(--glass-line);border-radius:9px;padding:8px;box-shadow:0 14px 36px rgba(0,0,0,.5)}
.fdrop-head{padding:1px 4px 6px;margin-bottom:4px;border-bottom:1px solid var(--glass-line)}
.fdrop-item{display:flex;align-items:center;gap:7px;padding:3px 4px;font-size:12px;color:var(--soft);white-space:nowrap;cursor:pointer}
.fdrop-item:hover{background:rgba(255,255,255,.05);border-radius:5px}
.fdrop-item input{width:auto;margin:0;flex:none}

/* Veille : encart graphe repliable (réutilise .revchart/.rc-*) */
.vchart-box{margin-bottom:14px}
.vchart-sum{list-style:none;cursor:pointer;display:flex;align-items:center;gap:8px;padding:9px 14px;font-weight:600;color:var(--ink);
  background:var(--glass);border:1px solid var(--glass-line);border-radius:10px}
.vchart-sum::-webkit-details-marker{display:none}
.vchart-chev{width:14px;height:14px;transition:transform .12s;flex:none}
.vchart-box[open] .vchart-chev{transform:rotate(90deg)}
.vchart-box[open] .vchart-sum{border-radius:10px 10px 0 0}
.vchart-box .panel{margin:0;border-top:0;border-radius:0 0 10px 10px}

/* Revenus : barre de progression 507h */
.pbar{height:12px;border-radius:8px;background:var(--glass-line,rgba(255,255,255,.08));overflow:hidden}
.pbar-fill{height:100%;border-radius:8px;background:var(--amber,#e0a800);transition:width .4s ease}

/* Revenus : graphe mensuel (barres + tooltip au survol, CSS pur) */
.rc-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:14px}
.rc-nav{display:flex;align-items:center;gap:8px}
.rc-nav .btn{padding:3px 7px}
.rc-year{font-size:14px;min-width:48px;text-align:center;font-variant-numeric:tabular-nums}
.revchart{display:flex;align-items:flex-end;gap:4px;height:170px;padding:6px 2px 0;overflow:visible}
.rc-col{position:relative;flex:1;min-width:0;display:flex;flex-direction:column;align-items:center;height:100%}
.rc-barwrap{flex:1;width:100%;display:flex;align-items:flex-end;justify-content:center;min-height:0}
.rc-bar{position:relative;overflow:hidden;width:72%;max-width:24px;min-height:2px;border-radius:4px 4px 0 0;
  background:rgba(127,127,127,.16);transition:filter .12s;display:flex;flex-direction:column-reverse}
.rc-col:hover .rc-bar{filter:brightness(1.22)}
/* Segment bas = proportion de cessées (rouge), empilé dans la jauge */
.rc-cess{position:absolute;left:0;right:0;bottom:0;background:linear-gradient(180deg,rgba(240,138,138,.92),rgba(240,138,138,.6))}
/* Segments par SOURCE de revenu (barre empilée, bas→haut). Facture Projection = doré. */
.rcs{width:100%;flex:none;min-height:1px}
.rcs-salaire{background:#6b8e7f}
.rcs-are{background:#6a8caf}
.rcs-facture{background:var(--teal)}
.rcs-facture_proj{background:var(--gold)}
.rcs-note{background:#8f7d9c}
.rcs-conges{background:#c08552}
.vc-leg{display:inline-flex;align-items:center;gap:5px;font-size:10.5px;color:var(--muted)}
.vc-leg i{width:9px;height:9px;border-radius:2px;display:inline-block}
.vc-leg-a{background:var(--teal)}
.vc-leg-c{background:rgba(240,138,138,.85);margin-left:6px}
/* barres cliquables (clic = filtrer le tableau sur le mois) + mois sélectionné */
a.rc-col{cursor:pointer;text-decoration:none;color:inherit}
.rc-col.sel .rc-bar{outline:2px solid var(--teal);outline-offset:2px;filter:brightness(1.15)}
.rc-empty .rc-bar{opacity:.22}
.rc-x{font-size:10px;color:var(--muted);margin-top:5px;white-space:nowrap;text-transform:capitalize}
.rc-tip{position:absolute;bottom:calc(100% + 8px);left:50%;transform:translateX(-50%);display:none;z-index:30;
  min-width:240px;max-width:340px;background:var(--solid,#141a24);border:1px solid var(--glass-line);
  border-radius:9px;padding:9px 11px;box-shadow:0 14px 36px rgba(0,0,0,.5);text-align:left;pointer-events:none}
.rc-col:hover .rc-tip{display:block}
.rc-col:first-child .rc-tip{left:0;transform:none}
.rc-col:last-child .rc-tip{left:auto;right:0;transform:none}
.rc-tip-h{font-size:12px;color:var(--ink);margin-bottom:6px;padding-bottom:5px;border-bottom:1px solid var(--glass-line);text-transform:capitalize;font-variant-numeric:tabular-nums}
.rc-tip-r{display:flex;gap:7px;align-items:baseline;font-size:11px;color:var(--soft);padding:2.5px 0}
.rc-tag{flex:none;font-size:8.5px;font-weight:600;padding:1px 6px;border-radius:999px;background:var(--teal-bg);color:var(--teal-fg)}
.rc-lbl{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rc-amt{flex:none;color:var(--ink);font-variant-numeric:tabular-nums}
.rc-st{flex:none;font-size:9.5px;color:var(--dim)}

/* Revenus : calendrier 24 mois */
.rcal-year{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:14px}
.rcal-month{border:1px solid var(--glass-line,rgba(255,255,255,.08));border-radius:10px;padding:8px}
.rcal-mname{font-weight:700;font-size:13px;margin-bottom:6px;text-align:center}
.rcal-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:2px}
.rcal-h{font-size:10px;color:var(--muted,#8a93a3);text-align:center;padding-bottom:2px}
.rcal-cell{font-size:11px;aspect-ratio:1;display:flex;align-items:center;justify-content:center;border:0;border-radius:5px;background:transparent;color:var(--ink,inherit);cursor:pointer;padding:0}
.rcal-cell:hover{background:var(--glass-line,rgba(255,255,255,.08))}
.rcal-empty{background:transparent;cursor:default}
.rcal-pra{background:rgba(224,168,0,.07)}
.rcal-travail{background:var(--teal-fg,#19c3a6) !important;color:#04201b !important;font-weight:700}
.rcal-prev{background:repeating-linear-gradient(45deg,rgba(224,168,0,.5),rgba(224,168,0,.5) 3px,rgba(224,168,0,.25) 3px,rgba(224,168,0,.25) 6px) !important;font-weight:700}
.rcal-ouv{box-shadow:inset 0 0 0 2px var(--amber,#e0a800)}
.rcal-anniv{box-shadow:inset 0 0 0 2px #e05a5a}
.legend{display:inline-flex;align-items:center;gap:5px}
.lgd{width:12px;height:12px;border-radius:3px;display:inline-block}
.lgd.rcal-pra{background:rgba(224,168,0,.25)}

/* --- Moodboard : planches A4 + éléments positionnés en % ------------------- */
.mbpage-wrap{margin:0 auto 22px;max-width:1060px}
.mbpage-bar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:0 auto 6px;
  max-width:760px;padding:0 2px}
.a4page.paysage~.mbpage-bar,.mbpage-wrap:has(.a4page.paysage) .mbpage-bar{max-width:1040px}
.a4page{position:relative;margin:0 auto;background:#fff;border-radius:4px;overflow:hidden;
  box-shadow:0 8px 30px rgba(0,0,0,.45);aspect-ratio:210/297;
  width:min(760px,100%,calc((100vh - 190px) * .7072))}
.a4page.paysage{aspect-ratio:297/210;width:min(1040px,100%,calc((100vh - 190px) * 1.4142))}
.mbitem{position:absolute;box-sizing:border-box;border:1px solid transparent;border-radius:3px}
.mbitem:hover,.mbitem.dragging{border-color:rgba(201,189,170,.9);box-shadow:0 2px 12px rgba(0,0,0,.18)}
.mbitem img{width:100%;height:100%;object-fit:cover;display:block;border-radius:2px;pointer-events:none}
.mbtext{width:100%;height:100%;padding:6px 8px;box-sizing:border-box;overflow:auto;outline:none;
  color:#14110c;line-height:1.35;border-radius:2px;cursor:text;white-space:pre-wrap}
.mbtext:empty::before{content:attr(data-ph);color:#9a948a}
.mbcard{display:flex;flex-direction:column;width:100%;height:100%;text-decoration:none;
  color:#14110c;background:#f3f1ec;border-radius:3px;overflow:hidden}
.mbcard-img{flex:1;background-size:cover;background-position:center;display:flex;align-items:center;justify-content:center}
.mbcard-img.noimg{background:#e6e2d8}.mbcard-img.noimg .icon{width:26px;height:26px;color:#8a857a}
.mbcard-t{flex:none;padding:5px 8px;font-size:12px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;background:rgba(255,255,255,.7)}
.mb-move,.mb-del,.mb-resize,.mb-edit{position:absolute;opacity:0;transition:opacity .12s;z-index:6}
.mbitem:hover .mb-move,.mbitem:hover .mb-del,.mbitem:hover .mb-resize,.mbitem:hover .mb-edit,
.mbitem.dragging .mb-move,.mbitem.dragging .mb-resize{opacity:1}
.mb-edit{bottom:5px;left:5px;width:22px;height:22px;border-radius:50%;border:0;background:var(--teal);
  color:var(--teal-ink);display:flex;align-items:center;justify-content:center;cursor:pointer;
  box-shadow:0 1px 4px rgba(0,0,0,.4)}
.mb-edit .icon{width:12px;height:12px}
.mb-move{top:5px;left:5px;width:22px;height:22px;border-radius:50%;background:var(--teal);
  color:var(--teal-ink);display:flex;align-items:center;justify-content:center;cursor:grab;
  box-shadow:0 1px 4px rgba(0,0,0,.4);touch-action:none}
.mb-move .icon{width:13px;height:13px}
.mb-del{top:5px;right:5px;width:22px;height:22px;border-radius:50%;border:0;background:#c0392b;
  color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer;
  box-shadow:0 1px 4px rgba(0,0,0,.4)}
.mb-del .icon{width:12px;height:12px}
.mb-resize{right:4px;bottom:4px;width:18px;height:18px;border-radius:50%;background:var(--teal);
  border:2px solid #fff;cursor:nwse-resize;box-shadow:0 1px 4px rgba(0,0,0,.4);touch-action:none}

/* --- Moodboard : modale « bibliothèque d'images du projet » ---------------- */
.mb-modal{position:fixed;inset:0;background:rgba(7,10,15,.8);z-index:200;display:flex;
  align-items:center;justify-content:center;padding:24px}
.mb-modal[hidden]{display:none}
.mb-modal-box{background:#0e1318;border:1px solid var(--line);border-radius:14px;
  max-width:920px;width:100%;max-height:84vh;display:flex;flex-direction:column;
  overflow:hidden;box-shadow:0 24px 70px rgba(0,0,0,.55)}
.mb-modal-head{display:flex;align-items:center;gap:8px;padding:12px 16px;border-bottom:1px solid var(--line)}
.mb-biblio-grid{padding:14px;display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));
  gap:12px;overflow:auto}
.mb-biblio-item{display:flex;flex-direction:column;padding:0;border:1px solid var(--line);
  border-radius:8px;overflow:hidden;background:var(--ctl-bg);cursor:pointer;text-align:left}
.mb-biblio-item:hover{border-color:var(--teal)}
.mb-biblio-item img{width:100%;height:108px;object-fit:cover;display:block;background:#000}
.mb-biblio-item span{padding:5px 7px;font-size:11px;color:var(--muted);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* --- Compte-rendu : chapitres repliables + suivi + commentaires ----------- */
.cr-chap{margin-bottom:14px}
.cr-chap > summary, .cr-suivi > summary{list-style:none;cursor:pointer}
.cr-chap > summary::-webkit-details-marker, .cr-suivi > summary::-webkit-details-marker{display:none}
.cr-chap-head{display:flex;align-items:center;gap:8px}
.cr-chap-head h2{margin:0}
.cr-chevron{width:15px;height:15px;flex:none;opacity:.7;transition:transform .15s}
details[open] > summary > .cr-chevron{transform:rotate(90deg)}
.cr-chap-body{padding-top:12px}
textarea.cr-grow{overflow:hidden;resize:none;min-height:58px;line-height:1.45}
.cr-suivi{border:1px solid var(--line);border-radius:10px;margin-bottom:9px;background:var(--ctl-bg)}
.cr-suivi-head{display:flex;gap:8px;align-items:center;flex-wrap:wrap;padding:9px 11px}
.cr-suivi[open] > .cr-suivi-head{border-bottom:1px solid var(--hair)}
.cr-suivi-when{color:var(--dim);font-size:12px;white-space:nowrap}
.cr-suivi-title{font-weight:600}
.cr-suivi-body{padding:10px 12px}
.cr-suivi-edit{display:flex;gap:10px;align-items:flex-start;flex-wrap:wrap}
.cr-comments{margin-top:10px;border-top:1px solid var(--hair);padding-top:9px;
  display:flex;flex-direction:column;gap:5px}
.cr-comment{display:flex;gap:8px;align-items:center;font-size:12.5px;
  background:var(--teal-bg);border-radius:7px;padding:4px 8px}
.cr-comment-txt{flex:1}
.cr-comment-meta{color:var(--dim);font-size:11px;white-space:nowrap}
.cr-comment-add{display:flex;gap:6px;align-items:center;margin-top:2px}
.cr-comment-add input{flex:1;min-width:0}

/* --- Veille : pastilles de complétude des couches ------------------------- */
.vc-dots{display:inline-flex;gap:3px}
.vc-dot{width:9px;height:9px;border-radius:50%;background:var(--line);display:inline-block}
.vc-dot.on{background:var(--teal)}

/* ================= RAPPELS =========================================
   Bouton d'en-tête, panneau déroulant, et liste (la même sur la page
   complète). Tout est préfixé « rap- » : app.css est partagé, et une
   classe générique y écrase un jour la mise en page d'une autre page.
   Aucun jeton inventé ici · « --panel » et « --accent » n'existent pas
   dans cette feuille, s'y adosser donne un repli codé en dur qui casse
   en thème clair. */
.rap-btn{cursor:pointer}
.rap-btn.ouvert{color:var(--ink);border-color:var(--line)}
.rap-pan{position:fixed;top:calc(var(--top-h) + 7px);right:14px;z-index:40;
  width:min(404px,calc(100vw - 20px));max-height:calc(100vh - var(--top-h) - 22px);
  display:flex;flex-direction:column;background:var(--solid);
  border:1px solid var(--line);border-radius:14px;
  box-shadow:0 18px 46px rgba(0,0,0,.42);overflow:hidden}
html[data-theme="clair"] .rap-pan{box-shadow:0 16px 40px rgba(38,36,31,.16)}
.rap-tete{padding:11px 12px 9px;border-bottom:1px solid var(--hair);flex:0 0 auto}
.rap-saisie{display:flex;gap:7px;align-items:center}
.rap-saisie input[type=text]{flex:1;min-width:0;font-size:13.5px;padding:9px 11px;
  border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--chip);
  color:var(--ink)}
.rap-saisie input[type=text]::placeholder{color:var(--dim)}
.rap-saisie button{flex:0 0 auto}
/* Ce que l'outil a compris de la ligne en cours de frappe. Réservé en hauteur
   même vide : sans cela, l'apparition de l'aperçu ferait sauter la liste
   entière d'un cran sous le curseur. */
.rap-lu{min-height:15px;margin-top:5px;font-size:11.5px;color:var(--dim);
  line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rap-lu b{color:var(--teal-fg);font-weight:600}
.rap-corps{overflow-y:auto;overscroll-behavior:contain;padding:6px 6px 4px;flex:1 1 auto}
.rap-sec{margin-bottom:4px}
.rap-sec-t{display:flex;align-items:center;gap:7px;margin:8px 6px 3px;font-size:10.5px;
  text-transform:uppercase;letter-spacing:.07em;color:var(--dim);font-weight:700}
.rap-sec-t::after{content:"";flex:1;height:1px;background:var(--hair)}
.rap-sec-t span{order:3;font-size:10.5px;color:var(--dim);font-weight:700}
.rap-sec-t.retard{color:var(--red-fg)}
.rap-sec-t.jour{color:var(--teal-fg)}
/* Une ligne. La grille tient en trois colonnes fixes pour que les cases à
   cocher restent alignées quel que soit le texte, y compris sur deux lignes. */
.rap-l{display:grid;grid-template-columns:26px 1fr 24px;gap:8px;align-items:start;
  padding:7px 6px;border-radius:9px}
.rap-case{grid-column:1;margin-top:1px;width:20px;height:20px;flex:0 0 auto;padding:0;
  display:inline-flex;align-items:center;justify-content:center;cursor:pointer;
  border:1.5px solid var(--line);border-radius:50%;background:transparent;
  color:transparent;transition:color .12s,border-color .12s,background .12s}
.rap-case .icon{width:12px;height:12px}
.rap-case:hover{border-color:var(--teal);color:var(--muted)}
.rap-l.est-fait .rap-case{background:var(--teal);border-color:var(--teal);
  color:var(--teal-ink)}
.rap-txt{grid-column:2;min-width:0}
.rap-ti{font-size:13.5px;color:var(--ink);line-height:1.35;overflow-wrap:anywhere}
.rap-pin{width:11px;height:11px;color:var(--gold-fg);margin-right:4px;vertical-align:-1px}
/* Un rappel coché s'efface, mais son titre reste du contenu : « --dim » mesure
   3,65:1 sur l'encre, « --muted » 6,49 - et la rature suffit à dire que c'est
   fait, sans avoir en plus à deviner le texte. */
.rap-l.est-fait .rap-ti{color:var(--muted);text-decoration:line-through}
.rap-m{display:flex;flex-wrap:wrap;gap:6px;align-items:center;margin-top:3px;
  font-size:11px;color:var(--dim)}
.rap-quand{font-weight:600;color:var(--muted)}
.rap-quand.chaud{color:var(--red-fg)}
/* UN RAPPEL ÉCHU SE LIT EN ROUGE · l'heure seule le disait, en 11 px et en bas
   de la ligne. Sur une liste de vingt-cinq, ça ne se voit pas. Le titre et la
   case prennent la couleur : c'est ce qu'on lit en premier.
   « --red-fg » et pas « --red » : le second mesure 2,41:1 sur le blanc du thème
   clair, il tient l'aplat, pas le texte. */
.rap-l.est-retard .rap-ti{color:var(--red-fg)}
.rap-l.est-retard .rap-case{border-color:var(--red)}
.rap-l.est-retard .rap-case:hover{border-color:var(--red-fg);color:var(--red-fg)}
.rap-rec{color:var(--dim)}
.rap-etq{font-size:10px;padding:1px 7px}
.rap-nt{border:1px solid var(--hair);border-radius:var(--pill);padding:0 6px;
  font-size:9.5px;text-transform:uppercase;letter-spacing:.05em;cursor:help}
/* Le volet de détail. PAS de <details> : ouvert, il fallait le faire entrer dans
   la grille de la ligne (« display:contents »), et là son volet s'écrasait à la
   largeur de la colonne du bouton - 26 px, libellés superposés, champs illisibles.
   Mesuré, pas supposé. Un conteneur ordinaire et un bouton qui bascule un
   attribut « hidden » donnent la main sur la mise en page, et l'accessibilité
   tient par aria-expanded. */
.rap-item{border-radius:9px}
.rap-item:hover{background:var(--ctl-bg)}
.rap-item.ouvert{background:var(--ctl-bg)}
.rap-dep{grid-column:3;justify-self:end;border:none;background:transparent;cursor:pointer;
  width:22px;height:22px;padding:0;display:inline-flex;align-items:center;
  justify-content:center;border-radius:6px;color:var(--dim)}
.rap-dep:hover{color:var(--ink);background:var(--chip)}
.rap-dep .icon{width:14px;height:14px;transition:transform .15s}
.rap-item.ouvert .rap-dep{color:var(--ink)}
.rap-item.ouvert .rap-dep .icon{transform:rotate(180deg)}
.rap-volet{padding:1px 6px 8px}
.rap-rep{display:flex;flex-wrap:wrap;gap:5px;padding:2px 0 7px}
/* « Dans 1 h » se cassait en deux lignes, ce qui empilait ensuite tout le
   volet : un libellé de commande ne se coupe pas. */
.rap-mini{font-size:11px;padding:3px 9px;border-radius:var(--pill);cursor:pointer;
  white-space:nowrap;
  border:1px solid var(--line);background:var(--chip);color:var(--muted)}
.rap-mini:hover{color:var(--ink);border-color:var(--teal)}
.rap-mini.danger{color:var(--red-fg);border-color:transparent;background:transparent}
/* Au survol : un fond TEINTÉ, pas un aplat. Un aplat de --red porte du blanc
   en thème sombre mais tombe à 2,4:1 en thème clair, où le rouge est pâle. */
.rap-mini.danger:hover{color:var(--red-fg);background:var(--red-bg);
  border-color:var(--red-fg)}
.rap-form{display:flex;flex-direction:column;gap:6px;padding:0 0 9px}
.rap-form input,.rap-form select,.rap-form textarea{font-size:12.5px;padding:6px 8px;
  border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--chip);
  color:var(--ink);width:100%;font-family:inherit}
.rap-form textarea{resize:vertical;min-height:38px}
.rap-duo{display:flex;gap:6px}
.rap-duo label{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;
  font-size:10px;text-transform:uppercase;letter-spacing:.05em;color:var(--dim)}
.rap-form-b{display:flex;gap:7px;align-items:center;justify-content:space-between}
.rap-vide{padding:24px 18px;text-align:center;color:var(--muted);font-size:12.5px;
  line-height:1.6}
.rap-vide strong{display:block;color:var(--ink);margin-bottom:4px;font-size:13px}
.rap-pied{flex:0 0 auto;display:flex;align-items:center;gap:10px;padding:8px 12px;
  border-top:1px solid var(--hair);font-size:11.5px}
.rap-pied .sp{flex:1}
.rap-pied a{color:var(--muted);text-decoration:none}
.rap-pied a:hover{color:var(--ink)}
/* L'attribut « hidden » est de spécificité nulle : n'importe quelle classe
   posant un « display » l'annule sans prévenir. On le réarme. */
.rap-pan[hidden],.rap-l [hidden]{display:none !important}
@media (max-width:620px){
  .rap-pan{right:8px;left:8px;width:auto;max-height:calc(100vh - var(--top-h) - 14px)}
}


/* ── Fil conducteur : d'où viennent les données, où elles vont ─────────────
   Depuis que Devis & factures est l'unique pilote, une même somme traverse
   trois ou quatre pages avant d'être un revenu. Sans ce fil, on corrige au
   mauvais endroit - et la correction est réécrite au passage suivant.
   L'étape courante est marquée : c'est ce qui rend le fil lisible d'un coup
   d'œil, plutôt qu'une liste de liens. */
.flux{display:flex;align-items:center;gap:6px;flex-wrap:wrap;
  font-size:11.5px;color:var(--dim);margin:0 0 12px;padding:7px 12px;
  border:1px solid var(--glass-line);border-radius:var(--pill);
  background:var(--glass-2);width:fit-content;max-width:100%}
.flux-t{font-weight:600;color:var(--muted);margin-right:2px}
.flux-e{display:inline-flex;align-items:center;gap:4px;padding:2px 9px;
  border-radius:var(--pill);border:1px solid transparent;
  color:var(--muted);text-decoration:none;white-space:nowrap}
a.flux-e:hover{color:var(--ink);border-color:var(--teal-fg)}
.flux-e.ici{color:var(--ink);font-weight:600;border-color:var(--glass-line);
  background:var(--chip)}
.flux-e .icon{width:12px;height:12px;flex:0 0 auto}
.flux-f{color:var(--dim);flex:0 0 auto}

/* =========================================================================
   COUCHE D'INTERACTION
   -------------------------------------------------------------------------
   Posée en fin de fichier, à dessein : elle passe APRÈS les composants et
   n'a donc pas à surenchérir en spécificité. Elle est retirable d'un bloc.

   Ce qu'elle corrige, mesuré sur la version précédente : 25 transitions pour
   1 316 lignes, DEUX états « pressé » dont un qui ne changeait qu'un curseur,
   AUCUN anneau de focus clavier, AUCUNE prise en compte du mouvement réduit.
   Un bouton n'animait qu'une seule propriété - sa luminosité - ce qui donne
   très exactement la sensation d'interrupteur.

   Rien ici ne touche à la palette, aux polices, aux rayons ni aux espacements.
   ========================================================================= */

/* ---- Le survol n'appartient qu'aux souris -------------------------------
   Sur un écran tactile il n'y a pas de « sortie de survol » : l'effet reste
   COLLÉ après le tap, jusqu'à ce qu'on touche ailleurs. Les tuiles restaient
   soulevées. On les repose. */
@media (hover: none){
  .btn:hover,.tile:hover,.tool:hover,.dash-tuile:hover,.minitile:hover,
  .carte-douce:hover{transform:none}
}

/* ---- L'anneau de focus, pour le clavier seulement -----------------------
   `:focus-visible` et non `:focus` : le navigateur ne le pose que lorsque la
   navigation est au clavier. À la souris, rien ne change - c'est ce qui avait
   fait retirer les contours d'origine, et c'est ce qui les rend acceptables
   aujourd'hui. Un `outline` ne participe pas à la mise en page : il ne peut
   rien décaler. */
a:focus-visible,button:focus-visible,summary:focus-visible,
[tabindex]:focus-visible,[role="button"]:focus-visible{
  outline:2px solid var(--teal-fg);outline-offset:2px;border-radius:var(--radius-sm)}

/* ---- Les boutons : six états au lieu de deux ---------------------------- */
.btn{box-shadow:var(--ombre-1);
  transition:transform var(--t-doigt) var(--sortie), box-shadow var(--t) var(--sortie),
             filter var(--t) var(--sortie), border-color var(--t) var(--sortie),
             color var(--t) var(--sortie), opacity var(--t) var(--sortie)}
@media (hover: hover){
  .btn:hover{transform:translateY(-1px);box-shadow:var(--ombre-2);filter:brightness(1.06)}
}
/* Le pressé. C'est LE geste qui manquait partout : sans lui, l'interface ne
   répond pas au doigt et paraît figée. Plus court que le survol - un retour
   tactile qui traîne se sent comme une latence. */
.btn:active{transform:translateY(0) scale(.985);box-shadow:var(--ombre-creuse);
  filter:brightness(.96);transition-duration:60ms}
.btn:focus-visible{outline:none;box-shadow:var(--ombre-1), var(--anneau)}
.btn:disabled,.btn[aria-disabled="true"]{opacity:.42;filter:grayscale(.5);box-shadow:none;
  transform:none;cursor:not-allowed;pointer-events:none}
/* L'état OCCUPÉ. Le bouton garde sa largeur (le texte devient transparent, il
   n'est pas retiré) : rien ne saute. Et il ne se laisse plus cliquer, ce qui
   règle du même coup la double soumission d'un formulaire. */
.btn[aria-busy="true"]{color:transparent !important;pointer-events:none;position:relative}
.btn[aria-busy="true"] .icon{visibility:hidden}
.btn[aria-busy="true"]::after{content:"";position:absolute;width:14px;height:14px;
  border:2px solid var(--teal-ink);border-top-color:transparent;border-radius:50%;
  animation:spin .7s linear infinite}
.btn.ghost[aria-busy="true"]::after{border-color:var(--ink);border-top-color:transparent}

/* L'état EN COURS, pour les attentes LONGUES (un appel à un modèle, la lecture
   d'un support). Contrairement à `aria-busy`, le libellé reste lisible et dit
   ce qui se passe : sur une minute d'attente, un bouton muet ne rassure
   personne. Le bouton ne se laisse plus cliquer, ce qui règle du même coup la
   double soumission - et ici elle coûte un second appel payant. */
.btn.en-cours{pointer-events:none;opacity:.9}
.btn.en-cours::before{content:"";flex:0 0 auto;width:13px;height:13px;border-radius:50%;
  border:2px solid currentColor;border-top-color:transparent;
  animation:spin .7s linear infinite}
.en-cours-note{margin:7px 2px 0;font-size:11.5px;line-height:1.45;color:var(--dim)}

/* ---- Les champs : la bordure ne saute plus ------------------------------
   Aucune transition n'était déclarée sur les champs : la bordure changeait
   d'un coup. L'anneau se substitue au contour retiré par `outline:none`. */
input,select,textarea{
  transition:border-color var(--t) var(--sortie), box-shadow var(--t) var(--sortie),
             background-color var(--t) var(--sortie)}

/* ── LE CURSEUR ───────────────────────────────────────────────────────────
   Il était laissé au navigateur dans sept pages (Play, Vidéothèque, Plans de
   feu, Tableau blanc, Personnage) : un habillage système au milieu d'un
   intranet entièrement plat et arrondi, exactement le défaut qu'on avait
   corrigé sur les menus déroulants. La partie parcourue est en accent, le
   reste en filet. La valeur est portée par « --p », posée par la page. */
input[type=range]{appearance:none;-webkit-appearance:none;background:none;
  height:20px;width:100%;cursor:pointer;border:0;outline:none}
input[type=range]::-webkit-slider-runnable-track{height:5px;border-radius:3px;
  background:linear-gradient(90deg,var(--teal) var(--p,50%),var(--line) var(--p,50%))}
input[type=range]::-moz-range-track{height:5px;border-radius:3px;background:var(--line)}
input[type=range]::-moz-range-progress{height:5px;border-radius:3px;background:var(--teal)}
input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:15px;height:15px;
  border-radius:50%;background:var(--solid);border:2px solid var(--teal);margin-top:-5px;
  box-shadow:0 1px 3px rgba(0,0,0,.25);transition:transform .15s var(--sortie)}
input[type=range]::-moz-range-thumb{width:13px;height:13px;border-radius:50%;
  background:var(--solid);border:2px solid var(--teal);box-shadow:0 1px 3px rgba(0,0,0,.25)}
input[type=range]:hover::-webkit-slider-thumb{transform:scale(1.18)}
input[type=range]:focus-visible::-webkit-slider-thumb{box-shadow:var(--anneau)}
@media (prefers-reduced-motion:reduce){
  input[type=range]::-webkit-slider-thumb{transition:none}}
/* PLUS D'ANNEAU SUR LES CHAMPS · c'est le bord gauche qui porte le focus
   désormais. L'anneau reste pour tout le reste (boutons, puces, liens), où
   aucun bord d'état ne le remplace. */
input:focus,select:focus,textarea:focus{box-shadow:none}
input[type=checkbox]:focus-visible,input[type=radio]:focus-visible{box-shadow:var(--anneau)}
/* SAUF quand le champ n'est pas la boîte. Ces conteneurs-là sont le champ aux
   yeux de l'utilisateur : ils portent le cadre, le fond et leur propre marque
   de focus, et le champ qu'ils abritent est nu (« border:0 ; padding:0 »).
   L'anneau s'y dessinait à ras du texte - il le TOUCHAIT, littéralement, faute
   de la moindre marge intérieure pour l'en séparer. C'est au conteneur de dire
   qu'il a le focus, pas à son contenu. */
.sb-line input:focus,.sb-line select:focus,
.mailsearch input:focus,.sb-ia-web input:focus,
.fd-fl input:focus,.fd-fl select:focus,
.cl-act input:focus,.gr-act input:focus,.sc-act input:focus,
.rowact input:focus,.cf-outils input:focus,.sim-pourq input:focus,
.ap-vignette input:focus{box-shadow:none}
.mailsearch{transition:border-color var(--t) var(--sortie), box-shadow var(--t) var(--sortie)}
.mailsearch:focus-within{box-shadow:var(--anneau)}

/* ---- Les tuiles : elles montent, et surtout elles REDESCENDENT ----------
   Elles montaient déjà de 2 px, mais sans courbe, et sans rien sous le clic.

   AUCUNE OMBRE AU REPOS, et c'est une correction : le thème sombre pose
   explicitement « box-shadow:none » sur ces surfaces (« filet net et aucune
   ombre »), et le thème clair leur donne la sienne. C'est une décision de
   dessin, pas un oubli - la mesure l'a rappelée après que cette couche l'eut
   piétinée. L'ombre n'apparaît donc QU'AU SURVOL, où elle n'est plus une
   décoration mais une réponse. Ces sélecteurs valent (0,2,0), comme les règles
   de thème, et passent après : ils gagnent sans avoir à surenchérir. */
.tile,.tool,.dash-tuile,.minitile,.carte-douce{
  transition:transform var(--t) var(--sortie), box-shadow var(--t) var(--sortie),
             border-color var(--t) var(--sortie)}
@media (hover: hover){
  .tile:hover,.carte-douce:hover,.minitile:hover{box-shadow:var(--ombre-2)}
  /* LES TUILES GARDENT LEUR LISERÉ EN PLUS DE L'OMBRE. Écrites avec les
     autres, elles perdaient l'arête colorée au survol : cette règle-ci est
     plus bas dans le fichier, de même poids, et une « box-shadow » ne
     s'additionne pas · elle remplace. Le liseré vaut « transparent » sur une
     tuile sans famille, la règle est donc sans effet visible pour elles. */
  .tool:hover,.dash-tuile:hover{box-shadow:inset 0 3px 0 var(--liz),var(--ombre-2)}
  .tile:hover .ic,.tool:hover .ic,.dash-tuile:hover .ic{transform:scale(1.06) rotate(-2deg)}
}
.tile .ic,.tool .ic,.dash-tuile .ic{transition:transform var(--t) var(--sortie)}
.tile:active,.tool:active,.dash-tuile:active,.minitile:active,.carte-douce:active{
  transform:translateY(0) scale(.995);box-shadow:var(--ombre-creuse);transition-duration:70ms}
.tile:focus-visible,.tool:focus-visible,.dash-tuile:focus-visible,
.minitile:focus-visible,.carte-douce:focus-visible{outline:none;
  box-shadow:var(--ombre-1), var(--anneau)}

/* ---- Les lignes de tableau : un filet plutôt qu'un aplat ----------------
   Repeindre toute la ligne pour dire « vous êtes ici » est brutal et efface
   la lecture. Un filet à gauche suit l'œil sans rien recouvrir. Posé sur la
   première cellule et non sur la ligne : avec `border-collapse:collapse`,
   une ombre sur `tr` n'est pas rendue de façon fiable. */
tbody tr,tbody tr td:first-child{
  transition:background var(--t) var(--sortie), box-shadow var(--t) var(--sortie)}
tbody tr td:first-child{box-shadow:inset 0 0 0 var(--teal)}
tbody tr:hover td:first-child{box-shadow:inset 3px 0 0 var(--teal)}

/* ---- Le petit chrome : navigation, en-tête, puces, segments ------------- */
.nav a,.htool,.hsous-l,.chip,.seg,.rap-btn,.onglet{
  transition:background var(--t) var(--sortie), color var(--t) var(--sortie),
             border-color var(--t) var(--sortie), transform var(--t-doigt) var(--sortie)}
.nav a:active,.htool:active,.hsous-l:active,.seg:active,.rap-btn:active,.onglet:active{
  transform:scale(.98);transition-duration:60ms}
a.chip:active,button.chip:active{transform:scale(.96);transition-duration:60ms}
/* Le bouton-icône des tableaux d'administration : même famille, même retour. */
.iconbtn{transition:border-color var(--t) var(--sortie), color var(--t) var(--sortie),
                    background var(--t) var(--sortie), transform var(--t-doigt) var(--sortie)}
.iconbtn:active{transform:scale(.92);transition-duration:60ms}
.iconbtn:focus-visible{outline:2px solid var(--teal-fg);outline-offset:2px}

/* ---- Les panneaux flottants s'ouvrent au lieu d'apparaître --------------
   OPACITÉ SEULEMENT, et c'est délibéré. Ces panneaux-là reçoivent leurs
   coordonnées en JavaScript, qui les mesure au moment de l'ouverture ; une
   animation de `transform` fausserait cette mesure de la valeur en cours
   d'interpolation, et le menu se poserait quelques pixels à côté. On vient de
   passer une soirée sur un décalage de ce genre - on ne le réintroduit pas
   pour quatre pixels de glissement. */
.hsous,.onglet-menu.on,.fdrop-panel,.rowmenu{animation:surgit var(--t) var(--sortie) both}
@keyframes surgit{from{opacity:0}to{opacity:1}}

/* ---- Et rien de tout cela ne s'impose ----------------------------------
   Qui a demandé moins d'animation dans son système ne doit pas en recevoir
   davantage parce qu'on vient d'en ajouter. Tout se coupe, les états
   restent. */
@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{transition-duration:1ms !important;animation-duration:1ms !important;
    animation-iteration-count:1 !important;scroll-behavior:auto !important}
  .btn:hover,.btn:active,.tile:hover,.tile:active,.tool:hover,.tool:active,
  .dash-tuile:hover,.dash-tuile:active,.minitile:hover,.minitile:active,
  .nav a:active,.htool:active,.iconbtn:active{transform:none}
  .spin,.btn[aria-busy="true"]::after{animation:spin 1.2s linear infinite !important}
}

/* =========================================================================
   DEUXIÈME COUCHE · ce qui se passe ENTRE les gestes
   -------------------------------------------------------------------------
   La première couche règle la réponse au doigt. Celle-ci s'occupe de ce que
   l'interface dit pendant qu'elle travaille, quand elle n'a rien à montrer,
   et quand elle répond. Même principe : posée en fin de fichier, retirable
   d'un bloc, aucune couleur nouvelle.
   ========================================================================= */

/* ---- 1 · Le message ne pousse plus la page -----------------------------
   Il s'insérait dans le flux, en tête de « main.page » : tout le contenu
   descendait d'un cran, et ce qu'on était en train de lire partait sous les
   yeux. Il flotte désormais au-dessus, en haut à droite, et ne déplace rien.

   « position:fixed » se résout bien ici sur la fenêtre : contrairement au
   sous-menu du header, cette pile n'a AUCUN ancêtre à backdrop-filter - elle
   est dans « main », pas dans « .topbar ». Vérifié avant de l'écrire. */
#msgs{position:fixed;z-index:80;top:calc(var(--top-h) + 14px);right:16px;
  display:flex;flex-direction:column;gap:8px;pointer-events:none;
  width:min(400px, calc(100vw - 32px));margin:0}
#msgs .msg{pointer-events:auto;margin:0;background:var(--solid);
  box-shadow:var(--ombre-2);animation:msg-entre 260ms var(--sortie) both}
@keyframes msg-entre{from{opacity:0;transform:translateX(16px) scale(.97)}to{opacity:1;transform:none}}
/* La sortie existait déjà (.msg.hide) : on lui donne le même geste, à l'envers. */
.msg.hide{opacity:0;transform:translateX(16px) scale(.97);
  transition:opacity .22s var(--sortie),transform .22s var(--sortie)}
@media (max-width:760px){
  /* Sur téléphone, la barre du haut prend toute la largeur : le message aussi,
     sans quoi il chevauche les icônes de l'en-tête. */
  #msgs{left:12px;right:12px;width:auto;top:calc(var(--top-h) + 8px)}
}

/* ---- 2 · L'attente a une forme -----------------------------------------
   Zéro squelette dans tout l'intranet : les pages qui vont chercher leurs
   données au loin restaient vides puis s'emplissaient d'un coup, sans qu'on
   sache si ça travaillait ou si c'était cassé.

   « .sk » est une barre qui pulse, à poser à la forme du contenu attendu ;
   « .sk-l » une ligne de tableau ; « .apparait » le fondu du vrai contenu
   quand il arrive - sans lui, le remplacement claque. */
.sk{height:11px;border-radius:5px;background:linear-gradient(90deg,
    var(--chip) 25%, color-mix(in srgb, var(--ink) 9%, var(--chip)) 37%, var(--chip) 63%);
  background-size:400% 100%;animation:sk-onde 1.3s linear infinite}
.sk-l{display:grid;grid-template-columns:1fr 92px;gap:12px;padding:9px 8px;
  border-top:1px solid var(--hair)}
.sk-bloc{border:1px solid var(--line);border-radius:var(--radius);padding:15px 16px;
  display:grid;gap:9px}
.sk-bloc .sk:first-child{width:45%;height:9px}
@keyframes sk-onde{from{background-position:100% 0}to{background-position:-100% 0}}
.apparait{animation:sk-fondu 320ms var(--sortie) both}
@keyframes sk-fondu{from{opacity:0;transform:translateY(3px)}to{opacity:1;transform:none}}

/* ---- 3 · Le vide dit quelque chose -------------------------------------
   Le vide est le moment où l'interface a le PLUS à dire : c'est là qu'on
   décide si l'outil paraît fini. Une phrase dans une boîte grise ne fait que
   constater. Gabarit : socle/templates/_vide.html. */
.vide{display:flex;flex-direction:column;align-items:center;text-align:center;gap:10px;
  padding:38px 22px;border:1px dashed var(--line);border-radius:var(--radius)}
.vide .rond{width:52px;height:52px;border-radius:50%;display:grid;place-items:center;
  background:var(--teal-bg);color:var(--teal-fg);flex:0 0 auto}
.vide .rond .icon{width:24px;height:24px}

/* ── LA PANNE ─────────────────────────────────────────────────────────────
   Le même bloc que le vide, mais en terracotta et avec une date : une panne
   dit ce qui a échoué, CE QUI RESTE VALABLE, et depuis quand. Le rond change
   de teinte, rien d'autre · un écran d'erreur qui ne ressemble à rien d'autre
   dans l'intranet donne l'impression que tout est cassé, pas seulement la
   mise à jour. */
.vide.panne{border-color:color-mix(in srgb,var(--red) 32%,var(--line))}
.vide.panne .rond{background:var(--red-bg);color:var(--red-fg)}
.vide .quand{font-size:10.5px;color:var(--dim);font-variant-numeric:tabular-nums;
  padding:4px 11px;border-radius:var(--pill);background:var(--chip);
  border:1px solid var(--line)}
.vide-gestes{display:flex;gap:8px;flex-wrap:wrap;justify-content:center}
/* « .btn.second » existe déjà plus haut, avec un sens arrêté · on ne le
   redéfinit pas ici. Une seconde déclaration, même identique en apparence,
   l'emporterait sur la première dans TOUT l'intranet. */

/* ── L'ATTENTE ────────────────────────────────────────────────────────────
   On montre la FORME de ce qui arrive plutôt qu'un rond qui tourne : on sait
   combien de lignes viennent et où regarder, et l'attente paraît plus courte
   parce qu'elle a déjà commencé à rendre quelque chose.
   Le balayage est LA MÊME LUEUR que celle des boutons au survol · l'intranet
   attend avec un geste qu'il a déjà. */
.attente{display:flex;flex-direction:column;gap:2px;padding:6px 2px}
.sk-l{display:flex;align-items:center;gap:12px;padding:10px 0;
  border-top:1px solid var(--hair)}
.sk-l:first-child{border-top:0}
.sk-c{flex:1;min-width:0;display:flex;flex-direction:column;gap:7px}
.sk{position:relative;overflow:hidden;border-radius:6px;background:var(--chip)}
.sk::after{content:"";position:absolute;inset:0;
  background:linear-gradient(105deg,transparent 30%,var(--lueur) 50%,transparent 70%);
  transform:translateX(-100%);animation:balaye 1.5s ease-in-out infinite}
@keyframes balaye{to{transform:translateX(100%)}}
.sk-rond{width:34px;height:34px;border-radius:10px;flex:0 0 auto}
.sk-t{height:11px;width:72%}
.sk-t.moyen{width:58%}
.sk-t.court{width:42%}
.attente-dit{display:flex;align-items:center;gap:8px;font-size:11.5px;color:var(--dim);
  padding-top:12px}
.pouls{width:6px;height:6px;border-radius:50%;background:var(--teal);flex:0 0 auto;
  animation:pouls 1.4s ease-in-out infinite}
@keyframes pouls{0%,100%{opacity:.25;transform:scale(.8)}50%{opacity:1;transform:scale(1)}}
@media (prefers-reduced-motion:reduce){
  .sk::after{animation:none}
  .pouls{animation:none;opacity:.7}}
.vide b{font-size:15px;font-weight:650;color:var(--ink)}
.vide p{margin:0;font-size:13px;color:var(--muted);max-width:44ch;line-height:1.6}
.vide .btn{margin-top:4px}

/* ---- 4 · Le passage d'une page à l'autre -------------------------------
   Django rend une page entière à chaque clic, d'où le clignotement. Cette
   règle demande au navigateur un fondu entre les deux rendus. Elle ne coûte
   rien : un navigateur qui ne la connaît pas l'ignore, et il ne se passe
   alors rien de plus qu'avant.

   L'en-tête et le volet latéral sont NOMMÉS : nommés, ils sont traités comme
   un même élément d'une page à l'autre et restent donc immobiles pendant le
   fondu, au lieu de disparaître et de réapparaître avec le reste. */
@view-transition{navigation:auto}
.topbar{view-transition-name:barre-du-haut}
.side{view-transition-name:volet-lateral}
::view-transition-old(root),::view-transition-new(root){animation-duration:220ms;
  animation-timing-function:cubic-bezier(.2,.7,.3,1)}
@media (prefers-reduced-motion: reduce){
  @view-transition{navigation:none}
}

/* ---- 5 · Les chiffres ne dansent plus ----------------------------------
   Mesuré sur Inter, à 30 px, sur six montants de même longueur : SIX largeurs
   différentes, 40,8 px d'écart entre la plus courte et la plus longue - le
   « 1 » est bien plus étroit que le « 8 ». Un total qui se met à jour respire
   donc à chaque changement, et une colonne d'euros ne s'aligne jamais tout à
   fait. Avec les chiffres tabulaires : une seule largeur, 0,0 px d'écart.

   Posé sur les tableaux (hérité par les cellules) et sur les grands nombres.
   Un nom propre qui contient des chiffres s'en trouve à peine affecté ; une
   colonne de montants, elle, cesse de zigzaguer. */
table,.big,.stat,.clock,.budget,.rc-amt,.mes b,.hcount,.nbadge{font-variant-numeric:tabular-nums}

/* ---- 6 · La densité ----------------------------------------------------
   Un seul jeton commande la hauteur des champs et des boutons ; le reste
   suit. Utile sur les longs tableaux - Devis, Contacts, Documents - sans rien
   redessiner. Le réglage se pose dans le volet latéral, à côté du thème, et
   se retient dans le navigateur. */
html[data-densite="compact"]{--ctl-h:30px;--radius:11px;--radius-sm:8px}
html[data-densite="compact"] td{padding:5px 8px}
html[data-densite="compact"] th{padding:0 8px 6px}
html[data-densite="compact"] .panel{padding-block:12px}
html[data-densite="compact"] .nav a{padding:6px 11px}
html[data-densite="compact"] .tile{padding:11px 13px}
html[data-densite="compact"] .tool{padding:13px;min-height:96px}
html[data-densite="compact"] .dash-tuile{padding:9px 12px;min-height:52px}
html[data-densite="compact"] .btn{font-size:12px}

/* ---- L'échelle des tableaux, appliquée aux petites mentions -------------
   Mesuré sur la page Revenus : dans la MÊME colonne, « Suivi Picsprod » à
   12 px et l'état à 11,5 px - deux étiquettes de même nature, deux tailles.
   Dans un tableau, tout ce qui n'est pas le texte de la cellule prend
   l'échelon « puce ». Ailleurs dans la page, « .small » ne bouge pas : c'est
   dans la comparaison ligne à ligne que l'écart se voit. */
td .small,th .small,td .chip,th .chip,td .rstat,td .rlien,td .pp-z,td .g,
td .bq-badge,td .af-pill,td .af-lk,td .rf-chip,td .rf-badge,td .mg-badge,
td .pg-mod,td .lg-role,td .cg-note{font-size:var(--t-puce)}

/* =========================================================================
   LA TUILE CHIFFRÉE · un seul modèle
   -------------------------------------------------------------------------
   Recensé : DIX-NEUF composants différents pour la même chose - un libellé et
   un nombre dans une boîte. Les valeurs allaient de 18 à 30 px, les graisses
   de 600 à 800, les libellés tantôt en capitales tantôt non, et une seule
   famille portait la bande de couleur à gauche.

   Le modèle retenu est celui de « Pilotage revenus » (.fr-card) : bande de
   4 px à gauche, libellé discret, nombre en chiffres tabulaires. Les autres
   classes sont RATTACHÉES à ce modèle plutôt que renommées : les gabarits
   gardent leur balisage, et le JavaScript qui vise « .fr-card » ou « .pp-tuile »
   continue de fonctionner.

   La bande vaut l'accent par défaut. Elle prend une couleur d'ÉTAT quand la
   page en a une à dire (.orange, .red, .green, .bleu, .gris) - sinon elle ne
   raconterait rien.
   ========================================================================= */
.fr-card,.fd-tuile,.af-tcard,.pp-tuile,.edt-tuile,.ev-stat,.bu-stat,.mg-tuile,
.ga-card,.lg-tuile,.px-stat,.qr-stat,.sd-stat,.cg-tuile,.bl-stat,.sim-chiffre,
.ab-stats .ab-tile,.bq-tot .tcard{
  background:var(--glass-2);border:1px solid var(--glass-line);
  border-left:4px solid var(--teal);border-radius:12px;padding:12px 16px;
  box-shadow:none;
  transition:transform var(--t) var(--sortie),box-shadow var(--t) var(--sortie),
             border-color var(--t) var(--sortie)}
@media (hover: hover){
  .fr-card:hover,.fd-tuile:hover,.af-tcard:hover,.pp-tuile:hover,.edt-tuile:hover,
  .ga-card:hover,.mg-tuile:hover,.lg-tuile:hover,.cg-tuile:hover{
    transform:translateY(-1px);box-shadow:var(--ombre-2)}
}
/* Les couleurs d'état, communes à toutes ces familles. */
.fr-card.orange,.fd-tuile.orange,.pp-tuile.orange,.cg-tuile.orange{border-left-color:var(--fr-orange)}
.fr-card.red,.fd-tuile.red,.pp-tuile.red,.cg-tuile.red{border-left-color:var(--fr-red)}
.fr-card.green,.fd-tuile.green,.pp-tuile.green,.cg-tuile.green{border-left-color:var(--fr-green)}
.fr-card.bleu,.fd-tuile.bleu,.pp-tuile.bleu,.cg-tuile.bleu{border-left-color:var(--fr-bleu)}
.fr-card.gris,.fd-tuile.gris,.pp-tuile.gris,.cg-tuile.gris{border-left-color:var(--fr-gris)}

/* Le libellé : discret, en bas de casse. Les capitales espacées faisaient
   trois libellés sur quatre plus bruyants que leur propre chiffre. */
.fr-lab,
.fd-tuile .lab,.af-tcard .lab,.ga-card .lab,.sd-stat .lab,.bl-stat .lab,
.pp-tuile .k,.edt-tuile .k,.lg-tuile .k,.cg-tuile .k,
.ev-stat .lbl,.bu-stat .lbl,.mg-tuile .lbl,
.px-stat .l,.qr-stat .l,.sim-chiffre .sim-l,.ab-stats .ab-tile .lbl,.bq-tot .tcard .lab{
  font-size:12px;color:var(--muted);font-weight:500;
  text-transform:none;letter-spacing:0;margin:0 0 2px}

/* La valeur : une seule taille, une seule graisse, et des chiffres qui ne
   dansent pas. */
.fr-val,
.fd-tuile .val,.af-tcard .val,.ga-card .val,.sd-stat .val,.bl-stat .val,
.ev-stat .val,.bu-stat .val,.mg-tuile .val,
.pp-tuile .v,.edt-tuile .v,.lg-tuile .v,.cg-tuile .v,.qr-stat .v,
.px-stat .n,.sim-chiffre .sim-v,.ab-stats .big,.ig-val,.sa-val,.bq-tot .tcard .val{
  font-size:20px;font-weight:700;letter-spacing:-.015em;line-height:1.15;
  margin-top:0;color:var(--ink);font-variant-numeric:tabular-nums}

/* La mention sous le nombre, quand il y en a une. */
.fr-net,.pp-tuile .d,.mg-tuile .sous,.ga-card .sous,.sd-stat .sous{
  font-size:11px;color:var(--dim);margin-top:3px;line-height:1.4}

/* La tuile qui compte plus que ses voisines. L'emphase passe par la BANDE et
   le cadre, jamais par un aplat coloré : un fond teinté refaisait une tuile qui
   ne ressemblait plus aux autres - c'était le cas du « Solde de l'Enveloppe »
   sur Suivi Picsprod, en dégradé doré au milieu de tuiles neutres. */
.fr-card.fort,.fd-tuile.fort,.pp-tuile.fort,.cg-tuile.fort,.mg-tuile.fort,
.bq-tot .tcard.fort{border-color:var(--glass-line);border-left-color:var(--gold-fg)}

/* ---- La bande, deuxième passe -------------------------------------------
   Le premier recensement partait des FEUILLES DE STYLE : il ne voyait que les
   tuiles dont la page déclarait une grande valeur en gras. Il en a raté la
   moitié. Le second est parti du RENDU - toute boîte contenant un petit
   libellé et un nombre écrit gros - et en a trouvé soixante et une de plus,
   dans cinq familles.

   « .tile » sert AUSSI de tuile de navigation, une centaine de fois : seules
   celles qui portent un grand nombre (« .big ») sont concernées, d'où le
   « :has() ». Sans lui, tout le tableau de bord se serait retrouvé rayé. */
.stat,.ig-card,.scard,.bq-card{border-left:4px solid var(--teal)}
/* …sauf les compteurs de l'accueil, devenus des pastilles. */
.statbar .stat{border-left-width:1px}
/* ET VOICI POURQUOI CE SÉLECTEUR-LÀ EST SI LOURD.
   Les deux règles de thème visent « html[data-theme="clair"] .tile » et sa
   jumelle sombre, soit (0,2,1) - l'une pose « border:0 », l'autre
   « border:1px ». Un simple « .tile:has(.big) » ne vaut que (0,2,0) : il
   PERD, où qu'il soit placé dans le fichier, et la bande ne paraissait pas
   sans que rien ne signale l'écrasement. Mesuré : bord gauche à 0 px alors
   que la règle correspondait bien à l'élément.
   On repasse donc au-dessus, explicitement. */
html[data-theme] .tile:has(.big),
html[data-theme] .dash-tuile:has(.big),
html[data-theme] .carte-douce:has(.big){border-left:4px solid var(--teal)}
/* Le survol de ces familles repeint TOUS les bords : la bande y perdait sa
   couleur. On la lui rend. */
.stat:hover,.statbar .stat:hover,.ig-card:hover,.scard:hover,.bq-card:hover,
html[data-theme] .tile:has(.big):hover,html[data-theme] .dash-tuile:has(.big):hover{
  border-left-color:var(--teal)}
.scard.on,html[data-theme] .tile:has(.big).on{border-left-color:var(--teal-fg)}


/* ══ L'ONGLET QUI GLISSE ════════════════════════════════════════════════════
   Un groupe d'onglets où le fond SAUTE d'un onglet à l'autre ne dit pas d'où
   l'on vient : l'œil retrouve la sélection au lieu de la suivre. Un pouce qui
   se déplace le dit, et coûte une seule transition.

   Le pouce est posé DANS le groupe, en position absolue, et se cale sur la
   position mesurée de l'onglet actif - jamais sur des largeurs supposées, qui
   changent avec la langue, la police et le zoom. Les onglets, eux, ne sont plus
   peints : c'est le pouce qui porte la couleur.

   Le groupe ne prend la classe « glissant » que si le script a pu mesurer :
   sans JavaScript, on retombe exactement sur l'ancien comportement. */
.glissant{position:relative}
.glissant > .pouce{position:absolute;z-index:0;top:3px;bottom:3px;left:0;width:0;
  border-radius:var(--pill,999px);background:var(--teal-bg);
  border:1px solid var(--teal-fg);pointer-events:none;
  transition:transform .24s cubic-bezier(.4,0,.2,1), width .24s cubic-bezier(.4,0,.2,1),
             opacity .18s}
.glissant > *:not(.pouce){position:relative;z-index:1}
/* L'onglet actif se dépeint : sinon on verrait deux fonds l'un sur l'autre
   pendant tout le trajet du pouce. */
.glissant.pret .sb-mode.active,
.glissant.pret .seg.on{background:transparent;border-color:transparent}
.segmented.glissant{overflow:visible}
.segmented.glissant > .pouce{border-radius:7px;top:0;bottom:0}
.segmented.glissant .seg+.seg{border-left-color:transparent}
@media (prefers-reduced-motion:reduce){
  .glissant > .pouce{transition:opacity .18s}
}


/* ── LA CAPSULE ────────────────────────────────────────────────────────────
   Des onglets espacés, chacun dans sa pastille, font sauter le pouce par-dessus
   des trous : ça ne glisse pas, ça bondit. Réunis dans UNE capsule, ils
   partagent un fond et le pouce circule dedans - c'est ce qui rend le geste
   lisible. Romain l'a vu tout de suite sur la démonstration.

   Les pages qui l'adoptent gardent leur propre style d'onglet ; la capsule ne
   fait qu'effacer le fond de l'ACTIF, puisque c'est le pouce qui le porte
   désormais. Et cet effacement doit passer en force : les styles d'onglets sont
   écrits dans un <style> DE LA PAGE, donc après cette feuille et gagnants à
   égalité de poids. C'est le seul endroit où l'on force, et seulement quand le
   pouce est réellement posé. */
/* Le sélecteur est DOUBLÉ à dessein. Les rangées d'onglets sont mises en forme
   dans un <style> de la page - « .vt-onglets{display:flex} » - qui arrive après
   cette feuille et gagne à poids égal. « .capsule.capsule » pèse deux classes
   et reprend la main sans avoir à retoucher six pages. */
.capsule.capsule{display:inline-flex;align-items:center;gap:2px;padding:3px;
  border-radius:var(--pill,999px);background:var(--chip);
  border:1px solid var(--glass-line);flex-wrap:nowrap;max-width:100%;
  overflow-x:auto;scrollbar-width:none;vertical-align:middle}
.capsule::-webkit-scrollbar{display:none}
/* Le pouce est exclu : sans cela l'effacement des fonds le rendrait invisible,
   et c'est LUI qui porte la couleur de l'onglet actif. */
.capsule > *:not(.pouce){border:0 !important;background:transparent !important;
  box-shadow:none !important;white-space:nowrap}
.capsule.pret > .on,.capsule.pret > .actif,.capsule.pret > .active,
.capsule.pret > .sb-mode.active,.capsule.pret > .seg.on{
  background:transparent !important;border-color:transparent !important;box-shadow:none !important}
.capsule.glissant > .pouce{top:3px;bottom:3px}
/* Dans une capsule, le pouce est PLEIN : c'est lui l'onglet actif, pas un
   contour posé autour. */
.capsule.glissant > .pouce{background:var(--solid);border-color:transparent;
  box-shadow:0 1px 3px rgba(0,0,0,.12)}


/* =========================================================================
   LA TUILE CLIQUABLE
   -------------------------------------------------------------------------
   Une tuile entière qui est un lien ne doit pas se comporter comme un lien de
   texte. Elle ne se souligne pas : elle se SOULÈVE, et le clic vaut partout
   sur elle. Demandé par Romain le 10/08/2026, captures à l'appui.

   CE QU'ON A MESURÉ AVANT DE TOUCHER. Cent neuf classes de ce dépôt écrivent
   « text-decoration:none » sur leur carte - leur auteur a donc dit ce qu'il
   voulait - et douze seulement le redisent dans une règle de survol. Les
   quatre-vingt-dix-sept autres se soulignaient quand même, parce que la règle
   globale « a:hover » pèse (0,1,1) et bat une classe seule (0,1,0). Ce n'était
   pas quatre-vingt-dix-sept oublis d'auteur : c'était une règle mal posée.

   On ne réécrit donc pas quatre-vingt-dix-sept fichiers. On corrige la portée
   de la règle globale pour les liens qui SONT une boîte, et on donne à ces
   boîtes-là le geste qu'elles n'avaient pas. Le repère n'est pas inventé : il
   existe déjà et il est bon, c'est « .statbar .stat ». On aligne dessus.

   Les vrais liens de texte - « .rlien », « .cg-lien », « .lien » - ne sont pas
   concernés et gardent leur soulignement au survol : c'est leur seule marque
   d'affordance, la retirer les rendrait muets.
   ========================================================================= */

/* ---- 1 · Un lien qui EMBALLE une boîte ne se souligne pas ---------------
   La détection est STRUCTURELLE et non nominative : un lien dont l'enfant
   direct est un bloc - un titre, un paragraphe, un div, une liste, un tableau
   - est une boîte, quel que soit le nom de sa classe. Une carte écrite demain
   en héritera sans qu'on ait à l'inscrire nulle part.

   Le poids est calculé, pas espéré : « a » (0,0,1) + le « :is » du « :has »
   (0,0,1) + « :hover » (0,1,0) = (0,1,2), contre (0,1,1) pour la règle
   globale. Sans le « :hover » dans le sélecteur, on serait à (0,0,2) et la
   règle aurait perdu en silence. */
a:has(> :is(div,h1,h2,h3,h4,h5,h6,p,section,article,ul,ol,dl,table,figure,form)):hover{
  text-decoration:none}

/* ---- 2 · …et elle se soulève ------------------------------------------
   Les familles connues - « .tile », « .tool », « .dash-tuile », « .minitile »,
   « .carte-douce » - le faisaient déjà. Voici celles que le recensement a
   trouvées seules dans leur coin, une par outil, chacune n'ayant qu'un
   changement de couleur de bord pour tout retour.

   Elles sont NOMMÉES et non détectées : soulever demande un jugement que le
   CSS ne peut pas rendre. Une rangée de liste ou une colonne de graphique
   contient elle aussi un bloc, et la voir décoller serait un défaut. */
.fd-tuile,.sc-carte,.pl-card,.tb-carte,.idea-carte,.scard,.gr-planche,
.sa-card,.stm-ami,.af-tcard,.md-dos,.vcard-thumb{
  transition:transform var(--t) var(--sortie), box-shadow var(--t) var(--sortie),
             border-color var(--t) var(--sortie)}
@media (hover: hover){
  .fd-tuile:hover,.sc-carte:hover,.pl-card:hover,.tb-carte:hover,.idea-carte:hover,
  .scard:hover,.gr-planche:hover,.sa-card:hover,.stm-ami:hover,.af-tcard:hover,
  .md-dos:hover,.vcard-thumb:hover{
    transform:translateY(-2px);box-shadow:var(--ombre-2)}
}
.fd-tuile:active,.sc-carte:active,.pl-card:active,.tb-carte:active,.idea-carte:active,
.scard:active,.gr-planche:active,.sa-card:active,.stm-ami:active,.af-tcard:active,
.md-dos:active,.vcard-thumb:active{
  transform:translateY(0) scale(.995);box-shadow:var(--ombre-creuse);transition-duration:70ms}

/* ---- 3 · Les pastilles : le même geste, en plus petit ------------------
   Une pastille n'est pas une carte : deux pixels la feraient sauter. Un seul,
   comme les compteurs de l'accueil qui servent ici de référence.

   « .fac-p » - les pastilles « À facturer » et « Émise » du tableau de bord -
   n'avait qu'un bord qui changeait de couleur, et se soulignait par-dessus le
   marché : c'est la capture que Romain a envoyée. */
.fac-p,.af-pill{transition:transform var(--t) var(--sortie),
  box-shadow var(--t) var(--sortie), border-color var(--t) var(--sortie),
  color var(--t) var(--sortie)}
.fac-p:hover,.af-pill:hover{text-decoration:none}
@media (hover: hover){
  .fac-p:hover,.af-pill:hover{transform:translateY(-1px);box-shadow:var(--ombre-2)}
}
.fac-p:active,.af-pill:active{transform:translateY(0) scale(.985);
  box-shadow:var(--ombre-creuse);transition-duration:60ms}

/* ---- 4 · Rien ne reste collé au doigt, rien ne s'impose ----------------
   Mêmes deux réserves que la couche d'interaction : sur un écran tactile il
   n'y a pas de sortie de survol, et qui a demandé moins d'animation dans son
   système ne doit pas en recevoir davantage. */
@media (hover: none){
  .fd-tuile:hover,.sc-carte:hover,.pl-card:hover,.tb-carte:hover,.idea-carte:hover,
  .scard:hover,.gr-planche:hover,.sa-card:hover,.stm-ami:hover,.af-tcard:hover,
  .md-dos:hover,.vcard-thumb:hover,.fac-p:hover,.af-pill:hover{transform:none}
}
@media (prefers-reduced-motion: reduce){
  .fd-tuile:hover,.sc-carte:hover,.pl-card:hover,.tb-carte:hover,.idea-carte:hover,
  .scard:hover,.gr-planche:hover,.sa-card:hover,.stm-ami:hover,.af-tcard:hover,
  .md-dos:hover,.vcard-thumb:hover,.fac-p:hover,.af-pill:hover,
  .fd-tuile:active,.idea-carte:active,.fac-p:active{transform:none}
}


/* ---- 5 · Les objets d'interface qui ne sont pas des boîtes -------------
   Le fil d'ariane, les pastilles, les onglets, les noms de fichier : ils ne
   contiennent aucun bloc, donc la règle structurelle ci-dessus ne les voit
   pas - et ils se soulignaient encore. C'est le fil de « Suivi Picsprod » que
   Romain a repéré.

   Le critère n'est plus « contient un bloc » mais « EST un objet ou un mot ».
   Un objet d'interface se pose lui-même, prend de la place, a une forme : sa
   règle porte un « display », un « padding », un rayon, un fond ou un bord.
   Un mot au milieu d'une phrase n'a rien de tout cela - et lui, il garde son
   soulignement, c'est sa seule affordance. Six classes sont dans ce cas et ne
   sont pas touchées : .back, .cg-lien, .cl-nom, .lien, .nt-ver-l, .pj-lien.

   Nommées et non détectées, faute d'un « :has() » du contenant. Le recensement
   qui les a trouvées est reproductible : toute classe qui écrit
   « text-decoration:none » sans le redire au survol se souligne contre
   l'intention de son auteur. */
.an-hline:hover,.ap-lien-espace:hover,.bib-proj:hover,.bib-th:hover,.bq-arrow:hover,
.bq-sort:hover,.card:hover,.ci-m:hover,.ct-th-sort:hover,.fc-row:hover,.flux-e:hover,
.fname:hover,.ga-tab:hover,.idea-chip:hover,.mbcard:hover,.md-porte:hover,.mq-l:hover,
.mrow:hover,.mz-row:hover,.qr-chip:hover,.rlien:hover,.sim-int:hover,.sup-proj:hover,
.sup-projtab:hover,.sup-promo:hover,.tr-chip:hover,.tr-onglet:hover,.tr-voir:hover,
.vm-lien:hover,.vmf-link:hover,.vt-chip:hover,.vt-onglet:hover,.vtf-link:hover{
  text-decoration:none}

/* ---- 6 · …et celles qui n'avaient QUE ça pour répondre ------------------
   Dix-sept de ces classes n'ont aucune autre réaction au survol : leur retirer
   le soulignement les laisserait muettes, ce qui est pire que le défaut qu'on
   corrige. On leur en donne un.

   Deux familles, deux gestes. Les pastilles et les onglets se soulèvent d'un
   pixel, comme les compteurs de l'accueil. Les mots posés dans une interface -
   un nom de fichier, un en-tête de colonne - reçoivent un halo de pastille
   fait en OMBRE PORTÉE et non en marge intérieure : une ombre ne prend aucune
   place, donc rien ne bouge autour. Un fond posé en « padding » aurait
   déplacé la ligne entière au passage de la souris. */
.idea-chip,.qr-chip,.tr-chip,.vt-chip,.tr-onglet,.vt-onglet,.card,.mbcard{
  transition:transform var(--t-doigt) var(--sortie), box-shadow var(--t) var(--sortie),
             border-color var(--t) var(--sortie)}
@media (hover: hover){
  .idea-chip:hover,.qr-chip:hover,.tr-chip:hover,.vt-chip:hover,
  .tr-onglet:hover,.vt-onglet:hover{transform:translateY(-1px);box-shadow:var(--ombre-2)}
  .card:hover,.mbcard:hover{transform:translateY(-2px);box-shadow:var(--ombre-2)}
  .ap-lien-espace:hover,.bib-th:hover,.fname:hover,.sup-projtab:hover,.sup-promo:hover,
  .tr-voir:hover,.vm-lien:hover,.vmf-link:hover,.vtf-link:hover,
  /* .ct-th-sort ne changeait QUE de couleur, et cette couleur est
  reprise en thème clair par la règle générale des liens : au survol, il ne se
  passait rien du tout. Le soulignement le cachait. */
  .ct-th-sort:hover{
    border-radius:4px;background:var(--chip);box-shadow:0 0 0 3px var(--chip)}
}
.ap-lien-espace,.bib-th,.fname,.sup-projtab,.sup-promo,.tr-voir,.vm-lien,
.vmf-link,.vtf-link,.ct-th-sort{
  transition:background var(--t) var(--sortie), box-shadow var(--t) var(--sortie)}
.idea-chip:active,.qr-chip:active,.tr-chip:active,.vt-chip:active,
.tr-onglet:active,.vt-onglet:active,.card:active,.mbcard:active{
  transform:translateY(0) scale(.985);box-shadow:var(--ombre-creuse);transition-duration:60ms}
@media (hover: none){
  .idea-chip:hover,.qr-chip:hover,.tr-chip:hover,.vt-chip:hover,.tr-onglet:hover,
  .vt-onglet:hover,.card:hover,.mbcard:hover{transform:none}
}
@media (prefers-reduced-motion: reduce){
  .idea-chip:hover,.qr-chip:hover,.tr-chip:hover,.vt-chip:hover,.tr-onglet:hover,
  .vt-onglet:hover,.card:hover,.mbcard:hover{transform:none}
}

/* =========================================================================
   TROISIÈME COUCHE · le mouvement qui PORTE UN SENS
   -------------------------------------------------------------------------
   Les deux couches précédentes règlent la réponse au geste et ce qui se passe
   entre les gestes. Celle-ci ajoute quatre mouvements qui disent quelque
   chose : l'ordre de lecture, « ça vient de changer », « vous êtes ici », et
   « voici où votre ligne est partie ». Rien de décoratif : chacun remplace une
   information qu'il fallait deviner.

   Posée en fin de fichier, retirable d'un bloc, aucune couleur nouvelle.
   ========================================================================= */

/* ---- 01 · L'arrivée en cascade -----------------------------------------
   Une page se posait d'un seul bloc. En décalant chaque tuile, l'oeil suit
   l'ordre de lecture au lieu de tout recevoir en même temps.

   Le décalage s'ARRÊTE au douzième : au-delà, la dernière tuile attendrait
   presque une seconde, et l'agrément deviendrait de l'attente. Le rang est
   posé par `initCascade` (base.html), qui plafonne aussi le compte. */
/* `backwards` ET SURTOUT PAS `both`. Une animation en état de remplissage
   crée un CONTEXTE D'EMPILEMENT, et `both` la laisse dans cet état pour
   toujours : chaque cellule de la grille en devenait un, définitivement.
   Le panneau de résultats de la recherche, pourtant en `z-index:50`, se
   retrouvait enfermé dans le contexte de SA cellule - et les cellules
   suivantes, peintes après, passaient par-dessus. C'est ce qui mettait les
   tuiles devant les résultats (signalé le 26/08/2026).

   `backwards` suffit : il tient l'état de départ pendant le délai, ce qui
   évite le clignotement avant l'entrée, puis LÂCHE tout une fois l'animation
   finie. Rien ne change à l'écran - la dernière image de `tuile-tombe` est
   déjà l'état naturel (`opacity:1; transform:none`). */
.cascade > *{animation:tuile-tombe 380ms var(--sortie) backwards;
  animation-delay:calc(var(--i, 0) * 55ms)}
@keyframes tuile-tombe{from{opacity:0;transform:translateY(9px) scale(.99)}to{opacity:1;transform:none}}

/* ---- 05 · La valeur qui change se signale ------------------------------
   Quand une saisie est enregistrée ou qu'un total se recalcule, rien ne le
   disait. Un halo d'une seconde sur la valeur modifiée suffit, et il s'éteint
   seul - aucun message à fermer. `window.signalerValeur(element)`. */
.valeur-changee{animation:valeur-halo 1100ms var(--sortie);border-radius:var(--radius-sm)}
@keyframes valeur-halo{
  0%{background:var(--teal-bg);box-shadow:0 0 0 3px var(--teal-bg)}
  70%{background:var(--teal-bg);box-shadow:0 0 0 3px transparent}
  100%{background:transparent;box-shadow:none}}

/* ---- 06 · Le repère qui suit le menu · RETIRÉ le 25/08/2026 -------------
   Essayé, posé en production, retiré le jour même. Deux raisons, la seconde
   étant celle qui compte :

   · Le rendu était mauvais. En thème clair, `--teal-fg` est presque noir : le
     filet de 3 px devenait une grosse capsule noire arrondie, qui se lisait
     comme un bouton abîmé et non comme un repère.
   · Surtout, le volet mêle des LIENS et des INTERTITRES de famille, et le
     repère se posait sur ces derniers. Le sélecteur aurait pu être resserré,
     mais le fond du problème demeure : l'entrée active ne change jamais sans
     rechargement de page, puisque Django rend une page entière à chaque clic.
     Il n'y a donc aucun glissement à voir - seulement un repère de plus qui
     redit ce que le fond et le filet `::before` disaient déjà.

   Ne pas le réintroduire sans que la navigation soit devenue côté client. */

/* ---- 04 · Le tri qui glisse --------------------------------------------
   Les lignes se téléportaient : impossible de suivre celle qu'on regardait.
   `window.trierEnGlissant(conteneur, comparateur)` relève les positions avant,
   réordonne, puis laisse chaque ligne rejoindre sa place. La classe n'est
   posée que le temps du mouvement. */
.glisse-en-place{transition:transform 340ms var(--sortie)}

@media (prefers-reduced-motion: reduce){
  .cascade > *{animation:none}
  .valeur-changee{animation:none}
  .glisse-en-place{transition:none}
}

/* ---- L'en-tête commun de page ------------------------------------------
   Trois emplacements et rien d'autre : le fil d'où l'on vient, le titre, les
   actions. Il remplace dix formes différentes qui vivaient chacune dans son
   gabarit, avec leur mise en forme écrite en dur.

   IL S'EFFACE S'IL N'A RIEN À DIRE. Django ne sait pas tester si un bloc est
   vide, mais le sélecteur le peut : une page qui ne donne pas de titre ne
   déclenche donc aucun espace, et rien ne change pour elle. C'est ce qui
   permet de poser cet en-tête sur deux cents pages sans en casser une seule. */
.tete-page{display:grid;gap:3px;margin:0 0 22px}
.tete-page:has(h1:empty){display:none;margin:0}
.tete-ligne{display:flex;align-items:flex-end;gap:16px;flex-wrap:wrap;justify-content:space-between}
.tete-ligne h1{margin:0;font-size:30px;font-weight:680;letter-spacing:-.025em;line-height:1.12;
  text-wrap:balance}
.tete-actions{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.tete-actions:empty{display:none}
/* Le FIL remplace à la fois le surtitre et le bouton « Retour » : il dit d'où
   l'on vient ET y ramène, sans occuper une ligne de plus. */
.tete-fil{display:flex;align-items:center;gap:7px;font-size:10.5px;text-transform:uppercase;
  letter-spacing:.1em;color:var(--dim);font-weight:650;min-height:15px}
.tete-fil a{color:inherit;text-decoration:none;
  transition:color var(--t) var(--sortie)}
.tete-fil a:hover{color:var(--ink);text-decoration:none}

/* ---- L'aide se replie sous un « i » ------------------------------------
   Cent trente et un paragraphes d'explication traînaient à même les pages.
   Ils ne sont pas supprimés : certains sont la seule trace écrite d'un
   fonctionnement - comment restaurer un dépôt Git si GitHub tombe, comment
   marche le chiffrement du coffre. On les replie là où ils sont, pour qu'ils
   gardent le contexte qu'ils expliquent.

   PAS de `display:contents` sur le <details> : le piège est connu ici, il fait
   perdre à l'élément sa boîte et les styles du parent cessent de s'y appliquer.
   Le <details> reste une boîte ordinaire. */
details.aide-repli{margin:10px 0 0}
details.aide-repli > summary{display:inline-flex;align-items:center;gap:7px;cursor:pointer;
  list-style:none;font-size:11.5px;color:var(--dim);user-select:none;
  padding:3px 9px 3px 3px;border-radius:999px;
  transition:color var(--t) var(--sortie), background var(--t) var(--sortie)}
details.aide-repli > summary::-webkit-details-marker{display:none}
details.aide-repli > summary::before{content:"i";width:18px;height:18px;flex:0 0 auto;
  border:1px solid var(--line);border-radius:50%;display:grid;place-items:center;
  font-size:10.5px;font-weight:700;font-style:italic;color:var(--dim);background:var(--solid);
  transition:color var(--t) var(--sortie), border-color var(--t) var(--sortie),
             background var(--t) var(--sortie)}
details.aide-repli > summary:hover{color:var(--ink);background:var(--chip)}
details.aide-repli > summary:hover::before{color:var(--teal-fg);border-color:var(--teal-fg);
  background:var(--teal-bg)}
details.aide-repli > summary:focus-visible{outline:none;box-shadow:var(--anneau)}
details.aide-repli[open] > summary{color:var(--ink)}
details.aide-repli[open] > summary::before{color:var(--teal-fg);border-color:var(--teal-fg)}
/* Le texte replié · même filet que les encadrés d'information de l'intranet. */
details.aide-repli > :not(summary){margin:8px 0 0;padding:12px 15px;
  border:1px solid var(--line);border-left:3px solid var(--teal-fg);
  border-radius:var(--radius-sm);background:var(--glass-2);
  font-size:12.5px;line-height:1.55;color:var(--muted);max-width:80ch;
  animation:aide-ouvre 240ms var(--sortie)}
@keyframes aide-ouvre{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion: reduce){details.aide-repli > :not(summary){animation:none}}
.tete-fil .tete-sep{opacity:.5}

/* Le fil dans la barre du haut · il occupe la place laissée par les menus
   transverses partis à l'étage. Il se tronque plutôt que de pousser les
   jauges, et disparaît sous 860 px où le bouton de menu prend sa place. */
.topbar .tete-fil{flex:0 1 auto;min-width:0;overflow:hidden;white-space:nowrap;
  text-overflow:ellipsis;display:block;line-height:1.2}
@media (max-width:860px){.topbar .tete-fil{display:none}}

/* ══ CE QUI RÉPOND SOUS LA SOURIS (05/09/2026) ═══════════════════════════════
   Romain, sur la première version de l'intranet : « les effets de survol sont
   très timides », « ça manque d'effet Wahou », avec iCloud en référence pour
   « plus fluide, plus animée, plus conviviale ».

   Deux tentatives de RECOLORATION ont été rejetées avant d'arriver ici. Ce
   qu'il demandait n'était pas une palette, c'était du MOUVEMENT. Aucune teinte
   n'est introduite dans ce bloc : les accents de survol empruntent `--teal`,
   déjà utilisé par le thème pour ses états actifs.

   TOUT TIENT DANS LA COURBE. `--sortie` part vite et arrive doucement, sans
   jamais dépasser sa cible : à durée égale elle paraît molle. `--ressort`
   dépasse légèrement avant de revenir, et c'est ce dépassement qui sépare
   « ça bouge » de « ça répond ». C'est le seul vrai changement ; le reste n'en
   est que l'application.

   Validé par Romain sur sa VRAIE page d'accueil, rendue avec ce CSS et un
   interrupteur pour comparer · pas sur une maquette. Les deux chantiers
   précédents avaient échoué faute exactement de ça.
   ─────────────────────────────────────────────────────────────────────────── */

/* ---- Les cartes et les tuiles ------------------------------------------- */
/* Pas de `will-change` : sur une grille de plusieurs dizaines de tuiles il
   réserve une couche de composition par élément pour un gain nul ici. */
.tile,.tool,.dash-tuile,.card,.mbcard,.statbar .stat{
  transition:transform var(--t-carte) var(--ressort),
             box-shadow var(--t-carte) var(--sortie),
             border-color var(--t) var(--sortie)}
.tile .ic,.tool .ic,.dash-tuile .ic{transition:transform var(--t-carte) var(--ressort)}
@media (hover: hover){
  .tile:hover,.tool:hover,.dash-tuile:hover,
  .card:hover,.mbcard:hover,.statbar .stat:hover{
    transform:translateY(-5px) scale(1.012);
    border-color:color-mix(in srgb, var(--teal) 45%, var(--line));
    box-shadow:0 20px 42px -20px rgba(0,0,0,.5), 0 4px 12px rgba(0,0,0,.14)}
  /* L'icône suit, avec un rien de rotation · c'est ce détail qui fait passer
     la tuile pour un objet plutôt que pour un rectangle. */
  .tile:hover .ic,.tool:hover .ic,.dash-tuile:hover .ic{
    transform:scale(1.12) rotate(-5deg)}
}
.tile:active,.tool:active,.dash-tuile:active{
  transform:translateY(-1px) scale(.998);transition-duration:90ms}

/* ---- Le menu de gauche --------------------------------------------------- */
/* Le rail se pose en `::after` · `::before` porte déjà celui de l'état actif,
   et l'écraser ferait disparaître le repère de la page courante. */
/* LE REBOND VAUT POUR TOUTE ENTRÉE, pas seulement pour celles qui sont des
   liens. « Personnel » et les groupes sont des boutons : ils ne bougeaient pas
   au survol quand leurs voisins avançaient de 3 px. */
.nav a,.nav .fam-label,.nav .grp-label{position:relative;
  transition:transform var(--t) var(--ressort), background var(--t) var(--sortie),
             color var(--t) var(--sortie)}
.nav a::after,.nav .fam-label::after,.nav .grp-label::after{
  content:"";position:absolute;left:-12px;top:50%;
  transform:translateY(-50%);width:3px;height:0;border-radius:0 3px 3px 0;
  background:var(--teal);pointer-events:none;
  transition:height var(--t-carte) var(--ressort)}
.nav a.active::after{height:23px}
@media (hover: hover){
  .nav a:hover::after,.nav .fam-label:hover::after,
  .nav .grp-label:hover::after{height:17px}
  .nav a:hover,.nav .fam-label:hover,.nav .grp-label:hover{transform:translateX(3px)}
}

/* ── LE BORD D'ÉTAT, POUR N'IMPORTE QUEL TABLEAU ──────────────────────────
   Une ligne déclare son état, et une arête de 4 px le montre à gauche. La
   pastille le dit en toutes lettres ; l'arête, elle, se lit SANS LIRE · on
   voit d'un coup d'œil combien de lignes appellent un geste, en quarante
   lignes comme en trois.

   EXPLICITE, ET NON DEVINÉ DEPUIS LA PASTILLE. On aurait pu déduire la teinte
   de la puce présente dans la ligne, avec « :has() », et le faire marcher
   partout sans toucher un gabarit. Mais une pastille n'est pas toujours un
   état : dans « Mes exports », « PII » est une ÉTIQUETTE, et un bord d'état y
   dirait une chose fausse. Chaque page déclare donc ce qu'elle veut dire.

   Emploi · sur la ligne :
     <tr class="etat" data-etat="erreur">
   Cinq valeurs, et ce sont celles du code couleur de Romain :
     ok · attente · erreur · info · clos (sans arête, volontairement) */
tr.etat>td:first-child{position:relative}
tr.etat>td:first-child::before{content:"";position:absolute;left:0;top:0;bottom:0;
  width:4px;background:var(--e,transparent);transition:width .16s var(--sortie)}
tr.etat:hover>td:first-child::before{width:6px}
tr.etat[data-etat="ok"]{--e:var(--fr-green)}
tr.etat[data-etat="attente"]{--e:var(--fr-orange)}
tr.etat[data-etat="erreur"]{--e:var(--fr-red)}
tr.etat[data-etat="info"]{--e:var(--fr-bleu)}
/* CLOS N'A PAS D'ARÊTE · lui en donner une, fût-elle grise, reviendrait à
   rendre à une ligne éteinte le poids visuel qu'on lui a justement retiré. */
tr.etat[data-etat="clos"]{--e:transparent}
@media (prefers-reduced-motion:reduce){tr.etat>td:first-child::before{transition:none}}

/* ---- Les lignes de tableau et de liste ----------------------------------- */
tbody tr{transition:background var(--t) var(--sortie)}
.crow,.lrow{transition:background var(--t) var(--sortie),
                       transform var(--t) var(--ressort)}
@media (hover: hover){
  /* On ne repeint pas une ligne DÉJÀ marquée · sélectionnée, active ou
     courante, elle porte un fond qui veut dire quelque chose. */
  tbody tr:not(.sel):not(.active):not(.on):not(.actif):hover{
    background:color-mix(in srgb, var(--teal) 7%, transparent)}
  .crow:hover,.lrow:hover{
    background:color-mix(in srgb, var(--teal) 6%, transparent);
    transform:translateX(2px)}
}

/* ---- Les boutons -------------------------------------------------------- */
/* Le soulèvement passe de 1 à 2 pixels et prend la courbe à ressort · à un
   pixel et sans dépassement, le geste ne se voyait pas. */
@media (hover: hover){
  .btn:hover{transform:translateY(-2px)}
  .btn.ghost:hover{background:var(--teal-bg);border-color:var(--teal)}
}
.btn{transition:transform var(--t-doigt) var(--ressort),
                box-shadow var(--t) var(--sortie), filter var(--t) var(--sortie),
                border-color var(--t) var(--sortie), color var(--t) var(--sortie),
                background var(--t) var(--sortie), opacity var(--t) var(--sortie)}
.btn:active{transform:translateY(0) scale(.965);transition-duration:70ms}

@media (prefers-reduced-motion: reduce){
  .tile,.tool,.dash-tuile,.card,.mbcard,.statbar .stat,.btn,.nav a,
  .crow,.lrow,tbody tr,.tile .ic,.tool .ic,.dash-tuile .ic,.nav a::after{
    transition-duration:.01ms !important}
  .tile:hover,.tool:hover,.dash-tuile:hover,.card:hover,.mbcard:hover,
  .statbar .stat:hover,.nav a:hover{transform:none}
}

/* ---- L'entrée en scène des blocs ---------------------------------------- */
/* LES DEUX CLASSES SONT POSÉES PAR LE SCRIPT, JAMAIS PAR LE GABARIT. C'est la
   seule façon sûre : si le script ne tourne pas - erreur, navigateur ancien,
   page servie sans JS - il ne reste rien et tout est visible. Un `opacity:0`
   écrit dans le HTML et attendant un script, c'est une page blanche le jour
   où le script casse.

   Pas de `animation` ni de `fill-mode` · une animation d'entrée en `both`
   laisse un contexte d'empilement PERMANENT qui piège ensuite tout élément
   flottant de la page. Une transition depuis une classe qu'on retire ne
   laisse rien du tout. */
.entree-depart{opacity:0;transform:translateY(16px)}
.entree-anime{transition:opacity 550ms var(--sortie), transform 550ms var(--ressort)}
@media (prefers-reduced-motion: reduce){
  .entree-depart{opacity:1;transform:none}
  .entree-anime{transition:none}
}

/* ---- Le bord d'un contrôle -------------------------------------------- */
/* `--line` sépare deux zones qui se touchent : il PEUT être discret, c'est un
   filet de mise en page. Le bord d'un bouton dit « ceci se clique » · c'est
   une information, et une forme non textuelle porteuse d'information demande
   3:1 contre son fond.

   MESURÉ, sur cette palette · un bouton secondaire ou fantôme ne se détachait
   de son fond que de 1,03 à 1,39 pour 1, selon le poids, le thème et la
   surface. Autant dire rien : il ne restait que le mot à lire, et un mot posé
   sur un fond uni ne ressemble pas à un bouton. C'était la réponse à
   « difficile d'identifier les boutons rapidement ».

   DEUX MESURES ET NON UNE · le bord doit se détacher de la surface qui ENTOURE
   le bouton et de son PROPRE remplissage, sans quoi il se voit de loin et
   disparaît de près. #8a857c rend 3,67 sur le blanc et 3,16 sur la page ;
   #716a60 rend 3,28 sur le panneau sombre et 3,53 sur la page.

   Aucune teinte nouvelle : deux gris CHAUDS, de la famille du laiton.

   Le rang des boutons, lui, n'est PAS en cause. Vérifié sur la page Factures,
   que je croyais mal rangée : « Filtrer » y porte un `btn second` délibéré et
   la page est correctement ordonnée. Le défaut n'était pas le rang, c'était
   que ce rang-là ne se voyait pas. */
:root{--ctl-line:#716a60}
html[data-theme="sombre"]{--ctl-line:#716a60}
html[data-theme="clair"]{--ctl-line:#8a857c}
/* `.btn.ghost.danger` pèse (0,3,0) et garde donc son bord rouge · une action
   destructrice ne doit pas retomber dans le gris commun. */
.btn.ghost,.btn.second{border-color:var(--ctl-line)}



/* ═══ LE TÉLÉPHONE ════════════════════════════════════════════════════════
   Sous 860 px, la seconde barre de l'en-tête ne peut pas tenir : ses six
   menus demandent 770 px. Elle disparaît, ses destinations passent dans le
   tiroir (bloc « Transverses »), et quatre d'entre elles remontent sur une
   barre d'onglets EN BAS · sous le pouce, là où il se trouve quand on tient
   l'appareil. Arbitré par Romain le 16/09/2026 contre deux autres sorties :
   le tiroir seul (deux gestes pour tout) et une barre qu'il faut pousser du
   doigt (les derniers outils se font oublier).
   Ce bloc est en FIN de fichier · « .nav-transverses » a longtemps porté
   aussi la classe « .nav », de même poids, et devait donc passer après elle
   pour l'emporter. Ce n'est plus qu'un bloc DANS le menu (17/09/2026), mais
   sa place ici reste la bonne : tout ce qui ne vaut qu'au téléphone est
   rassemblé au même endroit. */
.onglets{display:none}
.nav-transverses{display:none}
@media (max-width:860px){
  /* DIX PIXELS D'AIR SOUS LA BARRE D'ÉTAT · assez pour que le flou d'iOS ne
     morde plus les boutons de l'en-tête, assez peu pour ne pas voler une
     ligne à la page. Toutes les hauteurs calées sur « --top-h » suivent
     d'elles-mêmes. */
  :root{--top-h2:0px; --marge-barre:10px}
  .topbar2{display:none}
  /* LES TRANSVERSES DÉFILENT AVEC LE RESTE · elles formaient un SECOND
     « nav », donc un second conteneur de défilement, et restaient collées en
     haut du tiroir pendant que les familles défilaient dessous (signalé par
     Romain le 17/09/2026). On a tenté d'abord de leur donner leur hauteur
     naturelle ; le vrai remède était de les ranger DANS le menu principal,
     ce que fait désormais le gabarit. Il ne reste ici qu'à les montrer :
     sous 860 px seulement, puisque au large la barre du haut les porte. */
  /* SÉPARÉ DU RESTE, et pas seulement intercalé · ces six menus viennent de la
     barre du haut, pas de la table du menu. Un filet au-dessus et en dessous le
     dit à l'œil sans ajouter un mot (demandé par Romain le 17/09/2026). */
  .nav-transverses{display:flex;flex-direction:column;gap:2px;
    margin:8px 0;padding:8px 0;
    border-top:1px solid var(--glass-line);border-bottom:1px solid var(--glass-line)}
  .nav-transverses .section{padding-top:0}

  /* LA BARRE DU BAS RÉSERVE LA PLACE DE L'APPAREIL · le repère d'accueil de
     l'iPhone se dessine par-dessus la page, et il rognait les libellés
     « Messages », « Agenda » et « Fichiers ». La réserve était déjà demandée
     ici, mais « env() » répondait zéro faute de « viewport-fit=cover » dans le
     gabarit - corrigé le 17/09/2026, les deux vont ensemble.

     « max(…, 8px) » · sur un iPhone à bouton d'accueil la marge vaut zéro, et
     une barre collée au bord de l'écran se touche mal. Les marges latérales
     servent, elles, à l'orientation couchée. */
  .onglets{display:flex;position:fixed;left:0;right:0;bottom:0;z-index:45;
    background:var(--glass);backdrop-filter:blur(22px) saturate(140%);
    -webkit-backdrop-filter:blur(22px) saturate(140%);
    border-top:1px solid var(--glass-line);
    padding-bottom:max(var(--safe-bas), 8px);
    padding-left:max(var(--safe-gauche), 4px);
    padding-right:max(var(--safe-droite), 4px)}
  .ong{flex:1 1 0;display:flex;flex-direction:column;align-items:center;gap:3px;
    padding:8px 2px 6px;color:var(--muted);font-size:9.5px;font-weight:550;
    position:relative;background:none;border:0;cursor:pointer;font-family:inherit;
    text-decoration:none;min-width:0}
  .ong:hover{text-decoration:none;color:var(--ink)}
  .ong .icon{width:19px;height:19px}
  .ong>span{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .ong.on{color:var(--teal-fg)}
  .ong.on::before{content:"";position:absolute;top:0;left:50%;transform:translateX(-50%);
    width:22px;height:2.5px;border-radius:0 0 3px 3px;background:var(--teal)}
  /* La pastille se pose sur l'icône, pas dans le flux : le libellé tient déjà
     toute la largeur de l'onglet et n'a pas de place à céder. */
  .ong .opt{position:absolute;top:5px;right:calc(50% - 17px);min-width:15px;height:15px;
    padding:0 4px;border-radius:8px;background:var(--red-bg);color:var(--fr-red);
    font-size:9px;font-weight:700;display:grid;place-items:center;line-height:1}

  /* La page rend la place que les onglets prennent · sans quoi la dernière
     ligne de chaque page se lit sous une barre opaque. */
  .page{padding-bottom:calc(76px + max(var(--safe-bas), 8px))}
  .rap-pan{bottom:calc(78px + max(var(--safe-bas), 8px))}
}

/* ══ L'AIDE D'UNE RUBRIQUE SE REPLIE ══════════════════════════════════════
   RÈGLE DE L'INTRANET, posée par Romain le 17/09/2026 et valable PARTOUT :
   une explication de rubrique - « à quoi sert ce champ », « comment marche
   cette section » - ne s'imprime pas dans la page. Elle se range derrière un
   « i » à côté du titre, et s'ouvre au clic.

   Un paragraphe gris qui explique un champ sert une fois et encombre les cent
   fois suivantes : il pousse les champs vers le bas, allonge la page, et sur
   téléphone il faut faire défiler trois écrans avant d'atteindre le premier
   formulaire. Gabarit : socle/templates/_aide.html.

   CE QUI RESTE À L'ÉCRAN : ce qui n'est pas une explication mais une
   CONSÉQUENCE · un avertissement, un état, ce qu'un bouton va déclencher. Une
   infobulle se ferme ; une conséquence doit se lire sans geste.

   La bulle est en « position:absolute » dans un « .aide » en « relative » :
   pas de « fixed », qui se calerait sur le premier ancêtre filtré et non sur
   l'écran (le piège du panneau des rappels, corrigé le même jour). */
.aide{position:relative;display:inline-flex;vertical-align:-3px}
.aide-bt{display:inline-flex;align-items:center;justify-content:center;
  width:19px;height:19px;padding:0;border-radius:50%;cursor:pointer;
  border:1px solid var(--line);background:var(--chip);color:var(--muted)}
.aide-bt:hover{color:var(--teal-fg);border-color:var(--teal)}
.aide-bt .icon{width:12px;height:12px}
.aide-bt[aria-expanded="true"]{color:var(--teal-fg);border-color:var(--teal);
  background:var(--teal-bg)}
.aide-bulle{position:absolute;top:calc(100% + 7px);left:0;z-index:50;
  width:min(320px,calc(100vw - 32px));display:flex;flex-direction:column;gap:5px;
  padding:11px 13px;border-radius:12px;background:var(--solid);
  border:1px solid var(--line);box-shadow:0 14px 34px rgba(0,0,0,.34);
  font-size:12.5px;line-height:1.55;color:var(--muted);font-weight:400;
  text-transform:none;letter-spacing:normal;text-align:left;white-space:normal}
html[data-theme="clair"] .aide-bulle{box-shadow:0 12px 30px rgba(38,36,31,.14)}
.aide-bulle b{font-size:12px;color:var(--ink);font-weight:650}
/* « hidden » est de spécificité nulle : le « display » ci-dessus l'annulerait
   sans prévenir. On le réarme, comme pour le panneau des rappels. */
.aide-bulle[hidden]{display:none}
/* Près du bord droit, la bulle sortait de l'écran · la classe est posée par le
   JavaScript au moment de l'ouverture, quand la place se mesure. */
.aide-bulle.a-gauche{left:auto;right:0}
