/* SETRAT Gestion — feuille de style commune. Sortie de templates/base.html le 17/09/2026 (revue UI/UX, lot D, n°6) :
   53 Ko de CSS étaient ré-écrits dans CHAQUE page. Le navigateur garde ce fichier (empreinte ?v=). */
:root{--panel:#fff;
      /* identité SETRAT : violet + orange du logo (« Au Service du BTP ») */
      --navy:#443188; --navy2:#5B4BA5; --blue:#E8862F; --green:#198663;
      --red:#A32D2D; --amber:#A66815; --grey:#F2F0F7; --line:#DAD6E4; --ink:#26232E;
      /* 🩶 09/09 — `--muted` N'EXISTAIT QUE DANS LA COUCHE SOMBRE. Le gris du thème clair
         vivait en dur dans `.muted` ; tout gabarit écrivant `var(--muted)` héritait donc
         de la couleur normale en clair, et ne grisait rien. Mesuré sur le panneau du
         bulletin : montant et total à 14,43 de contraste tous les deux, aucune
         distinction. La variable est complétée ici, à sa valeur d'origine. */
      --muted:#6b6a64;
      /* 🎨 23/08 — CE QUI EST ÉCRIT EN BLANC DOIT SE LIRE. Mesuré : le blanc tombait
         à 3,39:1 sur le vert et 3,72:1 sur l'ambre (seuil 4,5:1 pour du texte normal,
         et une pastille fait 0,74 rem). Le vert et l'ambre ont été assombris à teinte
         CONSTANTE — 160,9° et 34,6°, exactement les mêmes qu'avant — jusqu'à 4,53:1
         et 4,55:1. Sur fond clair ils y gagnent aussi (un chiffre vert de carte passe
         de 3,39 à 4,53).
         L'ORANGE DU LOGO, LUI, NE BOUGE PAS : c'est l'identité SETRAT. Mais on cesse
         d'écrire en blanc dessus (2,66:1) — sur une surface orange le texte est
         désormais l'encre (5,79:1) — et le TEXTE orange, lui, prend `--blue-txt`,
         le même 28,2° assombri jusqu'à 4,52:1 sur blanc. Les deux vivent ensemble :
         `--blue` peint, `--blue-txt` écrit. */
      --blue-txt:#B56014;
    }
    *{box-sizing:border-box}
    /* 13/08 — LE FIELDSET NE SE RÉDUIT PAS TOUT SEUL. Les navigateurs lui donnent
       `min-inline-size: min-content` : il ignore la largeur de son parent et impose la
       sienne. L'écran de POINTAGE en faisait les frais depuis toujours — un formulaire
       de 358 px contenant un fieldset de 410, donc une page de 426 dans un téléphone de
       390. Trois rôles concernés (Yassine, Saloua, abdelhafid), c'est-à-dire ceux qui
       pointent au téléphone tous les jours. La règle est globale : elle vaut aussi pour
       la fiche de parc, et pour tout fieldset à venir. */
    fieldset{min-inline-size:0}
    /* ⚓ 23/08 — LA CIBLE D'UN SAUT DE PAGE ATTERRISSAIT SOUS L'EN-TÊTE COLLANT,
       donc entièrement masquée : on arrivait sur une ligne qu'on ne voyait pas.
       L'en-tête et la barre d'onglets sont tous deux `sticky` ; le défilement doit
       leur réserver leur hauteur. */
    html{scroll-padding-top:calc(var(--entete-h, 60px) + 62px)}
    body{margin:0;font-family:'Segoe UI',system-ui,Arial,sans-serif;color:var(--ink);background:#FAFAF7}
    header{background:var(--navy);color:#fff;padding:10px 16px;display:flex;align-items:center;
      justify-content:space-between;flex-wrap:wrap;gap:8px;position:sticky;top:0;z-index:10}
    header .brand{font-weight:700;font-size:1.05rem;letter-spacing:.3px}
    /* le slogan sur le violet de l'en-tête : l'orange de marque n'y donne que
       3,86:1 à 0,72 rem. Même teinte, éclaircie jusqu'à 4,53:1. */
    header .brand small{color:#EC994F}
    header .brand small{display:block;font-weight:400;font-size:.75rem;opacity:.8}
    nav{display:flex;gap:4px;flex-wrap:wrap;align-items:center}
    /* 🧭 23/08 (audit UI) — L'EN-TÊTE MANGEAIT UN QUART DE L'ÉCRAN DU DIRECTEUR.
       Mesuré au bon rôle, sur un portable 1280×720 : 190 px d'en-tête + 49 de barre
       d'onglets = 240 px sur 720, soit 33 %, en permanence, sur la machine où il lit
       ses tableaux de bord. (Un pointeur n'est pas concerné : ses neuf entrées
       tiennent sur une ligne dès 1 079 px — le constat n'existait pas si je le
       mesurais sur le seul compte dev.)
       Deux gaspillages, tous deux réglés ici sans rien retirer du menu :
       ① la MARQUE occupait une ligne à elle seule — 226 px, et 1 054 px de vide à sa
         droite — pendant que la nav s'enroulait juste en dessous ; elle se serre
         désormais à sa largeur et la nav vient combler le reste de la ligne ;
       ② au-dessus de 900 px on vise à la SOURIS, pas au pouce : les entrées peuvent
         se resserrer sans perdre leur cible (le confort tactile reste entier sur
         téléphone, où la règle de 34 px minimum continue de valoir). */
    @media(min-width:901px){
      header{flex-wrap:nowrap;align-items:flex-start}
      header .brand{flex:0 0 auto}
      nav{flex:1 1 auto;justify-content:flex-end}
      nav a,details.navgrp>summary{padding:6px 10px}
    }
    .navlinks{display:flex;gap:4px;flex-wrap:wrap;align-items:center}
    /* MENU REPLIABLE (29/07) : en dessous de 900 px, les liens passent derrière ☰ —
       l'en-tête tombe de ~369 px à ~90 px sur téléphone. Cloche et compte restent
       toujours dehors : on doit voir ce qui attend sans ouvrir le menu. */
    .navtoggle{display:none;background:rgba(255,255,255,.14);color:#fff;border:none;
      padding:8px 14px;border-radius:8px;font-size:.9rem;font-weight:600;cursor:pointer;
      font-family:inherit}
    .navtoggle:hover{background:rgba(255,255,255,.24)}
    @media(max-width:900px){
      .navtoggle{display:inline-flex;align-items:center;gap:6px}
      .navlinks{display:none;width:100%;flex-direction:column;align-items:stretch;
        gap:2px;margin-top:8px;max-height:65vh;overflow-y:auto}
      .navlinks.ouvert{display:flex}
      .navlinks .navsep{display:none}
      .navlinks .apercu-sel{width:100%;margin-top:6px}
    }
    nav a{color:#fff;text-decoration:none;padding:7px 13px;border-radius:7px;font-size:.9rem}
    nav a:hover{background:rgba(255,255,255,.12)}
    /* 🧭 23/08 — le marqueur « vous êtes ici » était le SEUL élément illisible du
       menu : blanc sur l'orange de marque, 2,66:1, quand tous ses voisins sont à
       10,3:1 sur le violet. L'orange reste (c'est lui qui dit « ici »), l'écriture
       passe à l'encre : 5,79:1. Un surligneur, pas un trou. */
    nav a.on{background:var(--blue);color:var(--ink);font-weight:600}
    nav .navsep{width:1px;align-self:stretch;background:rgba(255,255,255,.28);margin:3px 5px}
    @media(max-width:760px){nav .navsep{display:none}}
    /* --- groupes de modules (direction / dev) : menus déroulants --- */
    details.navgrp{position:relative}
    details.navgrp>summary{list-style:none;cursor:pointer;color:#fff;padding:7px 13px;
      border-radius:7px;font-size:.9rem;user-select:none;white-space:nowrap}
    details.navgrp>summary::-webkit-details-marker{display:none}
    /* 🔽 10/08 (Saïd, 2e passage : « je ne vois pas que la flèche est corrigée ») —
       il en restait DEUX dans le CSS global, et ce sont celles qu'on voit le plus :
       celle des groupes du menu (0,72 rem à 70 % d'opacité) et celle des blocs
       dépliables (0,95 rem). Toutes les flèches de l'application portent désormais la
       même taille et la même présence — c'est ce qu'il demandait dès le départ :
       « soit agrandir, soit une autre solution, system wide ». */
    details.navgrp>summary::after{content:" ▾";font-size:1rem;opacity:1;
      display:inline-block;transition:transform .15s ease}
    details.navgrp[open]>summary::after{transform:rotate(180deg)}
    details.navgrp>summary:hover{background:rgba(255,255,255,.12)}
    details.navgrp[open]>summary,details.navgrp>summary.on{background:var(--blue);
      color:var(--ink);font-weight:600}
    /* 🧭 23/08 — LE MENU DÉBORDAIT SOUS LE BAS DE L'ÉCRAN, SANS POUVOIR DÉFILER.
       « 📊 Direction » compte 17 entrées : sur un portable 1280×720, les deux
       dernières — « Décomptes » et « Marché & cautions » — étaient hors de portée,
       et rien ne le disait. Un menu qui ne rentre pas se fait défilant. */
    details.navgrp>.navdd{position:absolute;left:0;top:calc(100% + 4px);z-index:60;
      background:#362768;border:1px solid rgba(255,255,255,.18);border-radius:10px;
      padding:6px;min-width:215px;box-shadow:0 8px 22px rgba(0,0,0,.35);
      display:flex;flex-direction:column;gap:2px;
      max-height:calc(100vh - var(--entete-h, 60px) - 24px);overflow-y:auto;
      overscroll-behavior:contain}
    details.navgrp>.navdd a{display:block;border-radius:7px;padding:9px 12px}
    @media(max-width:640px){       /* mobile : le groupe se déplie sur place (pas de survol) */
      details.navgrp>.navdd{position:static;box-shadow:none;border:none;min-width:0;
        padding:0 0 4px 18px;background:transparent}
    }
    /* Tableaux sur téléphone : masquer les colonnes secondaires + resserrer */
    /* 🔔 cloche des tâches : le bouton reste compact (sinon le summary ouvert s'étire
       sur toute la ligne en mobile), le badge colle à la cloche */
    details.cloche>summary{width:max-content;padding:7px 12px 7px 9px;font-size:1.05rem;
      display:inline-flex;align-items:center;white-space:nowrap}
    details.cloche>summary::after{content:""}   /* pas de ▾ : la cloche + badge suffisent */
    /* CHEVRON D'OUVERTURE (Saïd 29/07 : « on clique sur le numéro mais pas d'indication »).
       Cause : passer un <summary> en display:flex SUPPRIME le marqueur natif du
       navigateur — c'était le cas des deux lignes qu'on clique le plus, le bon de
       commande et la demande d'appro. On pose donc un chevron explicite, qui pivote à
       l'ouverture. Exclus : les groupes du menu (déjà un ▾), la cloche, et les summary
       déguisés en bouton. */
    details:not(.navgrp):not(.cloche)>summary{list-style:none}
    details:not(.navgrp):not(.cloche)>summary::-webkit-details-marker{display:none}
    /* le chevron est SORTI DU FLUX (position absolue) : en élément flex il devenait la
       3e « colonne » d'un summary en justify-content:space-between et projetait le
       numéro du bon à l'autre bout de la ligne. */
    details:not(.navgrp):not(.cloche)>summary:not(.btn){position:relative;padding-left:24px}
    details:not(.navgrp):not(.cloche)>summary:not(.btn)::before{
      content:"▸";position:absolute;left:0;top:.02em;
      color:var(--navy);font-size:1.15rem;font-weight:700;
      transition:transform .15s ease}
    details[open]:not(.navgrp):not(.cloche)>summary:not(.btn)::before{transform:rotate(90deg)}
    /* 🔽 10/08 (Saïd : « regarde les chevrons sur les BC et les demandes, tu n'as pas
       TOUS les chevrons ») — il avait raison, et l'oubli venait de ma propre règle :
       `summary:not(.btn)` excluait les dépliables habillés en BOUTON. 14 sur le seul
       écran Achats n'avaient donc AUCUNE flèche. Or un bouton qui OUVRE un panneau et
       un bouton qui AGIT se ressemblent — et se confondent. Celui qui ouvre porte
       maintenant sa flèche, et elle pivote : on sait avant de cliquer si ça déplie ou
       si ça exécute. */
    details>summary.btn{position:relative}
    details>summary.btn::after{content:" ▾";display:inline-block;font-size:1rem;
      margin-left:6px;transition:transform .15s ease}
    details[open]>summary.btn::after{transform:rotate(180deg)}
    /* la pastille rouge du compte (le style .badge n'existait que pour les liens de nav) */
    details.cloche .badge{display:inline-block;min-width:18px;text-align:center;
      background:var(--red);color:#fff;border-radius:20px;font-size:.75rem;font-weight:700;
      padding:1px 6px;line-height:1.5}
    details.cloche>.navdd a{white-space:normal;line-height:1.4;padding:10px 12px;
      border-bottom:1px solid rgba(255,255,255,.08);overflow-wrap:anywhere}
    details.cloche>.navdd a:last-of-type{border-bottom:none}
    @media(max-width:640px){
      /* mobile : la nav s'enroule sur plusieurs lignes — la cloche quitte le flux et
         s'ÉPINGLE en haut à droite (toujours au même endroit, à côté de la marque),
         son panneau se SUPERPOSE à la page au lieu de pousser tout l'en-tête */
      header{padding-right:56px}
      details.cloche{position:absolute;top:8px;right:10px}
      details.cloche>summary{font-size:1.25rem;padding:8px 10px}
      details.cloche>.navdd{position:absolute;left:auto;right:0;top:calc(100% + 6px);
        min-width:255px;max-width:calc(100vw - 24px);background:#362768;
        border:1px solid rgba(255,255,255,.18);border-radius:10px;padding:6px;
        box-shadow:0 10px 26px rgba(0,0,0,.4)}
    }
    /* 🔔 10/08 — LE PANNEAU DE LA CLOCHE DÉFILE DANS LUI-MÊME. Saïd, avec 121 tâches :
       « il faut que je dézoome la page pour regarder le bas, et parfois quand je zoome
       ça va à droite, et j'ai aussi le problème sur mon mobile Safari ». Le panneau
       n'avait AUCUNE limite de hauteur : 121 tâches font près de 5 000 px, soit six
       écrans. Un bloc plus grand que la fenêtre ne se lit qu'en dézoomant, et sur iOS
       il emmène le zoom avec lui. Le menu principal en mobile a ce garde-fou depuis
       juillet (`.navlinks{max-height:65vh;overflow-y:auto}`) — la cloche ne l'avait
       jamais eu. `dvh` suit la barre d'adresse de Safari qui se rétracte au défilement
       (`vh` reste en repli pour les vieux navigateurs) et `overscroll-behavior` empêche
       le défilement de repartir dans la page derrière, une fois la liste au bout. */
    details.cloche>.navdd{box-sizing:border-box;
      max-height:min(70vh,560px);max-height:min(70dvh,560px);
      overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch}
    /* et les repères restent lisibles pendant qu'on fait défiler : à la 50e tâche on ne
       sait plus si on lit de l'argent ou du matériel */
    details.cloche>.navdd .th9{position:sticky;top:-6px;z-index:1;background:#362768;
      margin-left:0;margin-right:0;padding-left:8px;padding-right:8px}
    /* indice « le tableau continue à droite » (posé par JS sur les conteneurs qui débordent) */
    .swipehint{display:inline-block;font-size:.75rem;font-weight:600;color:#8a5512;background:#FBF1E0;
      border:1px solid var(--amber);border-radius:20px;padding:2px 10px;margin:0 0 6px}
    /* repli par paquets : masqué par le script, sans toucher au style en ligne des filtres */
    tr.paq-off{display:none}
    /* SOUS-MODULES (décision Saïd 27/07) : une page encombrée se découpe en onglets —
       un seul sous-module affiché à la fois, ?vue= dans l'URL. Barre défilable au pouce. */
    /* 29/07 (Saïd, après comparaison de 6 maquettes dans la vraie page) : les onglets
       étaient des pastilles BLANCHES bordées de gris pâle, posées sur un fond quasi
       blanc, entre deux cartes blanches — invisibles, et lus comme « une carte de plus ».
       Désormais un RAIL gris contient le groupe (il dit « un choix parmi ceux-ci »),
       les onglets inactifs sont blancs SUR CE RAIL donc lisibles, l'actif est violet. */
    /* COLLANTE (Saïd 29/07) : la barre suit le défilement et se pose SOUS l'en-tête —
       `--entete-h` est mesuré en JS, l'en-tête n'ayant pas de hauteur fixe (il change
       avec la largeur et le rôle). Sans ça, sur Stock (3 300 px) ou Parc (5 200 px) on
       perdait les onglets dès les premiers centimètres de défilement. */
    .smodbar{display:flex;gap:4px;overflow-x:auto;white-space:nowrap;margin:0 0 14px;
      padding:5px;background:#E4E0EE;border-radius:12px;
      position:sticky;top:calc(var(--entete-h, 0px) + 4px);z-index:8;
      box-shadow:0 2px 8px rgba(38,35,46,.10);
      -webkit-overflow-scrolling:touch;scrollbar-width:none;
      /* 🧭 23/08 — 59 % DES ONGLETS POUVAIENT ÊTRE HORS CHAMP SANS AUCUN INDICE.
         La barre défile, mais son ascenseur est masqué (à raison : au pouce il
         gêne) — donc sur téléphone on ne pouvait pas SAVOIR qu'il restait des
         onglets à droite. Ces deux ombres n'existent que du côté où il reste
         quelque chose : elles se collent au contenu (background-attachment:local),
         donc elles s'effacent d'elles-mêmes quand on atteint le bord. */
      background-image:
        linear-gradient(to right, #E4E0EE 40%, rgba(228,224,238,0)),
        linear-gradient(to left, #E4E0EE 40%, rgba(228,224,238,0)),
        radial-gradient(farthest-side at 0 50%, rgba(38,35,46,.20), rgba(38,35,46,0)),
        radial-gradient(farthest-side at 100% 50%, rgba(38,35,46,.20), rgba(38,35,46,0));
      background-position:left center,right center,left center,right center;
      background-repeat:no-repeat;
      background-size:34px 100%,34px 100%,15px 100%,15px 100%;
      background-attachment:local,local,scroll,scroll}
    .smodbar::-webkit-scrollbar{display:none}
    .smodbar a{flex:0 0 auto;display:inline-flex;align-items:center;gap:6px;
      padding:10px 16px;border-radius:8px;background:#fff;
      color:var(--navy);text-decoration:none;font-size:.9rem;font-weight:600}
    .smodbar a.on{background:var(--navy);color:#fff}
    /* 🔔 19/08 : le compteur d'un onglet (les pièces légales dépassées). Rouge parce
       qu'il dit un retard, discret parce qu'il n'appelle pas un geste immédiat — c'est
       précisément pour ça qu'il a quitté la cloche. */
    .smodbar a .smodbadge{display:inline-flex;align-items:center;justify-content:center;
      min-width:18px;height:18px;padding:0 5px;margin-left:6px;border-radius:9px;
      background:var(--red);color:#fff;font-size:.75rem;font-weight:700;line-height:1}
    .smodbar a.on .smodbadge{background:#fff;color:var(--navy)}
    /* compteur de lignes, posé par JS au-dessus de chaque liste (voir window.compter) */
    .compte{font-size:.82rem;color:#6b6a64;margin:0 0 6px;display:flex;align-items:baseline;gap:6px;flex-wrap:wrap}
    .compte b{color:var(--navy);font-size:1rem;font-weight:700}
    .compte .filtre{color:#8a5512;background:#FBF1E0;border:1px solid var(--amber);
      border-radius:20px;padding:1px 9px;font-size:.75rem;font-weight:600}
    nav a .badge{display:inline-block;min-width:18px;text-align:center;background:var(--red);color:#fff;
      border-radius:20px;font-size:.75rem;font-weight:700;padding:1px 6px;margin-left:4px}
    .usermenu{color:#fff;font-size:.9rem;padding:6px 4px 6px 12px;display:inline-flex;align-items:center;gap:7px;
      border-left:1px solid rgba(255,255,255,.25);margin-left:6px}
    .usermenu em{font-style:normal;background:var(--green);padding:2px 8px;border-radius:20px;font-size:.75rem}
    .usermenu a{padding:5px 9px;font-size:1rem}
    /* 📱 23/08 (arbitrage Saïd) — SUR TÉLÉPHONE, LE NOM PRENAIT UNE LIGNE ENTIÈRE DE
       L'EN-TÊTE. Mesuré à 390 px : 149 px d'en-tête, dont 40 pour la seule ligne du
       compte, parce que ☰ + la cloche + le compte (221 px) dépassaient de 25 px la
       largeur de la barre. Sa décision : garder la pastille de RÔLE — c'est elle qui
       lui sert à contrôler l'aperçu — et raccourcir le nom.
       Il se borne en LARGEUR, jamais par découpage de mots : « EL AMRANI
       Abdelouahab » donnerait « EL », et « M. B. Boudali » donnerait « M. ». Le début
       du nom suffit à se reconnaître, quel que soit l'ordre nom/prénom. */
    @media(max-width:640px){
      .usermenu{padding-left:6px;margin-left:3px;gap:5px}
      .usermenu .nom9{display:inline-block;max-width:78px;overflow:hidden;
        text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}
    }
    /* Sous 400 px — les Android courants (360) et l'iPhone SE (375) — il manquait
       encore 35 px et la ligne du compte revenait. Le nom se resserre un peu plus,
       ET le mot « Menu » s'efface à côté du ☰.
       J'ai d'abord cru que ce mot ne coûtait rien : en le rendant visible EN JS sur
       une page déjà mise en page, la hauteur ne bougeait pas. Faux — mesuré au
       chargement, il coûte 39 px à 360 px pour Zineb et pour un pointeur (dont les
       noms sont plus longs que « M. B. Boudali »). Rendre un élément après coup ne
       reproduit pas la mise en page initiale : il faut recharger pour mesurer. */
    @media(max-width:400px){
      .navtoggle span.mot9{display:none}
      .navtoggle{padding:8px 12px}
      .usermenu .nom9{max-width:62px}
    }
    .apercu-sel{width:auto;padding:6px 8px;border-radius:7px;border:1px solid rgba(255,255,255,.35);
      background:rgba(255,255,255,.12);color:#fff;font-size:.9rem;cursor:pointer}
    .apercu-sel option{color:#23252B}
    .apercu-bar{background:var(--amber);color:#fff;text-align:center;padding:7px 14px;font-size:.9rem}
    .apercu-bar a{color:#fff;font-weight:700;margin-left:10px;text-decoration:underline}
    main{max-width:1080px;margin:0 auto;padding:16px}
    /* 💬 23/08 (audit UI, 3e passage) — LE MESSAGE DE CONFIRMATION PARTAIT HORS
       ÉCRAN. Depuis le 09/08, réceptionner un bon ramène SUR ce bon (ancre #bcNN)
       pour qu'on voie son statut changer — bonne idée. Mais le message, lui, est
       rendu en haut de `main` : mesuré à 1 623 px au-dessus du haut de la fenêtre
       sur une liste de 30 bons. On enregistrait une réception et on ne lisait
       jamais ce que l'application venait d'écrire — ni les reliquats, ni le
       « bon SOLDÉ », ni un éventuel avertissement.
       Il se colle donc sous l'en-tête : où qu'on atterrisse dans la page, on le
       lit. Cela vaut pour TOUS les écrans, pas seulement les achats. */
    .flash{padding:11px 14px;border-radius:8px;margin:0 0 14px;font-size:1rem;
      position:sticky;top:calc(var(--entete-h, 60px) + 8px);z-index:7;
      box-shadow:0 3px 12px rgba(38,35,46,.16)}
    .flash.ok{background:#E1F5EE;border:1px solid var(--green);color:#0b6b4c}
    .flash.err{background:#FBE6E6;border:1px solid var(--red);color:#7a1f1f}
    .flash.warn{background:#FBF1E0;border:1px solid var(--amber);color:#8a5512}
    /* 💬 23/08 — TROIS CATÉGORIES ÉTAIENT PEINTES, LES AUTRES SORTAIENT NUES.
       Un `flash(…, "info")` — il en existe un, sur la carte grise — et tout appel
       sans catégorie (Flask lui donne alors « message ») s'affichaient sans fond,
       sans bordure, dans la couleur du texte courant : le retour de l'application
       devenait un paragraphe de plus au milieu de la page. Elles sont peintes ici,
       et le filet de sécurité vaut pour la prochaine catégorie inventée. */
    .flash.info,.flash.message{background:#EDEBF5;border:1px solid var(--navy2);
      color:#3B2F70}
    .cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:12px;margin-bottom:16px}
    .card{background:#fff;border:1px solid var(--line);border-radius:12px;padding:14px 16px;
      box-shadow:0 1px 3px rgba(0,0,0,.04)}
    .card .lbl{font-size:.75rem;text-transform:uppercase;letter-spacing:.4px;color:#6b6a64}
    .card .val{font-size:1.7rem;font-weight:700;color:var(--navy);margin-top:3px;line-height:1.1}
    .card .sub{font-size:.82rem;color:#6b6a64;margin-top:3px}
    /* 🎨 17/09 (revue UI/UX, lot B, n°4) — UNE TUILE QUI COMPTE UN TRAVAIL Y MÈNE. 50 tuiles
       de chiffres, aucune cliquable : « Stocks négatifs 4 — inventaire à faire ! » annonçait
       la tâche sans y conduire. Une tuile-lien garde exactement l'aspect d'une tuile ; seul
       le survol la distingue. Les tuiles d'information pure restent des <div>. */
    a.card{display:block;color:inherit;text-decoration:none;cursor:pointer}
    a.card:hover{border-color:var(--navy2);box-shadow:0 2px 8px rgba(0,0,0,.08)}
    /* 🔔 23/08 (audit UI, 3e passage) — L'ALARME ÉTAIT PLUS PÂLE QUE L'ORDINAIRE.
       Une carte en alerte ne se distinguait d'une carte normale QUE par la teinte
       de son chiffre — et cette teinte contraste 2,3 fois MOINS (4,55:1 contre
       10,29 pour le violet ordinaire). Même fond, même bordure : dans une rangée
       de quatre cartes, celle qui appelle un geste passait inaperçue.
       Elle prend donc le liseré des panneaux d'alerte — le motif est déjà en
       service ailleurs dans l'application — et un fond à peine teinté. */
    .card.good .val{color:var(--green)} .card.bad .val{color:var(--red)} .card.warn .val{color:var(--amber)}
    /* 🏠 22/09 — accueils par rôle : la tendance d'une tuile et la grille des raccourcis */
    .tuile-tend{font-size:.82rem;margin-top:3px}
    .tuile-tend.bon{color:var(--green)} .tuile-tend.mauvais{color:var(--red)}
    .racc{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}
    @media (min-width:700px){.racc{grid-template-columns:repeat(6,1fr)}}
    .racc-item{background:#fff;border:1px solid var(--line);border-radius:10px;padding:10px 6px;text-align:center;
      font-size:.82rem;text-decoration:none;color:var(--navy);font-weight:600}
    .racc-item span{display:block;font-size:1.3rem;margin-bottom:2px}
    .card.bad{border-left:5px solid var(--red);background:#FDF6F6}
    .card.warn{border-left:5px solid var(--amber);background:#FDF9F1}
    .card.good{border-left:5px solid var(--green)}
    .panel{background:#fff;border:1px solid var(--line);border-radius:12px;padding:16px;margin-bottom:16px}
    .panel h2{margin:0 0 12px;font-size:1.05rem;color:var(--navy)}
    table{width:100%;border-collapse:collapse;font-size:.9rem}
    th,td{padding:8px 10px;text-align:left;border-bottom:1px solid var(--line)}
    th{background:var(--navy);color:#fff;font-weight:600;font-size:.82rem}
    td.num,th.num{text-align:right;font-variant-numeric:tabular-nums}
    tr:nth-child(even) td{background:var(--grey)}
    /* 🦓 23/08 — QUATRE TABLEAUX AVAIENT PERDU LEURS LIGNES ALTERNÉES. Quand une
       ligne de détail repliée (.detrow, .motif-row) se glisse APRÈS chaque ligne,
       toutes les lignes visibles deviennent impaires et la règle ci-dessus ne tombe
       plus jamais sur l'une d'elles : 9 colonnes de saisie à suivre à l'œil nu, sans
       repère. Le CSS ne sait pas compter en sautant des lignes ; la boucle du
       gabarit, oui — elle pose `alt` une fois sur deux. */
    tr.alt>td{background:var(--grey)}
    .bar{height:9px;background:#E7E5DD;border-radius:5px;overflow:hidden;min-width:70px}
    .bar > i{display:block;height:100%;background:var(--blue)}
    label{display:block;font-size:.9rem;font-weight:600;color:#444;margin:10px 0 4px}
    input,select,textarea{width:100%;padding:11px;border:1px solid var(--line);border-radius:9px;
      font-size:1rem;font-family:inherit;background:#fff}
    /* ⌨️ 23/08 — L'ANNEAU DE FOCUS ÉTAIT LE MOINS VISIBLE DE L'ÉCRAN. Orange sur
       blanc : 2,66:1, quand un anneau de focus demande 3:1 contre ce qui l'entoure.
       Celui qui remplit une grille de pointage au clavier ne voyait pas où il en
       était. Le violet donne 10,3:1, et l'écart d'un pixel le détache du champ.
       Il vaut désormais pour TOUT ce qui se focalise — un bouton et un lien n'en
       déclaraient aucun et s'en remettaient au navigateur. */
    input:focus,select:focus,textarea:focus{outline:2px solid var(--navy);border-color:var(--navy)}
    /* 🔒 23/08 — UN CHAMP FERMÉ AVAIT EXACTEMENT L'AIR D'UN CHAMP OUVERT : même
       fond blanc, même bordure, même opacité. La règle du navigateur qui grise les
       champs désactivés était écrasée par notre `background:#fff` et rien ne la
       remplaçait. On tape dans un champ de la fiche de parc, du bon reçu ou d'une
       journée figée, et rien ne s'écrit — sans savoir pourquoi. */
    input:disabled,select:disabled,textarea:disabled,
    input[readonly],textarea[readonly]{background:#EFEEF3;color:#57545F;
      border-color:#E3E0EA;cursor:not-allowed}
    /* 🔘 04/09 — ET LES BOUTONS AUSSI. `disabled` bloquait le clic sans que rien ne se voie :
       un bouton plein, coloré, au curseur en forme de main, qui ne répond pas, se lit comme une
       panne. Il doit ANNONCER qu'il est hors service. */
    button:disabled,.btn:disabled,button[disabled],.btn[disabled]{opacity:.45;
      filter:grayscale(.7);cursor:not-allowed;box-shadow:none}
    button:disabled:hover,.btn:disabled:hover,button[disabled]:hover,.btn[disabled]:hover{
      transform:none;filter:grayscale(.7)}
    /* l'anneau suit la forme de l'élément — surtout PAS de border-radius ici, qui
       carrerait le 💬 et les chevrons ronds le temps du focus. */
    a:focus-visible,button:focus-visible,summary:focus-visible,
    [tabindex]:focus-visible{outline:2px solid var(--navy);outline-offset:1px}
    header a:focus-visible,header button:focus-visible,header summary:focus-visible,
    th a:focus-visible{outline-color:#fff}
    .btn{display:inline-block;background:var(--green);color:#fff;border:none;padding:13px 20px;
      border-radius:10px;font-size:1rem;font-weight:600;cursor:pointer;width:100%;
      text-decoration:none}
    a.pill{text-decoration:none}
    .btn:hover{filter:brightness(.95)}
    /* 🎨 23/08 — LE ROUGE REVIENT À CE QUI DÉTRUIT. `.btn.sm` ne disait que la
       TAILLE, mais peignait en rouge : un petit bouton qui n'avait rien déclaré
       était donc rouge par accident. Mesuré au navigateur sur 23 écrans : 309
       éléments rouges, dont 153 qui ne détruisaient rien — 150 d'entre eux sur le
       seul écran Fournisseurs, où « 🪪 Ouvrir la fiche » criait 150 fois à l'alarme.
       Un rouge qui sert à tout ne sert plus à rien : quand un vrai 🗑 paraît, l'œil
       ne le distingue plus. Les 67 boutons qui n'avaient pas d'intention en ont une
       maintenant (50 danger, les autres agir/ouvrir/neutre/alerte/ok), et le défaut
       devient NEUTRE : écrire du rouge demande désormais de dire `danger`.
       Un test tient le compte à zéro (mode d'échec : une règle juste sans cliquet
       se redémolit). */
    .btn.sm{width:auto;padding:6px 12px;font-size:.82rem}
    .btn.sm:not(.agir):not(.ouvrir):not(.ok):not(.danger):not(.alerte):not(.accent):not(.neutre){
      background:var(--grey);color:var(--ink);border:1px solid var(--line)}
    .row2{display:grid;grid-template-columns:1fr 1fr;gap:12px}
    /* 🎨 09/08 (arbitrage Saïd) — LE VOCABULAIRE DES ÉLÉMENTS CLIQUABLES.
       Motif : « ça n'a pas l'air cliquable », « deux boutons, tu n'as pas trouvé mieux ».
       312 cliquables sur 559 inventaient leur apparence au coup par coup, faute de forme
       officielle pour dire « ceci agit », « ceci détruit », « ceci mène au travail
       annoncé ». Deux causes premières, toutes deux corrigées ici : `.btn.sm` est ROUGE
       (donc tout petit bouton inoffensif devait repeindre son fond — 118 cas) et `.btn`
       ne déclarait pas `text-decoration:none` (donc 153 endroits le répétaient).
       Règle : l'INTENTION s'écrit en classe, la GÉOMÉTRIE peut rester en style.
       Le catalogue vit sur /composants et un test tient le compte. */
    .btn.agir{background:var(--navy)}          /* action principale : valider, créer, enregistrer */
    .btn.ouvrir{background:var(--navy2)}       /* mène vers un écran : ouvrir, consulter, imprimer */
    .btn.ok{background:var(--green)}           /* on constate, on confirme : recevoir, servir, clôturer */
    .btn.danger{background:var(--red)}         /* DÉTRUIT ou annule — jamais autre chose */
    .btn.alerte{background:var(--amber)}       /* traite un retard ou un manque */
    .btn.neutre{background:var(--grey);color:var(--ink);border:1px solid var(--line)}
    /* 09/08, 2e passe — trois manques révélés en convertissant les 71 dernières :
       · `accent` = la couleur de marque, pour ce qui appelle l'œil sans être une
         action métier (activer les notifications) ET pour le filtre ACTIF d'une barre
         de filtres — 16 barres l'écrivaient en condition Jinja dans un style inline ;
       · `a.nu` = un lien qui renonce volontairement à son soulignement (une carte
         entière cliquable, un logo) — 29 endroits le redéclaraient à la main ;
       · une PASTILLE peut être un lien quand elle est compacte et porte une
         destination : c'est alors une balise A portant la classe pill — jamais une
         balise SPAN qui aurait l'air cliquable sans l'être.
       (Écrire ici une balise en toutes lettres tromperait le compteur de /composants,
        qui lit les gabarits au motif « balise cliquable ».) */
    .btn.accent{background:var(--blue);color:var(--ink)}
    .btn.sm.accent{background:var(--blue);color:var(--ink)}
    /* le BOUTON-ICÔNE : une icône seule qui agit (🗑 dans une ligne de tableau). Neuf
       gabarits le redessinaient à l'identique en style inline — même déshabillage du
       bouton natif, même rouge. Il a désormais un nom, donc une seule définition. */
    button.icone{background:none;border:none;padding:0;cursor:pointer;font-size:1rem;
      color:var(--navy)}
    button.icone.danger{background:none;color:var(--red)}
    button.icone:hover{filter:brightness(1.2)}
    a.fort{font-weight:700}   /* un lien qu'on veut voir en premier dans une carte */
    /* 🔽 10/08 (Saïd) : « la flèche qui montre que la ligne peut s'ouvrir est trop
       petite ». Elle faisait 0,72 rem en gris atténué, sur une ligne de tableau où rien
       d'autre ne dit qu'on peut cliquer. Les `<details>` avaient déjà un chevron correct
       (0,95 rem, navy, qui pivote) ; les LIGNES DE TABLEAU n'avaient rien d'équivalent.
       Voici la forme officielle : même taille, même couleur, même rotation — et une
       cible tactile d'au moins 24 px, parce qu'on la vise avec un pouce. */
    /* 🔽 10/08, 3e passage : l'agrandir ne suffisait pas — un glyphe fin reste discret
       quel que soit son corps. Il devient donc un vrai BOUTON : pastille ronde, fond
       gris, bordure. On ne se demande plus si ça se clique. C'est la « autre solution »
       que Saïd proposait dès sa première remarque. */
    .chev{display:inline-flex;align-items:center;justify-content:center;
      width:24px;height:24px;border-radius:50%;background:var(--grey);
      border:1px solid var(--line);color:var(--navy);font-size:.82rem;line-height:1;
      vertical-align:middle;margin-left:4px;transition:transform .15s ease}
    tr.voirdet:hover .chev{background:var(--navy);color:#fff;border-color:var(--navy)}
    tr.ouv .chev,.ouv>.chev{transform:rotate(180deg)}
    tr.voirdet{cursor:pointer}
    /* le survol d'une ligne cliquable prenait EXACTEMENT la couleur de la zébrure :
       une ligne sur deux ne réagissait donc pas au passage de la souris. Il lui
       faut sa propre teinte, et elle doit peindre les cellules — c'est là que la
       zébrure est écrite. */
    tr.voirdet:hover>td{background:#E7E3F0}
    a.nu{text-decoration:none}
    /* 🎯 23/08 (audit UI, 3e passage) — QUINZE PASTILLES MÈNENT QUELQUE PART, 256
       ne font que dire un état, et RIEN NE LES DISTINGUAIT À L'ŒIL. Sur la ligne
       d'un bon de commande, « ✅ soldé » (un fait) et « ✅ validé » (qui ouvre
       l'écran de validation) sont collées à 6 px, mêmes forme, taille, graisse et
       couleur. Le vocabulaire les sépare pourtant depuis le 09/08 — une balise A
       pour ce qui mène, une SPAN pour ce qui constate — mais la seule marque était
       le curseur et un survol : rien au doigt, rien sur un téléphone.
       Un chevron le dit, en huit pixels, sur tous les fonds. */
    a.pill{cursor:pointer} a.pill:hover{filter:brightness(.95)}
    /* le chevron s'écrit EN CLAIR : passé par un échappement CSS, « 9 » a été
       relu comme un échappement OCTAL et les 85 pastilles affichaient « 9A ». */
    a.pill::after{content:' ›';font-weight:700;opacity:.75}
    /* `.btn.sm` ne dit que la TAILLE ; sa couleur rouge historique reste tant qu'un
       bouton n'a pas déclaré son intention — l'écrire la lève. */
    .btn.sm.agir{background:var(--navy)} .btn.sm.ouvrir{background:var(--navy2)}
    .btn.sm.ok{background:var(--green)} .btn.sm.danger{background:var(--red)}
    .btn.sm.alerte{background:var(--amber)}
    .btn.sm.neutre{background:var(--grey);color:var(--ink)}
    /* UN CHIFFRE QUI ANNONCE DU TRAVAIL DOIT Y MENER, et en avoir l'air AVANT qu'on
       l'essaie : c'est le défaut « N fiche(s) à classer », du texte orange non cliquable. */
    a.action{display:inline-block;text-decoration:none;background:var(--navy);color:#fff;
      font-weight:600;padding:6px 12px;border-radius:8px;font-size:.82rem}
    a.action.alerte{background:var(--amber)} a.action.ok{background:var(--green)}
    a.action:hover{filter:brightness(.95)}
    a.lien{color:var(--navy);font-weight:600}   /* lien de TEXTE, dans une phrase */
    .pill{display:inline-block;padding:2px 9px;border-radius:20px;font-size:.75rem;font-weight:600}
    /* les pastilles disent un ÉTAT ; une pastille cliquable ment */
    .pill.ok{background:var(--green);color:#fff} .pill.err{background:var(--red);color:#fff}
    .pill.warn{background:var(--amber);color:#fff} .pill.info{background:var(--navy);color:#fff}
    .pill.neutre{background:var(--grey);color:var(--ink);border:1px solid var(--line)}
    .muted{color:#6b6a64;font-size:.9rem}
    .mobile-only{display:none}     /* pendant de .hide-mobile (voir @media plus haut) */
    /* 📱 23/08 — CE BLOC A DÛ DESCENDRE ICI. Il était écrit AVANT les règles de
       base ; à spécificité égale, c'est la dernière écrite qui gagne, donc quatre
       de ses règles matchaient sans jamais s'appliquer. La plus coûteuse :
       `main{padding-bottom:76px}`, dont le commentaire explique qu'elle dégage le
       bouton flottant 💬 — il recouvrait donc le dernier bouton de chaque page sur
       téléphone, « Enregistrer » compris. Perdaient aussi : la taille tactile des
       petits boutons, les cartes en 2 colonnes et leur padding resserré. */
    @media(max-width:640px){
      .hide-mobile{display:none !important}
      /* Graphiques « 14 derniers jours » (bords RH et logistique) : sans min-width:0,
         les 14 colonnes flex ne peuvent pas descendre sous la largeur de leur libellé
         (« 11/07 ») et poussaient la PAGE à 416px sur un écran de 390 — audit visuel
         du 24/07. Les colonnes rétrécissent, et le libellé garde le jour seul. */
      .barjour{min-width:0}
      .mobile-only{display:inline !important}
      table th,table td{padding:8px 7px}
      /* padding-bottom : dégage le bouton flottant 💬 (bas-gauche, fixe) pour qu'il ne
         recouvre JAMAIS le dernier bouton/CTA d'une page (ex. « Enregistrer ») */
      main{padding:10px;padding-bottom:76px}
      nav a{padding:10px 13px;font-size:1rem}          /* cibles tactiles confortables */
      details.navgrp>summary{padding:10px 13px;font-size:1rem}
      .btn.sm{padding:9px 13px;font-size:.9rem}
      .cards{grid-template-columns:repeat(2,1fr);gap:9px}
      .card{padding:11px 12px}
      /* ZOOM AUTO iOS : sous 16px, Safari agrandit la page dès qu'on touche un champ —
         la mise en page saute et on perd la ligne qu'on remplissait. La règle existait
         mais les styles POSÉS À LA MAIN sur les champs (font-size:.82rem dans les
         tableaux de saisie) la battaient : audit UI du 25/07, 15 champs concernés sur
         parc, matériel, achats, effectif et annuaire. */
      input,select,textarea{font-size:16px !important}
      /* CIBLES TACTILES : un bouton de 29px se rate au pouce. Les boutons des tableaux
         portaient un padding de 5-6px en dur ; on impose une hauteur minimale sans
         toucher à leur largeur. */
      .btn,button,a.btn{min-height:34px}
      table .btn.sm,table button{padding-top:7px;padding-bottom:7px}
      /* sélecteurs « chantier » à width:auto (plusieurs pages, ajoutés à la main) :
         un nom de chantier long fait déborder toute la page en largeur sur mobile.
         Règle GLOBALE : n'affecte pas les select déjà contraints par leur grille/
         conteneur (déjà < 280px), corrige uniquement ceux qui débordaient. */
      select{max-width:min(280px,60vw)}
      /* lignes multi-champs (BC achats, bon carburant, pièces maintenance, BL interne) :
         colonnes fixes/3+ trop larges pour 390px -> grille 2 col. qui s'empile,
         plus de débordement horizontal de toute la page. min-width:0 sur les cellules :
         sans ça, une cellule de grille ne rétrécit jamais sous la taille intrinsèque
         de son <input> (piège CSS Grid classique), même avec width:100% sur l'input */
      .bcl,.pl,.blil,.ligne-bon{grid-template-columns:1fr 1fr !important}
      .bcl>div,.pl>div,.blil>div,.ligne-bon>div{min-width:0}
    }

    @media(max-width:560px){.row2{grid-template-columns:1fr}.card .val{font-size:1.45rem}}

    /* ══════════════════════════════════════════════════════════════════════════════
       🌙 07/09 — LE THÈME SOMBRE, EN COUCHE QUI S'AJOUTE (Saïd : « on ne remplace
       rien, mais il faut avoir un choix comme sur beaucoup de sites »).

       Pas une seule règle existante n'est réécrite : tout ce qui suit s'ajoute sous
       `:root[data-theme="sombre"]` et ne s'applique que si le lecteur l'a choisi. Le
       clair reste le clair, au pixel près.

       CE QUI A DÉCIDÉ DE LA MÉTHODE, mesuré avant d'écrire : 457 couleurs sont
       écrites en dur dans les gabarits, dont 283 dans des attributs `style=` que
       nulle feuille de style ne peut atteindre par une classe. Mais **222 de ces 283
       sont `color:#fff`** — du texte blanc sur une pastille colorée, qui se lit
       aussi bien sur fond sombre. Ne restent qu'une trentaine de fonds clairs
       inline : ils sont repris un par un, plus bas, par sélecteur d'attribut.
       L'identité SETRAT ne bouge pas : le violet reste le violet, l'orange du logo
       reste l'orange. On change les SURFACES, pas la marque.
       ══════════════════════════════════════════════════════════════════════════════ */
    :root[data-theme="sombre"]{
      /* ⚠️ le token s'appelle `--papier` et NON `--fond` : deux gabarits (le livre de
         paie et le livre RH) définissent déjà `--fond` sur leurs lignes de tableau. Un
         token de socle qui porte le même nom qu'un token d'écran finit par déteindre
         sur lui, et le jour où ça arrive personne ne cherche là. */
      --papier:#14131A;
      --carte:#1D1B25;     /* cartes, panneaux, champs */
      --ink:#E7E4EF;       /* l'encre */
      --line:#39354A;      /* les traits */
      --grey:#22202B;
      --muted:#A39EB5;     /* le texte secondaire — 5,9:1 sur le papier */
      /* `--navy2` NE BOUGE PAS : il est FOND de pastilles en texte blanc. L'éclaircir
         pour le rendre lisible comme texte cassait ces pastilles (2,7:1) — les deux
         emplois demandent l'inverse l'un de l'autre. C'est `--navy-txt` qui écrit. */
      --blue-txt:#F0A45E;  /* l'orange qui ÉCRIT : éclairci pour tenir 7,6:1 sur le
                              papier sombre, là où le #B56014 du clair tombe à 3,1:1 */
    }
    :root[data-theme="sombre"] body{background:var(--papier);color:var(--ink)}
    :root[data-theme="sombre"] .card,
    :root[data-theme="sombre"] .panel{background:var(--carte);border-color:var(--line)}
    :root[data-theme="sombre"] .card.bad{background:#2B1B1E}
    :root[data-theme="sombre"] .card.warn{background:#2B2318}
    :root[data-theme="sombre"] .card .lbl,
    :root[data-theme="sombre"] .card .sub,
    :root[data-theme="sombre"] .compte,
    :root[data-theme="sombre"] .muted{color:var(--muted)}
    :root[data-theme="sombre"] label{color:#CBC6D9}
    :root[data-theme="sombre"] input,
    :root[data-theme="sombre"] select,
    :root[data-theme="sombre"] textarea{background:#26232E;color:var(--ink);border-color:var(--line)}
    :root[data-theme="sombre"] input:disabled,
    :root[data-theme="sombre"] select:disabled,
    :root[data-theme="sombre"] textarea:disabled{background:#211F29;color:#918CA3;border-color:#302C3C}
    /* les bandeaux gardent leur sens : vert, rouge, ambre, violet — assombris, jamais changés de teinte */
    :root[data-theme="sombre"] .flash.ok{background:#12332A;color:#8FE3C4}
    :root[data-theme="sombre"] .flash.err{background:#3A1B1B;color:#FFB4B4}
    :root[data-theme="sombre"] .flash.warn{background:#382B12;color:#F2CB8C}
    :root[data-theme="sombre"] .flash.info,
    :root[data-theme="sombre"] .flash.message{background:#231F3C;color:#C9C0F7}
    :root[data-theme="sombre"] .bar{background:#2F2C39}
    :root[data-theme="sombre"] .smodbar a{background:var(--carte);border-color:var(--line);color:var(--ink)}
    :root[data-theme="sombre"] .smodbar a.on .smodbadge{background:var(--ink);color:var(--navy)}
    :root[data-theme="sombre"] .compte .filtre,
    :root[data-theme="sombre"] .swipehint{background:#382B12;color:#F2CB8C}
    :root[data-theme="sombre"] tr.voirdet:hover>td{background:#2A2637}
    :root[data-theme="sombre"] .apercu-sel option{color:var(--ink);background:var(--carte)}
    /* LES FONDS CLAIRS ÉCRITS DANS UN `style=` — ceux qu'aucune classe n'atteint.
       Ils sont peu nombreux (mesurés un par un) et se reprennent ici sans toucher
       aux gabarits. `!important` est ici légitime : un style inline ne cède à rien
       d'autre. Le `color:#fff` inline, lui, n'est PAS repris — il est sur une
       pastille de couleur, et il s'y lit aussi bien. */
    /* … et l'ENCRE avec : repeindre un fond blanc en sombre sans toucher au texte laisse
       du noir sur du noir. Mesuré sur un bouton construit en JavaScript : 1,24:1. */
    :root[data-theme="sombre"] [style*="background:#fff"],
    :root[data-theme="sombre"] [style*="background:#FFF"],
    :root[data-theme="sombre"] [style*="background:#F7F5FB"]{background:var(--carte) !important;
      color:var(--ink)}
    :root[data-theme="sombre"] [style*="background:#FBF1E0"],
    :root[data-theme="sombre"] [style*="background:#FBF6E9"],
    :root[data-theme="sombre"] [style*="background:#FEF9E7"],
    :root[data-theme="sombre"] [style*="background:#FFF3DF"],
    :root[data-theme="sombre"] [style*="background:#FFF4DC"]{background:#382B12 !important}
    /* ⚠️ CE QUE LE SÉLECTEUR D'ATTRIBUT NE PEUT PAS ATTRAPER, et il fallait le trouver
       au geste : dès que du JavaScript touche au style d'un élément (ne serait-ce que
       `el.style.display = "flex"`), le navigateur RÉÉCRIT tout l'attribut sous forme
       normalisée — `#FFF4DC` devient `rgb(255, 244, 220)`, et le sélecteur ne matche
       plus. Mesuré sur le bandeau des notifications : fond clair, encre presque
       blanche, **1,15:1**. On vise donc ces éléments-là par leur IDENTIFIANT, et on
       ajoute la forme rgb() en filet pour ceux qu'on n'aurait pas vus. */
    :root[data-theme="sombre"] #notif-invite{background:#382B12}
    :root[data-theme="sombre"] #notif-invite,
    :root[data-theme="sombre"] #notif-invite .icone{color:#F2CB8C}
    :root[data-theme="sombre"] [style*="rgb(255, 244, 220)"],
    :root[data-theme="sombre"] [style*="rgb(251, 241, 224)"]{background:#382B12 !important}
    :root[data-theme="sombre"] [style*="rgb(255, 255, 255)"]{background:var(--carte) !important;
      color:var(--ink)}
    :root[data-theme="sombre"] [style*="background:#FDECEA"],
    :root[data-theme="sombre"] [style*="background:#FDF6F5"],
    :root[data-theme="sombre"] [style*="background:#FBE9E7"],
    :root[data-theme="sombre"] [style*="background:#FBE6E6"]{background:#3A1B1B !important}
    :root[data-theme="sombre"] [style*="background:#E1F5EE"],
    :root[data-theme="sombre"] [style*="background:#E7F5EF"]{background:#12332A !important}
    :root[data-theme="sombre"] [style*="background:#EFEDF6"],
    :root[data-theme="sombre"] [style*="background:#EDF3FF"],
    :root[data-theme="sombre"] [style*="background:#F3F7FF"],
    :root[data-theme="sombre"] [style*="background:#EDEBF5"],
    :root[data-theme="sombre"] [style*="background:#F2F0F7"]{background:#231F3C !important}
    /* ⚠️ CE QUE LA MESURE DE CONTRASTE A SORTI, une fois le sombre posé — trois
       défauts qu'aucune relecture de code n'aurait montrés :

       ① LES LIENS SANS CLASSE gardaient le bleu du navigateur (#0000EE) : 1,8:1 sur
       fond sombre, illisible. On le corrige à specificité NULLE (`:where`) — la règle
       bat la feuille du navigateur et s'efface devant la moindre règle de l'app, donc
       aucun bouton, aucune pastille n'est touché.
       ② LE VIOLET DE MARQUE ÉCRIT EN TEXTE (titres de panneau, gros chiffres des
       cartes) tombait à 1,65:1. Le violet PEINT ne bouge pas — c'est l'identité ; le
       violet ÉCRIT s'éclaircit, même teinte, jusqu'à 7,6:1.
       ③ L'ORANGE DU LOGO porte du texte ENCRE depuis le 23/08 (5,79:1 en clair). En
       sombre, l'encre devenue presque blanche y retombait à 2,12:1 : sur l'orange, on
       réécrit l'encre SOMBRE — la surface de marque garde son texte lisible. */
    :where(:root[data-theme="sombre"]) :where(a){color:#9BB8FF}
    :root[data-theme="sombre"]{--navy-txt:#A99BEE}
    :root[data-theme="sombre"] .panel h2,
    :root[data-theme="sombre"] .card .val,
    :root[data-theme="sombre"] .compte b,
    :root[data-theme="sombre"] .smodbar a{color:var(--navy-txt)}
    /* 🏠 22/09 — les raccourcis des accueils par rôle sont des cartes comme les autres :
       `.racc-item` porte un fond blanc et une encre navy en dur, que la couche sombre ne
       reprenait pas (la règle générique `[style*="background:#fff"]` ne vise QUE les styles
       inline). Six pavés blancs restaient au milieu d'une page sombre. */
    :root[data-theme="sombre"] .racc-item{background:var(--carte);border-color:var(--line);
      color:var(--navy-txt)}
    /* 🎨 17/09 (revue UI/UX, n°1) — EN SOMBRE, L'ONGLET OUVERT NE SE VOYAIT PLUS : les deux règles ci-dessus
       (plus spécifiques) écrasaient `.smodbar a.on`. Même marquage qu'en clair : fond navy, texte blanc. */
    :root[data-theme="sombre"] .smodbar a.on{background:var(--navy);color:#fff;border-color:var(--navy)}
    :root[data-theme="sombre"] nav a.on,
    :root[data-theme="sombre"] details.navgrp[open]>summary,
    :root[data-theme="sombre"] details.navgrp>summary.on,
    :root[data-theme="sombre"] .btn.accent,
    :root[data-theme="sombre"] .btn.sm.accent{color:#26232E}
    :root[data-theme="sombre"] [style*="background:var(--blue)"]{color:#26232E !important}
    :root[data-theme="sombre"] [style*="color:var(--navy2)"]{color:var(--navy-txt) !important}

    /* LES ÉCRANS QUI ONT LEURS PROPRES COULEURS. Mesuré : douze gabarits seulement en
       portent, hors `base.html` — 85 couleurs en tout. Leur `<style>` vient APRÈS
       celui-ci dans la page, donc on les reprend avec la spécificité qu'il faut, ici,
       sans toucher à un seul de ces fichiers. Les états de la grille de pointage
       gardent leur SENS : vert = présent, ambre = demi-journée, rouge = absent, bleu
       = congé. On change la lumière, pas le code couleur que l'équipe a appris. */
    :root[data-theme="sombre"]{--panel:var(--carte)}
    :root[data-theme="sombre"] #ptable tr.sans-reponse{background:#221F2C}
    :root[data-theme="sombre"] .seg{background:var(--carte)}
    /* le gris secondaire est fait pour le papier sombre (5,9:1) ; posé sur le VIOLET de
       l'en-tête de colonne il retombe à 3,98:1 — là, il s'éclaircit. */
    /* le gris secondaire est fait pour le PAPIER sombre (5,9:1) ; posé sur une surface
       de MARQUE — en-tête de colonne, bouton ouvert — il retombe sous 4:1. */
    :root[data-theme="sombre"] th .muted,
    :root[data-theme="sombre"] #gp th .muted,
    :root[data-theme="sombre"] .btn .muted,
    :root[data-theme="sombre"] .pill .muted,
    :root[data-theme="sombre"] a.action .muted{color:#E4E0EE}
    :root[data-theme="sombre"] #gp th.j.periode{background:#2A3450;color:#C8D6F5}
    :root[data-theme="sombre"] .cel[data-v="j"]{background:#12332A;color:#8FE3C4;border-color:#2C6A51}
    :root[data-theme="sombre"] .cel[data-v="d"]{background:#382B12;color:#F2CB8C;border-color:#6E5624}
    :root[data-theme="sombre"] .cel[data-v="a"]{background:#3A1B1B;color:#FFB4B4;border-color:#6E3232}
    :root[data-theme="sombre"] .cel.fige{background:#232B3E;color:#B7C6E6;border-color:#3A4763}
    :root[data-theme="sombre"] .mo-vu{background:#3A1B1B;color:#FFB4B4;border-color:#6E3232}
    /* … et les huit autres écrans qui ont leurs propres couleurs. Mesuré : 85 couleurs
       en dur hors `base.html`, dans douze gabarits. Chacune est reprise ici, avec la
       spécificité qu'il faut, sans toucher à un seul de ces fichiers. */
    /* 📊 estimatif */
    :root[data-theme="sombre"] .estim .k,
    :root[data-theme="sombre"] .estim .wrap{background:var(--carte);border-color:var(--line)}
    :root[data-theme="sombre"] .estim .k b i,
    :root[data-theme="sombre"] .estim td.z,
    :root[data-theme="sombre"] .estim .barwrap em{color:var(--muted)}
    :root[data-theme="sombre"] table.dt td{border-bottom-color:var(--line)}
    :root[data-theme="sombre"] table.dt tbody tr:not(.lot):not(.grand):nth-child(even){background:#22202B}
    :root[data-theme="sombre"] table.dt tr.lot td{background:#262238;color:var(--navy-txt);border-top-color:var(--line)}
    :root[data-theme="sombre"] tr.lot-AV1 td,
    :root[data-theme="sombre"] tr.lot-AV2 td{background:#12332A}
    :root[data-theme="sombre"] .estim .bar{background:#2F2C39}
    /* 🚜 pointage matériel */
    :root[data-theme="sombre"] #mtable input.htrav{background:#26232E;color:var(--ink)}
    :root[data-theme="sombre"] #mtable .chk{color:var(--muted)}
    :root[data-theme="sombre"] tr.panne-on td,
    :root[data-theme="sombre"] tr.motif-row td{background:#3A1B1B !important}
    :root[data-theme="sombre"] tr.arret-on td{background:#262430 !important}
    :root[data-theme="sombre"] #mtable input.inval{background:#3A1B1B !important}
    :root[data-theme="sombre"] #mtable tr:not(.motif-row) td:first-child{background:var(--carte)}
    /* 📗 livres de paie — la ligne survolée et le total du net */
    :root[data-theme="sombre"] #t-livre tbody tr:hover{--fond:#382B12}
    :root[data-theme="sombre"] #t-livre tbody tr:hover td{background:#382B12}
    :root[data-theme="sombre"] #t-livre tfoot td.col-net{background:#4A3418;color:#F2CB8C}
    :root[data-theme="sombre"] #t-rh tr.detrow>td{background:#26232E}
    /* le violet ÉCRIT, partout où un écran le pose lui-même : le chevron des lignes
       dépliables et les en-têtes des tableaux imbriqués tombaient à 1,5:1. */
    :root[data-theme="sombre"] .chev,
    :root[data-theme="sombre"] #t-rh tr.detrow table th{color:var(--navy-txt)}
    /* `.lien` porte le violet de marque : lisible sur du blanc, éteint sur du sombre
       (1,56:1 sur les numéros de compte, qui viennent de devenir des liens). */
    :root[data-theme="sombre"] .lien{color:#9BB8FF}
    /* `!important` parce qu'un style INLINE ne cède à rien d'autre — l'écran « Accès
       refusé », qui est l'outil de contrôle de Saïd, y tombait à 1,65:1. */
    :root[data-theme="sombre"] [style*="color:var(--navy)"]{color:var(--navy-txt) !important}
    /* 📅 récap de pointage — les mêmes couleurs d'état que la grille */
    :root[data-theme="sombre"] .jp-j{background:#12332A;color:#8FE3C4}
    :root[data-theme="sombre"] .jp-d{background:#382B12;color:#F2CB8C}
    :root[data-theme="sombre"] .jp-a{background:#3A1B1B;color:#FFB4B4}
    /* 📆 planning (gantt) */
    :root[data-theme="sombre"] .gantt .g-track{background:#26232E}
    :root[data-theme="sombre"] .gantt .g-mois span{color:var(--muted)}
    /* les COLONNES FIGÉES des grands tableaux : elles portent un fond opaque pour
       masquer ce qui glisse dessous — blanc en clair, carte en sombre. */
    :root[data-theme="sombre"] .bcl td:first-child,
    :root[data-theme="sombre"] .bcl th:first-child,
    :root[data-theme="sombre"] .tmois td:first-child,
    :root[data-theme="sombre"] .tmois th:first-child,
    :root[data-theme="sombre"] .panel th:first-child,
    :root[data-theme="sombre"] #tb-papiers td:first-child,
    :root[data-theme="sombre"] #tb-papiers th:first-child{background:var(--carte)}
    /* la barre de modules porte un DÉGRADÉ (l'ombre qui dit « ça défile ») : il faut le
       reprendre en entier, un simple `background-color` passerait dessous. */
    :root[data-theme="sombre"] .smodbar{background:#26232E}
    :root[data-theme="sombre"] [style*="background:#FBFAF5"],
    :root[data-theme="sombre"] [style*="background:#FBFAFF"]{background:var(--carte) !important}
    /* le ROUGE ÉCRIT : comme le violet, il peint très bien mais il n'écrit pas sur du
       sombre — 2,2:1 sur les compteurs de l'accueil. Même teinte, éclaircie. */
    /* LES TROIS COULEURS DE MARQUE ONT CHACUNE DEUX MÉTIERS : elles PEIGNENT une
       pastille (texte blanc dessus, elles ne bougent pas) et elles ÉCRIVENT un chiffre
       (là, sur du sombre, elles s'éteignent). Mesuré : le violet à 1,65:1, le rouge à
       2,2:1, l'ambre à 3,41:1. Même teinte, éclaircie — la marque tient, le texte se lit. */
    :root[data-theme="sombre"]{--red-txt:#F08A8A;--amber-txt:#E7A94A;--green-txt:#5FCFA3}
    :root[data-theme="sombre"] [style*="color:var(--red)"]{color:var(--red-txt) !important}
    :root[data-theme="sombre"] [style*="color:var(--amber)"]{color:var(--amber-txt) !important}
    :root[data-theme="sombre"] [style*="color:var(--green)"]{color:var(--green-txt) !important}

    /* le sélecteur de thème, dans l'en-tête */
    .themebtn{background:rgba(255,255,255,.14);color:#fff;border:none;border-radius:7px;
      padding:6px 9px;font-size:.9rem;cursor:pointer;line-height:1}
    .themebtn:hover{background:rgba(255,255,255,.24)}

/* ⏱ 19/09 — LA GRILLE D'ENTRETIEN (fiche engin) : jusqu'à onze colonnes (huit paliers). Elle défile
   déjà horizontalement ; sur téléphone, sans repère, on ne sait plus QUELLE pièce on lit. La première
   colonne reste donc à l'écran. Le fond vient de la palette, pas d'une couleur écrite ici. */
.grille-entretien th:first-child,.grille-entretien td:first-child{position:sticky;left:0;background:var(--panel);z-index:1}
/* …et la colonne qui DÉCIDE (au magasin / à commander) reste à droite : sur un téléphone, les huit
   colonnes de paliers la poussaient hors écran, or c'est elle qu'on vient lire (relecture 19/09). */
.grille-entretien th:last-child,.grille-entretien td:last-child{position:sticky;right:0;background:var(--panel);z-index:1}
.grille-entretien td,.grille-entretien th{white-space:nowrap}
