/* of-commun.css — règles transversales de l'application, ÉCRITES À LA MAIN.
 *
 * Pourquoi ce fichier : les deux autres feuilles globales sont ENGENDRÉES — of-app.css par
 * tools/purge_css.php, of-icones.css par tools/subset_icones.js — et toute retouche y serait
 * effacée à la régénération suivante. Faute d'endroit où poser une correction transversale,
 * chaque module recopiait la sienne dans son propre CSS ; c'est ainsi que le même remède s'est
 * retrouvé dupliqué dans app/contacts, app/finance et app/temps, avec des valeurs qui ont
 * divergé. Les corrections qui valent pour TOUTE l'application vont désormais ici.
 *
 * Chargé après of-app.css (cf. include/utils/head.inc.php) : les règles ci-dessous priment donc
 * sur le thème sans avoir besoin de !important.
 */

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   Select2 — bouton « × » (allowClear) chevauchant la flèche d'ouverture
   ═══════════════════════════════════════════════════════════════════════════════════════════
   Select2 fait FLOTTER la croix (float:right, margin-right:20px, height:26px), valeurs prévues
   pour son habillage d'origine haut de 28 px. Le thème redimensionne le champ à ~38 px et élargit
   la flèche à 34 px : la croix retombe alors PAR-DESSUS la flèche. Le clic atterrit sur la flèche
   et rouvre la liste au lieu de vider le champ — l'utilisateur ne peut littéralement pas effacer
   sa sélection.

   ARITHMÉTIQUE (à refaire si le thème change ces valeurs) :
     · flèche : right:3px, largeur 34px  → elle occupe la bande [3px, 37px] depuis le bord droit ;
     · croix  : posée juste au-delà, à right:38px, sur une zone cliquable de 22px ;
     · texte  : repoussé au-delà des deux, soit 38 + 22 = 60px.

   ⚠️ Les correctifs locaux qui ont précédé ce fichier utilisaient right:26px — soit 11px SOUS la
   flèche. Ils atténuaient le défaut sans le supprimer : la zone restait partiellement couverte.
   Ne pas revenir à cette valeur en la croyant éprouvée. */

.select2-container--default .select2-selection--single .select2-selection__clear {
    position: absolute;
    right: 38px;
    top: 0;
    height: 100%;
    width: 22px;
    margin: 0;
    padding: 0;
    float: none;                 /* annule le float d'origine, source du chevauchement */
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.15em;
    font-weight: 400;
    line-height: 1;
    color: var(--ct-gray-600, #8a969c);
    cursor: pointer;
    z-index: 3;                  /* au-dessus de la flèche : le clic vise bien la croix */
}
.select2-container--default .select2-selection--single .select2-selection__clear:hover {
    color: var(--ct-danger, #fa5c7c);
}

/* Le libellé sélectionné doit s'arrêter AVANT la croix et la flèche, sinon un nom long passe
   dessous et les rend illisibles.
   Le réservoir n'est creusé QUE lorsqu'une croix existe : select2 pose lui-même la classe
   « select2-selection--clearable » sur le conteneur quand il en insère une, et la retire à la
   remise à zéro. S'appuyer dessus évite d'indenter inutilement les sélecteurs sans allowClear,
   et de faire sauter le texte au moment de la sélection. */
.select2-container--default .select2-selection--single.select2-selection--clearable .select2-selection__rendered {
    padding-right: 60px;
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   Select2 — état DÉSACTIVÉ illisible en thème sombre
   ═══════════════════════════════════════════════════════════════════════════════════════════
   `select2.min.css` code en dur le fond du champ désactivé :

       .select2-container--default.select2-container--disabled .select2-selection--single
       { background-color: #eee }

   Le thème habille bien le select2 ACTIF (of-app.css le repeint en var(--ct-input-bg)), mais il
   n'a aucune règle pour l'état désactivé — et celle du vendeur a une spécificité de (0,3,0),
   supérieure à la sienne (0,2,0). Résultat en sombre : une boîte quasi blanche portant un texte
   clair (--ct-body-color = #aab8c5), soit ~1,6:1. Le libellé disparaît purement et simplement.

   Vu sur le sélecteur « Dossier » d'une pièce VALIDÉE (le module Financier désactive tout l'en-tête
   dès la validation), mais le défaut n'a rien de local : il touche CHAQUE select2 désactivé de
   l'application. La correction est donc ici, pas dans le CSS du Financier.

   Les valeurs reprennent celles de Bootstrap pour un champ désactivé (--ct-tertiary-bg), pour
   qu'un select2 grisé ressemble à l'`<input>` grisé posé juste à côté de lui.
   ⚠️ Reproduire la spécificité du vendeur (les deux classes sur le conteneur) : un sélecteur plus
   court perdrait, et l'ordre de chargement n'y changerait rien. */
.select2-container--default.select2-container--disabled .select2-selection--single,
.select2-container--default.select2-container--disabled .select2-selection--multiple {
    background-color: var(--ct-tertiary-bg);
    border-color: var(--ct-border-color);
}
.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__rendered {
    color: var(--ct-body-color);
}
/* Un champ désactivé n'est pas un champ vide : sa flèche reste dessinée, mais en encre atténuée
   pour dire « non modifiable » plutôt que « rien à ouvrir ». */
.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__arrow b {
    border-top-color: var(--ct-secondary-color);
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   Zone de recherche — bouton d'effacement
   ═══════════════════════════════════════════════════════════════════════════════════════════
   Forme retenue dans l'application : un input-group, l'input, puis un bouton « × » masqué tant
   que le champ est vide (Agenda, Documents, Emails). La classe ci-dessous n'impose rien de plus
   qu'un alignement propre du glyphe, pour que les zones se ressemblent d'un module à l'autre. */
.of-search-clear {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   Badge d'initiales — repli quand l'utilisateur n'a pas de photo
   ═══════════════════════════════════════════════════════════════════════════════════════════
   Le badge réutilise les classes `.avatar-xs|sm|md` du thème, qui ne fixent QUE des dimensions
   (`.avatar-xs` = 1,5rem). Le texte héritait donc de la police du contexte — 1rem — et débordait
   d'un cercle de 24 px. Constaté sur l'agenda de la démonstration le 2026-07-28, où aucun
   utilisateur n'a de photo : toutes les initiales dépassaient de leur pastille.

   La taille est fixée ICI, une fois, et non en ligne dans les cinq endroits qui construisent un
   badge — c'est précisément la duplication qui avait laissé le défaut passer partout à la fois. */
.of-initials {
    font-weight: 700;
    line-height: 1;
    overflow: hidden;
    flex: 0 0 auto;          /* dans un flex, le cercle ne doit ni s'étirer ni se comprimer */
}
.of-initials.avatar-xs { font-size: .5625rem; }   /*  9px dans 24px */
.of-initials.avatar-sm { font-size: .75rem;   }   /* 12px dans 36px */
.of-initials.avatar-md { font-size: 1rem;     }   /* 16px dans 48px */
.of-initials.avatar-lg { font-size: 1.25rem;  }

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   Menu de gauche — il reste en place, y compris en mode « Condensé »
   ═══════════════════════════════════════════════════════════════════════════════════════════
   Le thème pose `position: fixed` sur `.leftside-menu`… puis la repasse à `absolute` pour le seul
   mode Condensé. Un élément absolu se positionne par rapport au document, pas à la fenêtre : la
   barre prenait donc toute la HAUTEUR DE LA PAGE (mesuré : 7167 px sur la liste des contacts, pour
   637 px d'entrées de menu) et s'en allait vers le haut dès qu'on défilait. On se retrouvait à
   regarder le vide sous les entrées disparues — signalé sur les Contacts et les Paramètres
   généraux, les deux écrans assez longs pour que ça se voie.

   Les deux autres modes (Survol, Épinglé) n'ont jamais eu le défaut : ils restent en `fixed`. Le
   correctif rétablit donc simplement la règle commune, sans rien changer d'autre — largeur, survol
   qui déplie, transitions : tout vient d'ailleurs.

   La condition sur `data-layout-position` n'est pas décorative : elle donne la priorité voulue sans
   `!important`, et elle laisse le thème décider s'il existe un jour une page en mise en page
   « scrollable », où une barre qui suit le document est cette fois le comportement attendu. */
html[data-layout-position="fixed"][data-sidenav-size="condensed"]:not([data-layout="topnav"]) .wrapper .leftside-menu {
    position: fixed;
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   … et il défile quand il ne tient pas dans la fenêtre, sans ascenseur visible
   ═══════════════════════════════════════════════════════════════════════════════════════════
   Une fois la barre fixée (règle ci-dessus), sa hauteur est celle de la fenêtre : sous ~640 px,
   les dernières entrées passaient sous le bord, hors d'atteinte. Les modes Survol et Épinglé, eux,
   défilaient déjà — le thème coupe le défilement du seul mode Condensé (`overflow: visible` forcé
   sur les conteneurs SimpleBar), afin que le libellé déplié au survol puisse déborder d'une barre
   large de 70 px.

   Le rendre défilable rouvre donc ce conflit : un bloc qui défile rogne son contenu sur les DEUX
   axes — on regagnait les entrées du bas en perdant les libellés, c'est-à-dire la seule façon de
   lire ce menu réduit à des icônes.

   D'où le détour : ce n'est pas le défilement qu'on refuse, c'est le rognage qu'on déplace. Le
   conteneur qui défile prend la largeur du libellé DÉPLIÉ (260 px) au lieu de celle de la barre —
   la coupure tombe alors exactement là où le libellé s'arrête, donc nulle part. La barre, elle,
   reste large de 70 px : seul un conteneur invisible dépasse, et `pointer-events` l'empêche de
   capter le moindre clic destiné à la page. Le contenu reprend les 70 px de la barre pour que la
   molette et les clics fonctionnent au-dessus des icônes.

   Mesuré : 95 px d'entrées regagnés à 560 px de fenêtre, aucune barre de défilement (ni native —
   SimpleBar pose déjà `scrollbar-width: none` — ni la sienne propre), libellé « Appels
   Téléphoniques » affiché en entier, et un clic à 100 px du bord atteint bien le tableau derrière.

   ⚠️ `max-width: 100%` est posé par SimpleBar sur le conteneur : sans le neutraliser, la largeur
   demandée est plafonnée à celle de la barre et tout le reste ne sert à rien — en silence. */
html[data-sidenav-size="condensed"]:not([data-layout="topnav"]) .wrapper .leftside-menu .simplebar-mask,
html[data-sidenav-size="condensed"]:not([data-layout="topnav"]) .wrapper .leftside-menu .simplebar-offset,
html[data-sidenav-size="condensed"]:not([data-layout="topnav"]) .wrapper .leftside-menu .simplebar-content-wrapper {
    width: var(--ct-leftbar-width) !important;
    max-width: none !important;
    pointer-events: none;
}
html[data-sidenav-size="condensed"]:not([data-layout="topnav"]) .wrapper .leftside-menu .simplebar-content-wrapper {
    overflow-y: auto !important;
    overflow-x: hidden !important;
}
html[data-sidenav-size="condensed"]:not([data-layout="topnav"]) .wrapper .leftside-menu .simplebar-content {
    width: var(--ct-leftbar-width-sm);
    pointer-events: auto;
}
/* La barre de SimpleBar est masquée : le menu défile, mais rien ne l'annonce visuellement — c'est
   le parti pris du thème pour les autres modes, on ne le change pas ici. */
html[data-sidenav-size="condensed"]:not([data-layout="topnav"]) .wrapper .leftside-menu .simplebar-scrollbar {
    display: none !important;
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   Menu de gauche — la couleur du module, en pastille derrière le glyphe
   ═══════════════════════════════════════════════════════════════════════════════════════════
   Chaque entrée déclare sa teinte (`--of-nav-teinte`, style en ligne dans include/shell/nav.inc.php,
   là où vivait déjà la couleur du module). Ce qui la portait avant : un `border-left: 5px solid`
   posé sur le glyphe lui-même — un trait COMPRIS dans la boîte de l'icône (`box-sizing: border-box`),
   qui lui prenait 5 px de large et poussait le dessin vers la droite. En mode Condensé, où le thème
   passe l'icône en `display: block`, ce même trait se retrouvait plaqué au bord gauche de la rangée,
   détaché du glyphe qu'il était censé qualifier.

   ⚠️ POURQUOI LA COULEUR NE TOUCHE PAS LE GLYPHE — c'était la piste évidente, la mesure l'a écartée.
   Le menu est peint d'un dégradé indigo (`data-menu-color="brand"`, #6379c3 → #546ee5) sur TOUTES
   les pages, thème clair comme sombre. Contraste d'un glyphe teinté sur ce fond, mélange 62 %
   teinte / 38 % blanc, mesuré sur les onze modules : de 1,27:1 (Notes de frais) à 2,76:1 (Temps
   passé). Le blanc pur y tient 4,16, le libellé du thème (#cedce4) 2,96. Teinter le symbole, c'est
   donc le rendre moins lisible qu'aujourd'hui — sur Contacts, Notes de frais et Financier, une
   icône qu'on devine. La teinte passe derrière ; le glyphe garde le blanc.

   ⚠️ POURQUOI LA PASTILLE EST ASSOMBRIE DE 15 % avant d'être posée. Quatre teintes de la palette
   sont trop claires pour porter un glyphe blanc à pleine couleur : Agenda 2,43:1, Dossiers 2,15,
   Tâches 2,54, Temps passé 2,00 — et c'est justement à pleine couleur que s'affiche la pastille de
   l'entrée COURANTE. `color-mix(… 85 %, #000)` remonte les onze modules à 3,08:1 au minimum en
   actif et 3,41:1 au repos, sans rien retirer à la couleur (l'œil lit toujours le rouge d'Emails,
   le cyan de l'Agenda). C'est une correction UNIFORME : pas d'exception module par module, donc
   rien à refaire quand une teinte change ou qu'un module s'ajoute.

   ⚠️ La pastille est un PSEUDO-ÉLÉMENT hors flux, pas une boîte redimensionnée. Le thème donne à
   l'icône trois géométries selon le mode du menu (45 px en Survol/Épinglé, 50 px en Condensé,
   `width: auto` + `display: block` en Compact) ; toucher à sa largeur aurait décalé la pastille
   hors du centre dans deux modes sur trois — le mode Survol REPLIÉ, celui de tout le monde, laisse
   l'icône à 45 px dans une barre de 70. Centrée sur la boîte quelle qu'en soit la taille, la
   pastille suit les trois modes sans qu'aucun ait à être traité à part. */
.side-nav .side-nav-link i {
    --of-nav-pastille: color-mix(in oklab, var(--of-nav-teinte, transparent) 85%, #000);
    position: relative;
    z-index: 0;                  /* crée le contexte d'empilement qui garde la pastille DESSOUS */
    color: #fff;                 /* le glyphe, lui, ne prend jamais la teinte (cf. la mesure) */
}
.side-nav .side-nav-link i::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 32px;
    height: 32px;
    transform: translate(-50%, -50%);
    border-radius: 9px;
    background: color-mix(in srgb, var(--of-nav-pastille) 55%, transparent);
    z-index: -1;
    transition: background-color .18s ease-in-out;
}
/* Une entrée sans teinte déclarée n'a pas de pastille du tout — sans ce garde-fou, le mélange
   `transparent 85 % + #000` lui en dessinerait une grise, visible et incompréhensible. */
.side-nav .side-nav-link i:not([style*="--of-nav-teinte"])::after {
    display: none;
}
.side-nav .side-nav-link:hover i::after {
    background: color-mix(in srgb, var(--of-nav-pastille) 80%, transparent);
}

/* ── L'entrée courante ────────────────────────────────────────────────────────────────────────
   Elle n'avait AUCUN fond jusqu'ici. Le thème stylise `.menuitem-active`, mais c'est `.active` que
   pose include/shell/scripts.inc.php, sur le <li> : la règle existait, elle ne rencontrait jamais
   son élément. La pastille du module ouvert passe donc à pleine teinte, et la rangée prend un voile
   blanc — deux signaux, l'un coloré, l'autre de forme, pour ceux qui ne distinguent pas les teintes. */
.side-nav .side-nav-item.active > .side-nav-link {
    background: rgba(255, 255, 255, .14);
    color: var(--ct-menu-item-active-color);
    font-weight: 600;
}
.side-nav .side-nav-item.active > .side-nav-link i::after {
    background: var(--of-nav-pastille);
}
/* Une sous-page (« Devis », « Vue Kanban »…) marque le <li> de SECOND niveau : sans cette règle,
   le module ouvert n'était signalé nulle part au premier niveau. `:has()` est isolé dans son propre
   bloc — glissé dans la liste ci-dessus, un navigateur qui ne le connaît pas invaliderait aussi les
   sélecteurs valides qui l'accompagnent. */
.side-nav .side-nav-item:has(> .collapse .active) > .side-nav-link {
    color: var(--ct-menu-item-active-color);
}
.side-nav .side-nav-item:has(> .collapse .active) > .side-nav-link i::after {
    background: var(--of-nav-pastille);
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   Actions de ligne — les gestes courants sortis du menu « … »
   ═══════════════════════════════════════════════════════════════════════════════════════════
   Une ou deux actions par ligne se répètent toute la journée (consulter, ouvrir) ; les autres se
   CHOISISSENT (créer une tâche, convertir, supprimer). Les premières sont donc rendues en icônes
   devant le menu, qui garde les secondes. Contacts et Documents partagent ces règles : le même
   remède écrit deux fois aurait divergé, c'est déjà arrivé dans ce projet.

   ⚠️ La teinte BRUTE d'un module ne convient pas. Mesuré sur le fond réel des lignes, en thème
   sombre : le violet des Contacts tombe à 2,02:1 et le gris `text-secondary` à 2,45:1 — sous le
   seuil de 3:1 exigé d'un élément graphique, d'où des icônes qu'on devine. C'est la teinte qu'il
   faut corriger, pas l'opacité : assombrir de 15 % en thème clair, éclaircir de 35 % en sombre,
   exactement comme les filets des blocs de la fiche contact.

   Chaque icône déclare sa teinte de base dans `--of-act` ; la correction est faite ici, une fois.
   Le « … » du menu reçoit le même traitement : il faisait partie du même groupe visuel tout en
   restant sur le gris du thème, à 2,45:1. */
.of-row-actions {
    --of-act: #64748B;                       /* ardoise par défaut : jamais d'action sans teinte */
}
.of-row-actions .of-act {
    cursor: pointer;
    line-height: 1;                          /* n'étire pas la ligne du tableau */
}
.of-row-actions .of-act > i,
.of-row-actions .card-drop > i {
    color: color-mix(in srgb, var(--of-act) 85%, black);
    transition: transform .12s ease, filter .12s ease;
}
[data-bs-theme="dark"] .of-row-actions .of-act > i,
[data-bs-theme="dark"] .of-row-actions .card-drop > i {
    color: color-mix(in srgb, var(--of-act) 65%, white);
}
/* Sans libellé ni bordure, le survol est le seul retour dont ces icônes disposent. */
.of-row-actions .of-act:hover > i {
    transform: scale(1.18);
    filter: brightness(1.15);
}

/* ══════════════════════════════════════════════════════════════════════════════════════════════
 * BADGE NEUTRE ET REGROUPEMENT — lisibles dans les deux thèmes
 *
 * `badge text-bg-light` de Bootstrap fige sa couleur de TEXTE à la compilation, pour le thème
 * clair. En thème sombre le fond suit le thème, pas le texte : mesuré dans la bibliothèque de
 * modèles, rgb(49,58,70) sur rgb(70,79,91) — un contraste de 1,4:1 là où il en faut 4,5. Les badges
 * de nature et de catégorie y étaient illisibles.
 *
 * ⚠️ NE PAS employer les variables `--bs-*` ici : ce thème ne les définit pas. Un
 * `var(--bs-tertiary-bg)` se résout en valeur invalide, donc en fond TRANSPARENT — la règle paraît
 * écrite et ne s'applique pas, en silence. Les variables du thème portent le préfixe `--ct-`.
 *
 * `--ct-dark` est la bonne encre : elle S'INVERSE d'un thème à l'autre (#313a46 en clair, #f1f1f1
 * en sombre). `--ct-body-color` et `--ct-emphasis-color`, eux, restent gris dans les deux et
 * donnaient moins de 3:1 sur un fond tertiaire.
 * ═════════════════════════════════════════════════════════════════════════════════════════════ */
.of-badge {
    background-color: var(--ct-tertiary-bg);
    color: var(--ct-dark);
    border: 1px solid var(--ct-border-color);
    font-weight: 500;
}

/* ══ Centrage VERTICAL du libellé des badges ══════════════════════════════════════════════════
   🔴 SIGNALÉ LE 2026-09-25 : « les libellés sont rarement centrés verticalement dans les badges ».
   MESURÉ dans Chrome sur 18 écrans (≈ 400 badges) : le texte d'un `.badge` était partout TROP HAUT
   — 1,4 px pour les capitales, 0,4 px pour les minuscules. Deux causes :

   1. `line-height: 1` + padding SYMÉTRIQUE (Bootstrap) avec Nunito, dont l'ascendant (1,01 em)
      l'emporte largement sur le descendant (0,35 em) : la ligne de base tombe bas dans la boîte,
      et le texte, lui, remonte. On déplace donc 0,09 em de padding du bas vers le haut : la
      HAUTEUR du badge ne change pas, le texte descend d'un pixel — capitales et minuscules se
      répartissent alors de part et d'autre du centre (−0,4 / +0,6 px), là où l'œil le cherche.
   2. Dans une ligne FLEX ou une grille, le badge est un élément étiré (`align-self: stretch` par
      défaut) : il prend toute la hauteur de la ligne, son texte reste collé en haut, et deux
      lignes voisines se touchent — les badges paraissent empilés (fiche « Par personne »).
      `align-self: center` lui rend sa hauteur propre, centrée sur la ligne.

   ⚠️ Les utilitaires de padding (`py-1`, `p-0`…) restent maîtres : ils sont en !important. */
.badge {
    padding-top: calc(var(--ct-badge-padding-y) + .09em);
    padding-bottom: calc(var(--ct-badge-padding-y) - .09em);
    align-self: center;
}

/* Variante TEINTÉE : même mécanique, mais la couleur se choisit au point d'usage via
   `--of-badge-teinte`, typiquement une couleur de module (`--color-e-mails`, `--color-dossiers`…).
   Sert à distinguer d'un coup d'œil des étiquettes de natures différentes posées côte à côte —
   compte et dossier sur une ligne de résultat de recherche, par exemple — sans rendre la liste
   bariolée : seul un fond très dilué porte la couleur, l'encre reste celle du thème.

   ⚠️ `--ct-dark` et non une encre figée : c'est la seule encre du thème qui s'inverse entre clair
   et sombre. Une couleur en dur ici donnerait un badge illisible dans l'un des deux — le piège que
   `badge bg-light` avait déjà tendu (1,4:1 en sombre).

   Usage : <span class="badge of-badge-teinte" style="--of-badge-teinte: var(--color-e-mails)"> */
.of-badge-teinte {
    background-color: color-mix(in srgb, var(--of-badge-teinte, var(--ct-info)) 18%, var(--ct-secondary-bg));
    color: var(--ct-dark);
    border: 1px solid color-mix(in srgb, var(--of-badge-teinte, var(--ct-info)) 45%, transparent);
    font-weight: 500;
}

/* Étiquette du DOSSIER d'un email (réception, envoyés…) sur les listes qui en mêlent plusieurs :
   même mécanique, teinte un peu plus soutenue et icône à la couleur pleine — ici la couleur n'est
   pas une nuance d'ambiance, c'est ce qui permet de distinguer « Boîte de réception » d'« Envoyés »
   sans lire. L'encre du texte reste celle du thème. */
.of-badge-teinte.ei-badge-dossier {
    background-color: color-mix(in srgb, var(--of-badge-teinte, var(--ct-info)) 24%, var(--ct-secondary-bg));
}
.of-badge-teinte.ei-badge-dossier > .mdi { color: var(--of-badge-teinte); }

/* Variante « portée » : même mécanique, teinte d'accent, pour que la visibilité se lise d'un coup
   d'œil sans écraser le nom du modèle. */
.of-badge-portee {
    background-color: color-mix(in srgb, var(--ct-info) 18%, var(--ct-secondary-bg));
    color: var(--ct-dark);
    border: 1px solid color-mix(in srgb, var(--ct-info) 45%, transparent);
    font-weight: 500;
}

/* Marqueur de portée dans une LISTE (cadenas devant le nom d'un document restreint). Un badge à
   texte y serait de trop : on ne signale ici que l'exception, et sur des lignes qui tronquent déjà
   leur contenu. `--ct-` et non `--bs-` : le thème ne définit pas les secondes, la règle paraîtrait
   écrite sans jamais s'appliquer.

   ⚠️ DEUX teintes, une par thème, et ce n'est pas une coquetterie : `--ct-info` n'est PAS redéfini
   en sombre, si bien que la même teinte donne 2,55:1 sur fond clair — sous le seuil de 3:1 d'un
   élément graphique — et 4,51:1 sur fond sombre. La foncer suffisamment pour le clair (4,9:1) la
   rendrait illisible en sombre (2,35:1). Mesuré, pas estimé. */
.of-portee-restreint {
    color: color-mix(in srgb, var(--ct-info) 70%, black);
    vertical-align: -0.05em;
}

[data-bs-theme="dark"] .of-portee-restreint {
    color: var(--ct-info);
}

/* ── Encre sur un fond de COULEUR PLEINE (badges, toasts) ──────────────────────────────────────
 *
 * 🔴 SIGNALÉ LE 2026-09-24 : le badge « Administrateur » de la liste des utilisateurs, blanc sur
 * ambre, illisible. Il demandait pourtant la bonne chose — `badge bg-warning text-dark`.
 *
 * Le piège : `text-dark` ne veut pas dire « encre foncée ». Il veut dire « la couleur NOMMÉE dark
 * du thème », et le thème sombre l'inverse : `--ct-dark-rgb` y vaut 241,241,241. Sur un fond
 * `bg-warning` — qui, lui, ne s'inverse pas — cela donne du blanc cassé sur du jaune : 1,41:1.
 * Le même piège frappe l'autre sens, et depuis plus longtemps encore : `.badge` pose `color:#fff`
 * par défaut, si bien qu'un `badge bg-success` NU — il y en a partout — est blanc sur vert clair,
 * soit 2,02:1, et `badge bg-info` 2,55:1. Dans les deux thèmes, depuis toujours.
 *
 * La règle : sur un fond de couleur pleine qui ne suit pas le thème, l'encre ne doit pas le suivre
 * non plus. Elle se choisit sur la clarté du FOND. Les trois teintes ci-dessous sont claires, donc
 * l'encre y est foncée et FIXE : 9,7:1 sur ambre, 7,63:1 sur vert, 6,04:1 sur cyan.
 *
 * ⚠️ Ne vaut QUE pour les fonds pleins clairs. Un fond qui suit le thème (`bg-light`, ou la
 * mécanique teintée de `.of-badge-teinte` plus haut) doit au contraire garder une encre de thème —
 * les deux s'inversent ensemble, et une encre figée y casserait l'un des deux thèmes.
 *
 * ⚠️ Mesuré, pas estimé : tests/verifier_contraste_couleurs.php CALCULE ces rapports (WCAG 2.1) et
 * refait le calcul à chaque passage, y compris si une teinte du thème change. */
.badge.bg-warning, .bg-warning.text-dark, .bg-warning.text-white, .text-bg-warning,
.badge.bg-success, .bg-success.text-dark, .bg-success.text-white, .text-bg-success,
.badge.bg-info,    .bg-info.text-dark,    .bg-info.text-white,    .text-bg-info {
    color: #212529 !important;
}

/* ── En-tête de groupe (regroupement par catégorie, vues cartes et table) ───────────────────────
 *
 * C'est un BOUTON, pas un simple titre : cliquer replie le groupe. Il doit donc se voir comme tel —
 * un liseré coloré, un fond teinté et un chevron. Un simple filet gris sous un texte en capitales se
 * lisait comme une décoration, et personne n'aurait pensé à cliquer dessus.
 *
 * La teinte vient de `--of-groupe-couleur`, que chaque page pose selon son module (défaut : la
 * couleur des Documents). Le fond est calculé par `color-mix` sur le fond du thème, donc il suit le
 * clair comme le sombre sans qu'on ait à écrire deux règles. */
.of-groupe-titre {
    --of-groupe-couleur: var(--color-documents, #3B82F6);
    display: flex;
    align-items: center;
    gap: .5rem;
    width: 100%;
    font-size: .82rem;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--ct-dark);
    background-color: color-mix(in srgb, var(--of-groupe-couleur) 12%, var(--ct-secondary-bg));
    border: 0;
    border-left: 3px solid var(--of-groupe-couleur);
    border-radius: .25rem;
    padding: .45rem .7rem;
    text-align: left;
    cursor: pointer;
    transition: background-color .12s ease;
}
.of-groupe-titre:hover {
    background-color: color-mix(in srgb, var(--of-groupe-couleur) 22%, var(--ct-secondary-bg));
}
.of-groupe-titre .of-groupe-chevron {
    transition: transform .15s ease;
    color: var(--of-groupe-couleur);
}
.of-groupe-titre[aria-expanded="false"] .of-groupe-chevron { transform: rotate(-90deg); }
/* ⚠️ Le compte se place JUSTE APRÈS le nom, pas en bout de bande : sur un écran large, un
   `margin-left: auto` le renvoyait à plus de mille pixels du titre auquel il se rapporte. */
.of-groupe-titre .of-groupe-nb {
    font-weight: 500;
    letter-spacing: 0;
    text-transform: none;
    color: var(--ct-body-color);
}

/* ⚠️ Une cellule de tableau ne se colore PAS par `background-color` : le thème le surcharge en
   `!important`. Le seul chemin qui tienne est la variable `--ct-table-bg`, que la feuille du thème
   lit elle-même. Le reste du bloc, lui, s'applique normalement — d'où un faux diagnostic possible
   « la feuille n'est pas chargée » alors que seule la couleur de fond est ignorée. */
tr.of-groupe-ligne > td {
    --of-groupe-couleur: var(--color-documents, #3B82F6);
    --ct-table-bg: color-mix(in srgb, var(--of-groupe-couleur) 12%, var(--ct-secondary-bg));
    color: var(--ct-dark);
    font-weight: 600;
    letter-spacing: .03em;
    cursor: pointer;
    /* ⚠️ SURTOUT PAS `box-shadow` pour le filet de gauche, si tentant soit-il : la ligne zébrée de
       DataTables (`table.dataTable.table-striped > tbody > tr:nth-of-type(2n+1) > *`) a une
       spécificité supérieure et écrase la propriété ENTIÈRE par son `inset 0 0 0 9999px`. Résultat
       observé : une ligne de groupe sur deux perdait son filet — un défaut d'apparence ALTERNÉE,
       qu'on prend d'abord pour un hasard d'affichage. `border-left` est libre, et le seul rival est
       la remise à zéro `tbody, td, tfoot, th, thead, tr` que ce sélecteur bat déjà. */
    border-left: 3px solid var(--of-groupe-couleur);
}
tr.of-groupe-ligne > td:hover {
    --ct-table-bg: color-mix(in srgb, var(--of-groupe-couleur) 22%, var(--ct-secondary-bg));
}

/* Repliement. Sur les cartes, on masque les colonnes du groupe ; en table, ses lignes. */
.of-groupe-replie { display: none !important; }

/* ── Densité « compacte » des listes ──────────────────────────────────────────────────────────
   Posée par head.inc.php sur <html> (`data-densite="compacte"`), depuis la préférence de compte.
   « confortable » ne pose AUCUN attribut : c'est l'aspect d'origine, et une règle qui le redirait
   serait une règle de plus à tenir en accord avec le thème.

   ⚠️ ON RESSERRE LES ESPACES, PAS LE TEXTE. Réduire la taille de police gagnerait des lignes en
   les rendant moins lisibles — c'est l'inverse du service rendu à qui passe sa journée dans une
   liste de plusieurs centaines d'entrées. Seuls les rembourrages verticaux diminuent.

   ⚠️ Ciblé sur les TABLEAUX (`.table`), pas sur tout : appliquer un resserrement global toucherait
   les formulaires, les fenêtres et les cartes, où la place n'est pas le problème.

   ⚠️ `!important` — ET C'EST DÉLIBÉRÉ. `css/custom-styles.css` impose déjà
   `.table td { padding: .75rem !important }` à tous les tableaux de l'application. Sans marquer
   cette règle-ci à son tour, la densité compacte n'a AUCUN effet visible : l'attribut est bien
   posé sur <html>, la feuille bien chargée, la règle bien écrite — et rien ne bouge. C'est
   exactement le défaut qui nous a été rapporté, et il est invisible sur un banc d'essai qui
   oublie de charger `custom-styles.css` : les deux premiers l'ont oublié et ont donné un
   « ça marche » qui ne valait rien. Un banc doit charger les feuilles de la PAGE SERVIE, dans
   son ordre. */
html[data-densite="compacte"] .table > :not(caption) > * > * {
  padding-top: .35rem !important;
  padding-bottom: .35rem !important;
}
html[data-densite="compacte"] .table-sm > :not(caption) > * > * {
  padding-top: .2rem !important;
  padding-bottom: .2rem !important;
}
/* Les cellules d'action gardent leur hauteur de cible : un bouton de 24 px reste un bouton de
   24 px, sinon on gagne des lignes en perdant des clics. */
html[data-densite="compacte"] .table .of-row-actions { line-height: 1.1; }

/* ══════════════════════════════════════════════════════════════════════════════════════════════
   Onglet « Modules » des réglages — chaque section porte la couleur de son module.

   Le panneau aligne une quinzaine de réglages appartenant à six modules différents : sans repère,
   on cherche « Agenda » en lisant les titres un à un. La couleur de module est déjà le repère
   qu'utilise le reste de l'application (menu, rubans d'en-tête) — c'est elle qu'on reprend ici,
   pas une palette de plus.

   ⚠️ LA COULEUR NE PORTE QUE LE FILET ET L'ICÔNE, JAMAIS LE TITRE. Ces teintes sont choisies pour
   des aplats à texte blanc ; posées sur du texte, plusieurs tombent sous 3:1 sur le fond de la
   fenêtre (le piège déjà rencontré sur les blocs de la fiche contact). Le titre garde donc l'encre
   du thème, et la couleur reste un repère à côté de lui — ce qui la dispense aussi du seuil 4,5:1,
   puisqu'elle ne porte aucune information que le mot ne dise déjà.

   « Listes » couvre Contacts ET Documents : elle n'emprunte la couleur d'aucun des deux et retombe
   sur la bordure neutre du thème. Un repère faux serait pire que pas de repère.
   ══════════════════════════════════════════════════════════════════════════════════════════════ */
/* ⚠️ LA TEINTE BRUTE N'EST PAS LISIBLE, ET L'ÉCHEC EST SYMÉTRIQUE. Mesuré sur le fond réel de la
   fenêtre : en thème CLAIR, Dossiers tombe à 2,15:1, Agenda à 2,43:1, Tâches à 2,54:1 — les teintes
   vives. En thème SOMBRE, ce sont les foncées qui disparaissent : Espaces de travail à 1,85:1,
   À la connexion à 2,36:1. Le seuil applicable est 3:1 (repère graphique, pas du texte).
   On dérive donc la teinte PAR THÈME — assombrie sur fond clair, éclaircie sur fond sombre —
   plutôt que de retoucher quatorze couleurs à la main. Le fond à 8 %, lui, garde la teinte brute :
   il ne porte rien, il situe. */
.up-section-titre {
  --up-lisible: color-mix(in srgb, var(--up-couleur, var(--ct-border-color)) 72%, #000);
  border-left: 3px solid var(--up-lisible);
  border-radius: 2px;
  padding: .25rem .5rem;
  background: color-mix(in srgb, var(--up-couleur, transparent) 8%, transparent);
}
html[data-bs-theme="dark"] .up-section-titre {
  --up-lisible: color-mix(in srgb, var(--up-couleur, var(--ct-border-color)) 62%, #fff);
}
.up-section-titre > i { color: var(--up-lisible, var(--of-encre-attenuee)); }
/* Sans couleur de module (« Listes » couvre Contacts ET Documents), on ne dérive rien : l'encre
   atténuée du thème est déjà tenue pour lisible dans les deux modes. */
.up-section-titre:not([style*="--up-couleur"]) {
  --up-lisible: var(--of-encre-attenuee);
  border-left-color: var(--ct-border-color);
}

/* ══════════════════════════════════════════════════════════════════════════════════════════════
   Onglets de référentiels — Paramètres généraux (Facturation, Notes de frais).

   ⚠️ MESURÉ AVANT DE TOUCHER, ET LE DÉFAUT EST À L'ENVERS : l'onglet ACTIF ressortait à 4,69:1 sur
   blanc, en encre GRISE ET TRANSLUCIDE (rgba(108,117,125,.75)), tandis que les inactifs tenaient
   3,03:1 — sous le seuil de 4,5:1 exigé pour du texte. Autrement dit, l'onglet sélectionné était
   à peine plus lisible que les autres, et aucun des deux n'était vraiment lisible. La seule marque
   de sélection était un filet gris très clair (#E5E8EB) sur fond blanc : invisible.

   On rend donc l'ACTIF franc — encre pleine du thème et accent de 2 px dans la COULEUR DU MODULE,
   le repère que l'application emploie déjà partout (ruban d'en-tête, barre du menu) — et l'on
   relève les inactifs jusqu'au seuil.

   ⚠️ Portée limitée à `.of-onglets` : `.nav-tabs` sert dans tout le produit (emails, contacts,
   dossiers, fiches). Une règle globale aurait changé une dizaine d'écrans qu'on n'a pas regardés.
   ══════════════════════════════════════════════════════════════════════════════════════════════ */
.of-onglets.nav-tabs {
    --of-onglet-couleur: var(--ct-primary);
    border-bottom: 1px solid var(--ct-border-color);
    gap: .15rem;
}
.of-onglets.nav-tabs .nav-link {
    /* ⚠️ `--of-encre-attenuee` nue mesure 4,07:1 sur blanc — sous le seuil de 4,5:1. Le jeton est
       partagé et validé pour d'AUTRES fonds ; on ne le déplace donc pas, on l'assombrit ici, où le
       fond est blanc. Un onglet inactif reste un texte qu'on doit pouvoir lire pour choisir. */
    color: color-mix(in srgb, var(--of-encre-attenuee) 84%, #000);
    border: 0;
    /* Le filet vit sur l'onglet lui-même : posé sur un pseudo-élément, il passerait sous la bordure
       du conteneur et l'on ne verrait plus qu'un liseré coupé. */
    border-bottom: 2px solid transparent;
    padding: .5rem .9rem;
    font-weight: 500;
}
.of-onglets.nav-tabs .nav-link:hover,
.of-onglets.nav-tabs .nav-link:focus {
    color: var(--ct-dark);
    border-bottom-color: color-mix(in srgb, var(--of-onglet-couleur) 40%, transparent);
    background-color: transparent;
}
html[data-bs-theme="dark"] .of-onglets.nav-tabs .nav-link {
    /* En sombre, assombrir irait dans le mur : c'est l'inverse qu'il faut. */
    color: color-mix(in srgb, var(--of-encre-attenuee) 88%, #fff);
}
.of-onglets.nav-tabs .nav-link.active {
    color: var(--ct-dark);
    font-weight: 600;
    background-color: transparent;
    border-bottom-color: var(--of-onglet-couleur);
}
/* ⚠️ PIÈGE DE SPÉCIFICITÉ, MESURÉ : la règle sombre des onglets INACTIFS
   (`html[data-bs-theme="dark"] … .nav-link`) porte un sélecteur d'élément de plus que celle de
   l'actif (`… .nav-link.active`). À nombre de classes égal, c'est elle qui l'emporte — et en
   thème sombre l'onglet sélectionné reprenait l'encre des autres : 6,09:1 des deux côtés, plus
   aucune distinction. Il faut donc redire l'actif AU MÊME NIVEAU. */
html[data-bs-theme="dark"] .of-onglets.nav-tabs .nav-link.active {
    color: var(--ct-dark);
}

/* ══ Repère « factures échues à relancer » du bandeau du haut ═════════════════════════════════
   La pastille du thème (`.noti-icon-badge`) est un POINT MUET de 7 px : y écrire un nombre donne
   un point difforme dont le chiffre déborde. La cloche (#notif-unread-badge) et les Notes
   (#of-notes-badge) se sont déjà chacune fait la leur ; c'est exactement la même forme qui est
   reprise ici, pour que les trois compteurs du bandeau soient le même objet.

   La règle vit dans of-commun.css et non dans finance.css : l'icône est sur TOUTES les pages,
   alors que la feuille du module Financier n'est servie que sur les siennes — la pastille se
   serait affichée difforme partout ailleurs, c'est-à-dire là où elle sert.

   ⚠️ Le GLYPHE est à la couleur du Financier (il dit où le clic mène), la PASTILLE au rouge de
   l'alerte — comme la cloche, dont l'icône est neutre et le compteur rouge, et comme le bouton
   « À relancer » de la page des factures, qui est le même travail vu de plus près. */
/* ⚠️ 4e fois (26/09) : le parapheur avait la pastille MUETTE du thème — un point rouge sans chiffre, à côté
   de trois compteurs chiffrés. Tout compteur du bandeau se déclare ICI. */
#of-relances-badge,
#of-parapheur-badge {
    position: absolute;
    top: 2px;
    right: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    height: 16px;
    padding: 0 4px;
    border-radius: 8px;
    background: var(--ct-danger, #fa5c7c);
    color: #fff;
    font-size: 10px;
    font-weight: 700;
    line-height: 1;
    box-sizing: border-box;
    box-shadow: 0 0 0 2px var(--ct-topbar-bg, #fff);
}

/* Position des compteurs du bandeau du POSTE — réglée à l'œil par le propriétaire dans la console (26/09) :
   la pastille se pose en bas à droite de l'icône, au lieu de la chevaucher par le haut. Une SEULE règle pour les
   quatre compteurs (relances, parapheur, notes, notifications), qui gardent chacun leur forme dans leur feuille.
   ⚠️ Limitée à `.topbar-menu` (bandeau du poste) : la cloche du front MOBILE porte le même #notif-unread-badge,
   sur une icône d'une autre taille — la déplacer de -12 px l'aurait fait sortir de son bouton. La spécificité
   (classe + id) l'emporte sur les règles d'origine, quel que soit l'ordre de chargement des feuilles. */
.topbar-menu #of-relances-badge,
.topbar-menu #of-parapheur-badge,
.topbar-menu #of-notes-badge,
.topbar-menu #notif-unread-badge {
    top: 15px;
    right: -12px;
}

/* ── « N résultats » à côté d'un champ de recherche (assets/js/of_resultats.js) ───────────────────
   Une pastille discrète, aux couleurs du thème (clair comme sombre, par les variables --ct-) ;
   « Aucun résultat » prend la teinte d'avertissement, sans alarmer. Elle ne se replie jamais sur
   deux lignes : c'est une étiquette, pas une phrase. */
.of-nb-resultats {
    display: inline-flex; align-items: center; align-self: center; flex: 0 0 auto;
    margin-left: .5rem; padding: .15rem .6rem; border-radius: 1rem;
    font-size: .78rem; font-weight: 600; line-height: 1.4; white-space: nowrap;
    color: var(--ct-primary); background: rgba(var(--ct-primary-rgb), .12);
}
.of-nb-resultats.est-vide { color: var(--ct-warning-text-emphasis); background: var(--ct-warning-bg-subtle); }
/* Hors du flux (of_resultats.js) : elle ne déplace jamais le champ ni ses voisins. « dedans », elle
   se pose sur la droite du champ ; `pointer-events: none` : un clic à cet endroit reste un clic
   dans le champ, et la zone de texte s'arrête avant elle (marge intérieure posée par le script). */
.of-nb-resultats.a-gauche, .of-nb-resultats.dedans { z-index: 5; }
.of-nb-resultats.dedans { font-size: .72rem; padding: .05rem .5rem; pointer-events: none; }
.of-nb-resultats.dans-libelle { margin-left: .4rem; font-size: .72rem; padding: .05rem .5rem; vertical-align: 1px; }

/* ── Sens d'un appel téléphonique : entrant en VERT, sortant en ROUGE (2026-09-21) ──────────────────
   Classes globales : la liste et la fiche du module Appels (poste, espace contact), la liste mobile.
   ⚠️ PAS `--ct-success` / `--ct-danger` : #0acf97 et #fa5c7c sont trop pâles sur fond clair (le vert
   ~2:1, sous les 3:1 d'une icône et les 4,5:1 du texte de la puce mobile). Deux encres MESURÉES,
   une par thème — et le sens reste dit par la flèche de l'icône et son libellé, pas par la seule
   couleur. */
:root { --of-appel-entrant: #157347; --of-appel-sortant: #c62828; }
[data-bs-theme="dark"] { --of-appel-entrant: #4ade80; --of-appel-sortant: #ff8a80; }
.of-appel-entrant, .of-appel-entrant .mdi { color: var(--of-appel-entrant) !important; }
.of-appel-sortant, .of-appel-sortant .mdi { color: var(--of-appel-sortant) !important; }

/* ── Bandeau d'avertissement des testeurs de la DÉMONSTRATION (nav.inc.php, 2026-09-21) ────────────
   Discret mais lisible : teinte d'information du thème, encre du corps de texte (lisible en clair
   comme en sombre), liseré à gauche. Il se place en tête du contenu, au-dessus du ruban du module. */
.of-demo-bandeau {
    display: flex; align-items: flex-start; gap: .6rem;
    margin: .75rem 0 .25rem; padding: .55rem .85rem;
    border-left: 4px solid var(--ct-info); border-radius: .35rem;
    background: rgba(var(--ct-info-rgb), .1); color: var(--ct-body-color);
    font-size: .82rem; line-height: 1.45;
}
.of-demo-bandeau > .mdi { color: var(--ct-info); font-size: 1.1rem; line-height: 1.2; }
.of-demo-bandeau > div { flex: 1 1 auto; }
.of-demo-bandeau .btn-close { flex: 0 0 auto; font-size: .65rem; margin-top: .2rem; }

/* ══ Popup « dossiers rattachés » (assets/js/of_dossier_flags.js) ═══════════════════════════════
   Il héritait de `.card` : même fond que les lignes de la liste des emails, ombre légère, titre
   gris clair — dans les deux thèmes on ne le distinguait pas et il se lisait mal (signalé le
   2026-09-21). Il est désormais cerné et coiffé de la couleur des Dossiers, sur un fond plus
   contrasté que la page, avec une ombre franche. En-tête ambre : encre FONCÉE (≈ 9:1 ; le blanc
   n'y tiendrait que 2:1). La croix de détachement n'apparaît que si le serveur l'autorise. */
#of-doss-pop {
    min-width: 230px; max-width: 360px;
    background: var(--ct-secondary-bg, #fff);
    /* Encre FIXÉE, pas `--ct-emphasis-color` : en thème clair elle vaut un gris à 75 % d'opacité
       (mesuré), précisément le texte délavé qu'il fallait supprimer. */
    color: #212529;
    border: 2px solid var(--color-dossiers, #F59E0B);
    border-radius: 8px;
    box-shadow: 0 12px 32px rgba(0, 0, 0, .28), 0 2px 6px rgba(0, 0, 0, .18);
    overflow: hidden;
    font-size: .875rem;
}
[data-bs-theme="dark"] #of-doss-pop {
    background: var(--ct-tertiary-bg, #2c3035);
    color: #eef1f4;
    box-shadow: 0 12px 32px rgba(0, 0, 0, .65), 0 0 0 1px rgba(255, 255, 255, .06);
}
#of-doss-pop .of-doss-pop-tete {
    background: var(--color-dossiers, #F59E0B);
    color: #1f2328;
    font-weight: 700; font-size: .8rem;
    padding: .4rem .7rem;
}
#of-doss-pop .of-doss-pop-liste { padding: .25rem 0; }
#of-doss-pop .of-doss-ligne {
    display: flex; align-items: center; gap: .35rem;
    padding: 0 .35rem 0 0;
}
#of-doss-pop .of-doss-ligne + .of-doss-ligne { border-top: 1px solid var(--ct-border-color, #dee2e6); }
#of-doss-pop .of-doss-link {
    flex: 1 1 auto; min-width: 0;
    display: flex; align-items: center; gap: .45rem;
    padding: .45rem .7rem;
    color: inherit; font-weight: 600; text-decoration: none;
}
#of-doss-pop .of-doss-link span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#of-doss-pop .of-doss-link .mdi { color: var(--color-dossiers, #F59E0B); font-size: 1.05rem; }
#of-doss-pop .of-doss-link:hover,
#of-doss-pop .of-doss-link:focus-visible { background: var(--ct-tertiary-bg, #f1f3f5); text-decoration: underline; }
[data-bs-theme="dark"] #of-doss-pop .of-doss-link:hover,
[data-bs-theme="dark"] #of-doss-pop .of-doss-link:focus-visible { background: rgba(255, 255, 255, .07); }
#of-doss-pop .of-doss-retirer {
    flex: 0 0 auto;
    width: 1.75rem; height: 1.75rem;
    display: inline-flex; align-items: center; justify-content: center;
    border: 0; border-radius: 50%; background: transparent;
    color: var(--ct-danger, #fa5c7c); font-size: 1.1rem;
}
#of-doss-pop .of-doss-retirer:hover,
#of-doss-pop .of-doss-retirer:focus-visible { background: var(--ct-danger, #fa5c7c); color: #fff; }
#of-doss-pop .of-doss-ligne.confirmer { flex-wrap: wrap; padding: .45rem .6rem; gap: .4rem; }
#of-doss-pop .of-doss-question { flex: 1 1 100%; font-weight: 600; }
#of-doss-pop .of-doss-ligne.en-cours { opacity: .55; }

/* ══ Recherche dans les fenêtres « Classer dans un dossier » (ofFiltreDossiers, fonctions_communes.js)
   Des milliers de dossiers ne tiennent pas dans une fenêtre : la liste défile, le champ reste. */
.of-liste-dossiers { max-height: 45vh; overflow-y: auto; padding-right: .25rem; }

/* ══ Sous-menu collant des pages à sections (Paramètres généraux) ════════════════════════════════
   Sur une section longue (le catalogue d'articles), le menu des sections partait avec la page : pour
   passer d'« Articles » à « Utilisateurs », il fallait remonter tout en haut (signalé le 2026-09-25).
   Il reste désormais sous la barre supérieure. À partir de 992 px seulement : en dessous, il est
   EMPILÉ au-dessus du contenu — collant, il en masquerait le haut. S'il est plus haut que la
   fenêtre (seize sections sur un petit portable), il défile lui-même au lieu d'être coupé. */
@media (min-width: 992px) {
    .of-sousnav-collante {
        position: sticky;
        top: calc(var(--ct-topbar-height, 70px) + 1rem);
        max-height: calc(100vh - var(--ct-topbar-height, 70px) - 2rem);
        overflow-y: auto;
        z-index: 2;
    }
}

/* ══ Bouton « revenir en haut de la page » (of-shell.js, brancherRetourHaut) ═════════════════════
   N'apparaît qu'après un défilement d'au moins un écran : sur une page courte, il ne serait qu'un
   bouton de plus. Au-dessus du contenu, SOUS les fenêtres modales (1055) et les notifications. */
.of-retour-haut {
    position: fixed;
    /* Collé au bord (12 px) : à 24 px, il recouvrait la colonne des boutons « Supprimer » des
       tableaux qui vont jusqu'au bord de la carte (le catalogue d'articles, mesuré à 1366 px). */
    right: .75rem;
    bottom: 1.5rem;
    z-index: 1030;
    width: 44px; height: 44px;                 /* cible tactile minimale */
    display: inline-flex; align-items: center; justify-content: center;
    border: 0; border-radius: 50%;
    background: var(--ct-primary, #727cf5);
    color: #fff;                               /* 3,55:1 sur le primaire : au-dessus des 3:1 exigés d'une icône */
    font-size: 1.35rem;
    box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .25);
    opacity: .9;
    transition: opacity .15s ease-in-out, transform .15s ease-in-out;
}
.of-retour-haut:hover, .of-retour-haut:focus-visible { opacity: 1; transform: translateY(-2px); }
.of-retour-haut:focus-visible { outline: 3px solid var(--ct-primary-bg-subtle, #c8cbfb); outline-offset: 2px; }
.of-retour-haut[hidden] { display: none; }
/* Sous 768 px, la barre d'onglets occupe le bas de l'écran : le bouton se pose au-dessus. */
@media (max-width: 767.98px) {
    .of-retour-haut { bottom: calc(var(--m-tabbar-h, 64px) + env(safe-area-inset-bottom) + 1rem); right: 1rem; }
}
@media (prefers-reduced-motion: reduce) {
    .of-retour-haut { transition: none; }
    .of-retour-haut:hover, .of-retour-haut:focus-visible { transform: none; }
}

/* ── Droit DEMANDÉ, mis en évidence dans les exceptions d'une personne ──────────────────────────
   On arrive sur Droits d'accès par un lien « Accorder les droits » (un message « X n'a pas les
   droits … » d'un autre écran) : les lignes des droits manquants sont soulignées, pour qu'on n'ait
   pas à les chercher parmi une soixantaine (DroitsUI.ouvrirPour). */
.of-droit-cible {
    border-radius: 6px;
    background: color-mix(in srgb, var(--ct-warning, #ffbc00) 14%, transparent);
    box-shadow: 0 0 0 2px var(--ct-warning, #ffbc00);
}
/* Le « Comment faire » d'un message de droits manquants : une icône, l'aide au survol. */
.of-comment-faire { cursor: help; color: var(--of-encre-attenuee, #6c757d); }
