/*------------------------------------------------------------------
Theme : Default
[Table of contents]

1.
2. Quickview
3. Sizes
4. Tables
5. Reports
6. Left Sidebar
9. Grid stack
10. DevExpress Grid
11. DevExpress Treeview
15. DevExpress Tabs from blazering template
16. DevExpress Modal
17. DevExpress Windows
18. DevExpress drawer
19. DevExpress graph
20. Web Reports
22. Infobulle de marqueur — carte partagée 2D/3D
23. Explorers — shell + primitives partagées
24. Marqueurs de valeur — overlay permanent sur la carte
25. Boutons — géométrie non exprimable en variables Bootstrap
26. Logo — emplacements de marque pilotés par tokens
27. Cartes — ce que Bootstrap ne porte pas
28. Shell « boxed » — sidebar détachée, décalage du contenu
29. Échelle de tailles de texte — utilitaires .fs-*
30. Page de connexion
31. Onglets — le composant NavLinks
32. Impression
33. Listes
34. Barres de progression
35. Carte — rouet de rafraîchissement
36. Badges
37. Helpers de position et de débordement
38. Helpers de bordure
39. Gouttière resserrée
40. Cases à cocher, boutons radio et interrupteurs
41. Typographie
42. Ajustements sous 768 px
43. Shell hérité — menu, en-tête, conteneur (placé EN TÊTE du fichier)
44. Formulaires hérités (placé EN TÊTE, juste après le 43)
46. Rail d'outils — la boîte de droite et son panneau collé
47. Bandeaux applicatifs (AppDock) — réservation de place
48. SweetAlert — espacement des boutons
-------------------------------------------------------------------*/



/*------------------------------------------------------------------
[43. Shell hérité — menu, en-tête, conteneur]

Déménagée de theme.css (section « Core Layout »), moins une règle morte.

⚠️ CONTRAIREMENT À LA COUCHE RESPONSIVE (§ 42), CELLE-CI EST BIEN VIVANTE.
Même protocole : le shell a été relevé sur 12 éléments et ~60 propriétés, la
section retirée en entier, puis re-relevé. Résultat : 18 ÉCARTS, dont le fond
sombre du menu (rgb(43,48,59) → transparent), le fond blanc de l'en-tête, le
z-index du menu (1000 → auto), la hauteur du conteneur, et des PUCES qui
réapparaissent dans le menu (list-style disc, retrait de 32px). Le § 28 du
lot 4 repositionne le shell, il ne le peint pas : les deux sont complémentaires,
pas redondants.

D'abord déménagée à l'identique, cette section a ensuite été RÉSORBÉE contre le
§ 28 — c'est ce qui suit.

CE QUI A ÉTÉ RETIRÉ, ET COMMENT ON L'A SU.
Chaque déclaration de la section a été retirée à chaud, une par une, dans la
feuille vivante, et les porteurs re-relevés : celles dont le retrait ne déplace
AUCUNE valeur calculée sont masquées par le § 28. Le test a tourné dans trois
conditions (1400 px menu déplié, 1400 px replié, 900 px) et seules les
déclarations masquées dans les TROIS ont été retenues : 110 sur 221.
63 ont pu être retirées effectivement — l'écart tient aux raccourcis, une
déclaration écrite `margin: 0` ne partant que si ses quatre valeurs longues
sont masquées.

CE QUE LA MÉTHODE NE PEUT PAS TRANCHER, et qui reste donc en place :
  - les sélecteurs à :hover / :focus / :active / :visited, et les
    pseudo-éléments : la sonde les neutralise pour pouvoir interroger le DOM,
    elle mesure donc l'élément au repos et ne conclut rien sur l'état. 10
    déclarations dans ce cas, dont le `background-color: transparent !important`
    qui oblige encore le § 28 à un !important sur la pastille de compte ;
  - tout ce qui vit sous @media : le contexte doit être actif pour que le test
    ait un sens. Le script de résorption les a d'ailleurs écartées explicitement
    — une première version ne le faisait pas et a retiré le `height: 48px` du
    .header mobile, rattrapé au diff avant écriture.

VÉRIFICATION. Relevé A/B complet — 23 éléments, 40 propriétés, menu déplié et
replié — à 1400, 900 et 375 px, en rechargeant la feuille depuis le disque
entre les deux versions : 0 écart sur 1840 mesures à chaque largeur.
Un seul retour en arrière : `overflow: auto` sur .menu-items, que la sonde
croyait masquée parce qu'elle retire overflow-x et overflow-y séparément alors
que la déclaration écrite est le raccourci. C'est le défilement du menu ; le
relevé A/B l'a rattrapé (seul écart des 1840), il est remis avec sa mise en
garde sur place.

⚠️ CE QUE CETTE SECTION N'EST PAS : un doublon du § 28. La coupe intégrale a
été essayée d'abord — 78 écarts, dont le fond sombre du menu, le fond blanc de
l'en-tête, le z-index 1000, les puces qui reviennent dans le menu et la hauteur
du conteneur. Le § 28 POSITIONNE le shell, le § 43 le PEINT. Les deux restent.

Retiré comme mort : `.header > .pull-left, .header > .pull-right` — .pull-left
et .pull-right sont du vocabulaire Bootstrap 3, remplacé par .float-start /
.float-end en v4, et plus aucun élément ne les porte.
*/

html {
  height: 100%;
}

/* Body Triggers for layout options
------------------------------------------------------------------
*/
body {
  height: 100%;
  -moz-osx-font-smoothing: grayscale;
}





body.dashboard {
  background: var(--page-bg-color);
}

body.dashboard .page-container {
  background: var(--page-bg-color);
}

body.rtl [class^="col-"],
body.rtl [class*="col-"] {
  float: left;
}

    body.menu-pin .overlay {
        padding-left: 30px;
    }



/* Page Container
------------------------------------
*/
.page-container {
  height: 100%;
}

.page-container .page-content-wrapper {
  min-height: 100%;
  position: relative;
}

    .page-container .page-content-wrapper .content {
        /* Content holder */
        z-index: 10;
        /*padding-bottom: 100px;*/
        min-height: 100%;
        -webkit-transition: all 0.3s ease;
        transition: all 0.3s ease;
    }

.page-container .page-content-wrapper .content.full-width {
  width: 100%;
}










.page-title {
  margin-top: 0px;
}

















/* Other overides */
.container-fluid {
  position: relative;
}





@media (min-width: 1400px) {
  .container {
    max-width: 1140px;
  }
}

/* SPLIT VIEW FOR EMAIL and APPS
------------------------------------
*/














































/* Horizontal Menu
------------------------------------
*/






























































































































































































































/* La peinture de l'ancienne barre supérieure (.header et ses descendants :
   .bubble, .notification-list, ul li, .dropdown-menu) est partie avec elle.
   Zéro porteur mesuré dans le dépôt, modules clients compris. */

/* Bootstrap navbar
------------------------------------
*/




.nav > li > a {
  padding: 10px 15px;
}

















/* Header Responsive Classes
------------------------------------
*/

/* Le bloc mobile de .header est parti avec la barre supérieure. */



/* Breadcrumbs
------------------------------------
*/


















/* Main Menu Sidebar
------------------------------------
*/
















.page-sidebar {
  background-color: var(--kd-nav-bg);
  z-index: 1000;
  -webkit-transition: -webkit-transform 400ms cubic-bezier(0.05, 0.74, 0.27, 0.99);
  -moz-transition: -moz-transform 400ms cubic-bezier(0.05, 0.74, 0.27, 0.99);
  -o-transition: -o-transform 400ms cubic-bezier(0.05, 0.74, 0.27, 0.99);
  -webkit-backface-visibility: hidden;
  -webkit-perspective: 1000;
}


/* ⚠️ NE PAS RETIRER — TOUTE LA COULEUR DE REPOS DU MENU TIENT À CETTE RÈGLE.
   Retirée par erreur à la résorption du § 43 (232e75f4), remise le 18/08/2026.
   Sans elle un lien du menu n’a AUCUNE couleur propre : il tombe sur le
   `a { color: var(--primary-color) }` du § 41 et s’affiche EN VERT.

   POURQUOI PERSONNE NE L’A VU TOUT DE SUITE. La règle `:visited` plus bas
   repeint en gris tout lien DÉJÀ VISITÉ : seuls les items jamais ouverts
   viraient au vert, et pas les mêmes d’un poste à l’autre. Les icônes
   restaient grises de leur côté — .icon-thumbnail porte sa propre couleur —
   ce qui donnait des libellés verts sur des pictos gris.

   POURQUOI LA SONDE DE MASQUAGE S’EST TROMPÉE. Elle neutralise les
   pseudo-classes dynamiques pour pouvoir interroger le DOM : `.page-sidebar
   a:visited` devient alors un `.page-sidebar a` situé PLUS BAS dans le
   fichier, qui masque celui-ci. Le masquage était un artefact de la sonde.
   ⚠️ Et getComputedStyle() ne peut pas rattraper l’erreur : par protection de
   la vie privée il renvoie TOUJOURS le style non-visité. Un relevé A/B ne voit
   donc jamais l’effet de `:visited` — c’est l’angle mort de tout le protocole.

   Les valeurs passent par les jetons : c’est ce qui permettra à un habillage
   client de repeindre le menu (cf. docs/architecture/habillage-client.md). */
.page-sidebar a,
.page-sidebar button {
    color: var(--kd-nav-fg);
}

.page-sidebar a:hover, .page-sidebar a:active,
.page-sidebar button:hover,
.page-sidebar button:active {
  color: var(--kd-nav-fg-active);
  background-color: transparent !important;
}

.page-sidebar a:hover:focus, .page-sidebar a:active:focus,
.page-sidebar button:hover:focus,
.page-sidebar button:active:focus {
  color: var(--kd-nav-fg-active);
}

/* Le `:focus` de ce bloc porte, indépendamment du `:visited` : sans lui le
   `a:focus { color: var(--secondary-color) }` du § 41 l’emporterait sur la
   règle de repos ci-dessus — même spécificité (0,1,1), mais écrite plus bas —
   et un item atteint au clavier passerait au vert. */
.page-sidebar a:visited, .page-sidebar a:focus,
.page-sidebar button:visited,
.page-sidebar button:focus {
  color: var(--kd-nav-fg);
}

.page-sidebar .sidebar-header {
  /* Side-bar header */
  line-height: 60px;
  border-bottom: 1px solid var(--kd-nav-border);
  color: var(--kd-nav-fg-active);
  clear: both;
  z-index: 10;
  position: relative;
  user-select: none;
}

/* Retiré avec le panneau #appMenu et son chevron (cf. NavBar.razor) :
   .sidebar-header-controls, .sidebar-slide-toggle et .sidebar-overlay-slide
   n'ont plus aucun porteur dans le markup. */


.page-sidebar .sidebar-menu {
  /* Side-bar Menu */
  position: relative;
}



.page-sidebar .sidebar-menu .menu-items {
  /* Side-bar Menut Items */
  list-style: none;
  margin: 0;
  padding: 0;
  margin: 0;
  padding: 0;
  position: relative;
  /* ⚠️ REMIS APRÈS COUP. La sonde de résorption l'avait cru masquée : elle
     retire overflow-x et overflow-y séparément, alors que la déclaration
     écrite est le RACCOURCI — le relevé A/B a rattrapé l'erreur (overflow
     auto -> visible, seul écart sur 1840 mesures). C'est le défilement du
     menu quand les entrées dépassent la hauteur de la boîte. */
  overflow: auto;
  -webkit-overflow-scrolling: touch;
}

.page-sidebar .sidebar-menu .menu-items li:hover > .icon-thumbnail, .page-sidebar .sidebar-menu .menu-items li.open > .icon-thumbnail, .page-sidebar .sidebar-menu .menu-items li.active > .icon-thumbnail {
  color: var(--kd-nav-fg-active);
}

.page-sidebar .sidebar-menu .menu-items li:hover > a, .page-sidebar .sidebar-menu .menu-items li.open > a, .page-sidebar .sidebar-menu .menu-items li.active > a {
  color: var(--kd-nav-fg-active);
}

.page-sidebar .sidebar-menu .menu-items li > a {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.page-sidebar .sidebar-menu .menu-items > li {
  display: block;
  clear: right;
}

.page-sidebar .sidebar-menu .menu-items > li:after, .page-sidebar .sidebar-menu .menu-items > li:before {
  display: table;
  content: " ";
  clear: both;
}

    .page-sidebar .sidebar-menu .menu-items > li > a {
        font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif, sans-serif;
        line-height: 40px;
        clear: both;
    }

.page-sidebar .sidebar-menu .menu-items > li > a.open {
  background: #313131;
}

.page-sidebar .sidebar-menu .menu-items > li > a > .arrow {
  float: right;
  padding-right: 10px;
}

.page-sidebar .sidebar-menu .menu-items > li > a > .arrow:before {
  float: right;
  display: inline;
  font-size: 16px;
  font-family: "pages-icon";
  height: auto;
  content: "\e989";
  font-weight: 300;
  text-shadow: none;
  -webkit-transition: all 0.12s ease;
  transition: all 0.12s ease;
}

.page-sidebar .sidebar-menu .menu-items > li > a > .arrow.open:before {
  -webkit-transform: rotate(-90deg);
  -ms-transform: rotate(-90deg);
  transform: rotate(-90deg);
}

.page-sidebar .sidebar-menu .menu-items > li > a > .badge {
  margin-top: 13px;
}








.page-sidebar .sidebar-menu .menu-items > li.active > ul.sub-menu {
  display: block;
}

.page-sidebar .sidebar-menu .menu-items > li > .icon-thumbnail:first-letter {
  text-transform: uppercase;
}

/* Retiré : la géométrie du 3e niveau (padding-left 10 / icônes 25 px) pesait
   plus lourd que les règles de sous-menu d'app.css §28 et remettait l'ancien
   design en dessous du 2e niveau. Le rail traite désormais tous les niveaux
   avec la même grammaire. */

.page-sidebar .sidebar-menu .menu-items > li > ul.sub-menu li > a > .arrow:before {
  float: right;
  margin-top: 1px;
  margin-right: 20px;
  display: inline;
  font-size: 16px;
  font-family: "pages-icon";
  content: "\e989";
  font-weight: 300;
  text-shadow: none;
  -webkit-transition: all 0.12s ease;
  transition: all 0.12s ease;
}

.page-sidebar .sidebar-menu .menu-items > li > ul.sub-menu li > a > .arrow.open:before {
  float: right;
  margin-top: 1px;
  margin-right: 18px;
  display: inline;
  font-size: 16px;
  -webkit-transform: rotate(-90deg);
  -ms-transform: rotate(-90deg);
  transform: rotate(-90deg);
  font-weight: 300;
  text-shadow: none;
}

.page-sidebar .sidebar-menu .menu-items > li ul.sub-menu {
  display: none;
  list-style: none;
  clear: both;
  margin: 0 0 10px 0;
  background-color: #21252d;
  padding: 18px 0 10px 0;
}

.page-sidebar .sidebar-menu .menu-items > li ul.sub-menu > li {
  background: none;
  padding: 0px 20px 0 40px;
  margin-top: 1px;
}

.page-sidebar .sidebar-menu .menu-items > li ul.sub-menu > li:hover > .icon-thumbnail {
  color: var(--kd-nav-fg-active);
}

.page-sidebar .sidebar-menu .menu-items > li ul.sub-menu > li > a {
  display: inline-block;
  padding: 5px 0px;
  font-size: 12px;
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif, sans-serif;
  white-space: normal;
}

.page-sidebar .sidebar-menu .menu-items > li ul.sub-menu > li .icon-thumbnail {
  width: 30px;
  height: 30px;
  line-height: 30px;
  margin: 0;
  background-color: var(--kd-nav-bg);
  font-size: 12px;
}

.page-sidebar .sidebar-menu .muted {
  color: rgba(33, 33, 33, 0.62);
}

/* [data-toggle-pin] retiré : plus aucun élément ne porte cet attribut depuis
   que le bouton d'épinglage a cédé la place à la pastille .kd-sidebar-toggle,
   qui porte sa propre icône Phosphor (app.css §28). */

/* Sidebar icon holder
------------------------------------
*/
.icon-thumbnail {
    /*width: 40px;*/
    line-height: 40px;
    text-align: center;
    vertical-align: middle;
    color: var(--kd-nav-fg);   /* même gris que les libellés (§ 43) : .icon-thumbnail
                                  n’est porté QUE par le menu (NavBarMenuLink /
                                  NavBarSubMenu), et c’est lui qui a gardé les
                                  pictos gris pendant que les libellés viraient
                                  au vert. */
    -webkit-transition: -webkit-transform 0 0.4s cubic-bezier(0.05, 0.74, 0.27, 0.99);
    transition: transform 0 0.4s cubic-bezier(0.05, 0.74, 0.27, 0.99);
    -webkit-backface-visibility: hidden;
    -webkit-perspective: 1000;
    -webkit-text-size-adjust: 100%;
    -ms-text-size-adjust: 100%;
    font-weight: bold;
}

    /* --kd-menu-icon (18 px). Revenue à 16 px un temps parce que 18 pesaient plus
       lourd que des libellés de 12 : elle remonte AVEC eux, passés à 13 px
       (14/09/2026). Et c'est cette règle qui fixe AUSSI la taille des icônes de
       sous-menu : elle bat les 12 px que theme.css leur prévoyait. */
    .icon-thumbnail > i {
        font-size: var(--kd-menu-icon);
    }

.icon-thumbnail > svg {
  width: auto;
  height: auto;
  display: inline;
}

/* Sidebar Responsive Classes
------------------------------------
*/


/* Bloc @media (min-width:980px) vidé : ses trois règles compensaient la
   géométrie de l'ancien rail (sidebar de 280 px calée à left:-210px).
   - [data-toggle-pin] : plus aucun élément ne porte cet attribut, le bouton
     d'épinglage ayant été remplacé par la pastille .kd-sidebar-toggle ;
   - translateX(30px) sur .sidebar-header-controls : poussait le chevron HORS de
     la boîte (mesuré : 251→279 pour un en-tête qui s'arrête à 270), donc rogné
     par l'overflow:hidden, et juste sous la pastille ;
   - translate3d(-13px) sur les icônes : déjà neutralisé par app.css §28. */

@media (max-width: 575px) {
  .page-sidebar {
    width: 250px;
    z-index: auto;
    left: 0 !important;
    -webkit-transform: translate3d(0, 0px, 0px) !important;
    transform: translate3d(0, 0px, 0px) !important;
  }
  .page-sidebar .sidebar-menu .menu-items > li ul.sub-menu > li {
    padding: 0px 30px 0 36px;
  }
  .icon-thumbnail {
    margin-right: 24px;
  }
  .page-sidebar {
    display: none;
    z-index: auto;
  }
  .page-sidebar.visible {
    display: block;
  }
}

@media (min-width: 768px) and (max-width: 1024px) and (orientation: landscape) {
  .page-sidebar .sidebar-menu .menu-items ul {
    -webkit-transform: translateZ(0);
    -webkit-backface-visibility: hidden;
    -webkit-perspective: 1000;
  }
}

@media only screen and (min-device-width: 768px) and (max-device-width: 1024px) and (orientation: portrait) {
  .page-sidebar .sidebar-menu .menu-items > li > a {
    padding-left: 27px;
  }
  .page-sidebar .sidebar-menu .menu-items > li ul.sub-menu > li {
    padding: 0px 27px 0 31px;
  }
}

@media (max-width: 575px) {
  .sidebar-menu .icon-thumbnail {
    margin-right: 15px;
  }
}

@media (max-width: 767px) {
  .page-sidebar .sidebar-header {
    padding: 0 13px;
  }
  .page-sidebar .sidebar-menu .menu-items > li > a {
    padding-left: 20px;
  }
  .page-sidebar .sidebar-menu .menu-items > li ul.sub-menu > li {
    padding: 0px 25px 0 28px;
  }
}

@media (max-width: 480px) {
  .page-sidebar .sidebar-header {
    height: 48px;
    line-height: 48px;
  }
}

/* END Sidebar
------------------------------------
*/
/* Overlay Search
------------------------------------
*/
.overlay {
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  background: rgba(255, 255, 255, 0.95);
  z-index: 1010;
  padding-left: 64px;
  overflow: auto;
}

.overlay .inline-block {
  display: inline-block;
}



.overlay > div {
  width: 100%;
  height: 260px;
  display: block;
  overflow: hidden;
}



    





/* Quick View
------------------------------------
*/










































































































/* Pages Scroll bar
------------------------------------
*/
.scroll {
  position: relative;
  overflow: auto;
}



































/* hover & drag */



/*------------------------------------------------------------------
[44. Formulaires hérités]

Dernière section de theme.css, qui disparaît avec elle.

Placée ICI, entre le § 43 et le § 28, pour reproduire exactement l'ordre
d'origine : dans theme.css, « Core Layout » précédait « Form Elements », et
l'ensemble était chargé AVANT app.css. Déplacer ce bloc plus bas rendrait à ces
règles un arbitrage qu'elles n'ont jamais gagné (cf. le commit de la tranche 9,
où un simple ajout en fin de fichier a ressuscité l'ancien shell).

L'essentiel est `.form-group-default` : le champ « étiquette au-dessus, dans
la boîte » du thème, avec sa bordure qui vire au vert au focus. Il n'a que
3 porteurs, mais c'est un composant à part entière, sans équivalent Bootstrap.

Le reste surcharge les champs Bootstrap (hauteur 32px, rayon 2px, corps 12px,
état désactivé à 33% d'opacité sur fond #fafafa). À confronter aux jetons
--bs-form-* au lot des primitives : c'est le même schéma que les badges et les
barres de progression, où theme.css masquait ce que tokens.css décrivait.

Retiré comme mort : la famille `.has-warning` (4 règles — aucun élément ne
porte has-warning, has-error ni has-success ; ce sont les états de validation
de Bootstrap 3, remplacés par .is-invalid / .is-valid en v4, et Blazor pose de
son côté `modified valid`/`invalid`), et le bloc Quill (`.ql-container`,
`.ql-editor`, `#quill` — l'éditeur Quill n'est nulle part dans le dépôt).

⚠️ `.has-error` et `.has-success` sont CONSERVÉES : aucun porteur non plus,
mais l'audit statique les voit encore citées quelque part et je n'ai pas pu
lever le doute. À trancher avec le reste des états de validation.
*/

/* LOT C — LE VERROU DE POLICE DU THÈME EST LEVÉ.
   Cette règle s'écrivait `font-size: 12px !important`. Deux conséquences, et
   les deux sont des pannes :
     - AUCUN habillage client ne pouvait passer : 12 px en dur, sans variable ;
     - toute taille posée sur un <button>, un <input> ou un <label> était
       IGNORÉE, où qu'elle soit écrite, y compris dans un CSS scopé Blazor
       (l'attribut [b-xxxx] monte la spécificité, pas la priorité : un
       !important d'élément bat une règle de classe scopée).
   La valeur ne change pas — --kd-fs-base vaut .75rem, soit les mêmes 12 px —
   mais elle devient pilotable, et elle cesse d'écraser tout le monde.

   ⚠️ LA RÈGLE PERD AUSSI SES DUELS. En 0-0-1 sans !important, elle cède à
   toute règle de classe. Le cadrage a été refait règle par règle sur les trois
   feuilles chargées avant celle-ci (Bootstrap, thème DevExpress, tokens.css).
   DEUX familles auraient GROSSI et sont re-épinglées explicitement, chacune
   près de sa section :
       .form-select              1rem    -> 16 px  17 fichiers  § 44, ci-dessous
       .dxbl-text-edit (input)   .875rem -> 14 px  TOUS les éditeurs DevExpress,
                                                   dont l'input a `font-size:
                                                   inherit`                § 10
   Les fausses alertes, vérifiées et écartées : `.btn-sm` (tokens.css pose
   --bs-btn-font-size sur `.btn`, chargé APRÈS Bootstrap, donc il gagnait déjà
   sur les 0.875rem de `.btn-sm`), `.form-control-sm` (même raisonnement avec le
   `.form-control` de cette section), `.input-group-text` (tenu ici même), et
   `.input-group-sm`, `.accordion-button`, `.col-form-label-*`, sans aucun
   porteur dans le dépôt.
   Elle continue en revanche de battre le reset de Bootstrap
   (`button, input, … { font-size: inherit }`, même spécificité, chargé avant) :
   un bouton posé dans un titre ne se met donc pas à grandir par héritage.

   ⚠️ CE QUI REMONTE VOLONTAIREMENT, et qui est à regarder à l'écran : une
   vingtaine de tailles écrites à la main sur des <button>/<input> reprennent
   enfin effet. La liste mesurée est dans le rapport du lot ; ce sont pour
   l'essentiel des boutons d'icône des explorateurs et des bandeaux d'alarme,
   dont le glyphe était rabattu à 12 px dans une pastille de 30 à 40. */
label,
input,
button,
select,
textarea {
  font-size: var(--kd-fs-base);
  font-weight: normal;
  line-height: 20px;
}

label {
  font-family: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif, sans-serif;
  letter-spacing: -0.006em;
}

select[multiple],
select[size] {
  height: auto !important;
}

input:focus,
select:focus,
textarea:focus,
input[type="file"]:focus,
input[type="radio"]:focus,
input[type="checkbox"]:focus {
  outline: none;
  -webkit-box-shadow: none;
  box-shadow: none;
}

input:-webkit-autofill, input.error:-webkit-autofill {
  -webkit-box-shadow: 0 0 0 1000px #fff inset !important;
}

input:-webkit-autofill:focus, input.error:-webkit-autofill:focus {
  -webkit-box-shadow: 0 0 0 1000px #fff inset !important;
}

form legend {
  margin: 15px 0px 10px 0px;
}

.form-control {
  background-color: #fff;
  background-image: none;
  border: 1px solid rgba(33, 33, 33, 0.14);
  border-top-color: rgba(33, 33, 33, 0.21);
  font-family: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif, sans-serif;
  -webkit-appearance: none;
  color: #212121;
  outline: 0;
  padding: 6px 8px;
  line-height: 16px;
  /* Jeton, pas 12px en dur : sinon le champ de saisie est le seul contrôle que
     l'échelle de texte d'un habillage client ne peut pas atteindre. Valeur
     identique (--kd-fs-base = .75rem = 12px), cf. `.form-select` juste dessous. */
  font-size: var(--kd-fs-base);
  font-weight: normal;
  vertical-align: middle;
  min-height: 32px;
  -webkit-transition: all 0.12s ease;
  transition: all 0.12s ease;
  border-radius: 2px;
  -webkit-border-radius: 2px;
  -moz-border-radius: 2px;
  -webkit-transition: background 0.2s linear 0s;
  transition: background 0.2s linear 0s;
}

/* LOT C — épinglage rendu nécessaire par la levée du verrou de police
   (ci-dessus). `.form-control` tenait déjà sa taille en propre, quelques lignes
   plus haut ; `.form-select` non — il n'était tenu que par le !important, et
   serait remonté au 1rem d'usine de Bootstrap, soit 16 px sur 17 fichiers.
   Les variantes -sm / -lg sont couvertes par la même règle : elles ne changent
   que le rembourrage chez nous. */
.form-select,
.form-select-sm,
.form-select-lg {
  font-size: var(--kd-fs-base);
}

.form-control::-moz-placeholder {
  color: inherit;
  opacity: 0.47;
  font-weight: 400;
  font-family: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
}

.form-control:-ms-input-placeholder {
  color: inherit;
  opacity: 0.47;
  font-weight: 400;
  font-family: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
}

.form-control::-webkit-input-placeholder {
  color: inherit;
  opacity: 0.47;
  font-weight: 400;
  font-family: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
}

.form-control::placeholder {
  color: inherit;
  opacity: 0.47;
  font-weight: 400;
  font-family: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
}

.form-control:focus {
  border-color: unset;
  background-color: #fff;
  border: 1px solid var(--primary-color);
  box-shadow: none;
  outline: 0 !important;
}

.form-control:focus::-moz-placeholder {
  color: inherit;
  opacity: 0.57;
  font-weight: 400;
  font-family: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
}

.form-control:focus:-ms-input-placeholder {
  color: inherit;
  opacity: 0.57;
  font-weight: 400;
  font-family: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
}

.form-control:focus::-webkit-input-placeholder {
  color: inherit;
  opacity: 0.57;
  font-weight: 400;
  font-family: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
}

.form-control:focus::placeholder {
  color: inherit;
  opacity: 0.57;
  font-weight: 400;
  font-family: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
}

.form-control[disabled], .form-control[readonly],
fieldset[disabled] .form-control {
  background: #fafafa;
  color: rgba(75, 75, 75, 0.33);
  cursor: not-allowed;
}

/* Input Sizes
--------------------------------------------------
*/

.input-group-text {
  background-color: #fff;
  color: rgba(75, 75, 75, 0.67);
  font-size: 12px;
  line-height: 13px;
  border: 1px solid rgba(33, 33, 33, 0.14);
  border-top-color: rgba(33, 33, 33, 0.21);
  padding-left: 10px;
  padding-right: 10px;
  border-radius: 2px;
}

.input-group-text.primary {
  background-color: var(--primary-color);
  color: var(--kd-on-primary);
}

.input-group-text.info {
  background-color: #3b4752;
  color: #fff;
}

.input-group-text.warning {
  background-color: #ffd945;
  color: #4b4b4b;
}

.input-group-text.danger {
  background-color: #d83c31;
  color: #fff;
}

/* Form layouts
--------------------------------------------------
*/
@media only screen and (min-width: 540px) {
  form .row {
    margin-left: 0;
    margin-right: 0;
  }
  form .row [class*="col-sm-"]:not(:first-child), form .row [class*="col-sm-"]:not(:last-child) {
    padding-right: 7px;
    padding-left: 7px;
  }
  form .row [class*="col-sm-"]:first-child {
    padding-left: 0;
  }
  form .row [class*="col-sm-"]:last-child {
    padding-right: 0;
  }
}

@media only screen and (min-width: 720px) {
  form .row {
    margin-left: 0;
    margin-right: 0;
  }
  /*form .row [class*="col-"]:not(:first-child), form .row [class*="col-"]:not(:last-child) {
    padding-right: 0;
    padding-left: 0;
  }*/
  form .row [class*="col-"]:first-child {
    padding-left: 0;
  }
  form .row [class*="col-"]:last-child {
    padding-right: 0;
  }
  form .row [class*="col-md-"]:not(:first-child), form .row [class*="col-md-"]:not(:last-child) {
    padding-right: 7px;
    padding-left: 7px;
  }
  form .row [class*="col-md-"]:first-child {
    padding-left: 0;
  }
  form .row [class*="col-md-"]:last-child {
    padding-right: 0;
  }
}

@media only screen and (min-width: 960px) {
  form .row {
    margin-left: 0;
    margin-right: 0;
  }
  form .row [class*="col-lg-"]:not(:first-child), form .row [class*="col-lg-"]:not(:last-child) {
    padding-right: 7px;
    padding-left: 7px;
  }
  form .row [class*="col-lg-"]:first-child {
    padding-left: 0;
  }
  form .row [class*="col-lg-"]:last-child {
    padding-right: 0;
  }
}

@media only screen and (min-width: 1200px) {
  form .row {
    margin-left: 0;
    margin-right: 0;
  }
  form .row [class*="col-xl-"]:not(:first-child), form .row [class*="col-xl-"]:not(:last-child) {
    padding-right: 7px;
    padding-left: 7px;
  }
  form .row [class*="col-xl-"]:first-child {
    padding-left: 0;
  }
  form .row [class*="col-xl-"]:last-child {
    padding-right: 0;
  }
}

/* Form layouts  : Horizontal
--------------------------------------------------
*/
.form-horizontal .form-group {
  border-bottom: 1px solid #e0e0e0;
  padding-top: 13px;
  padding-bottom: 13px;
  margin-bottom: 0;
}

.form-horizontal .form-group:last-child {
  border-bottom: none;
}

.form-horizontal .radio {
  padding-top: 7px;
  margin-top: 0;
  margin-bottom: 0;
}

/* Form layouts  : Attached
--------------------------------------------------
*/

/* Form layouts  : Responsive Handlers
--------------------------------------------------
*/

/* Form Groups
--------------------------------------------------
*/
.form-group {
  margin-bottom: 10px;
}

.form-group label:not(.error) {
  color: #4b4b4b;
  transition: all 150ms cubic-bezier(0.4, 0, 0.2, 1), color 150ms cubic-bezier(0.4, 0, 0.2, 1);
}

.form-group label .help {
  margin-left: 8px;
}

.form-group .help {
  font-size: 12px;
  color: rgba(33, 33, 33, 0.67);
  letter-spacing: normal;
  line-height: 18px;
  display: block;
  margin-top: 6px;
  margin-left: 3px;
}

.form-group-default {
  background-color: #fff;
  font-family: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
  position: relative;
  border: 1px solid rgba(33, 33, 33, 0.14);
  border-top-color: rgba(8, 7, 7, 0.14);
  border-radius: 2px;
  padding-top: 5px;
  padding-left: 9px;
  padding-right: 9px;
  padding-bottom: 4px;
  overflow: hidden;
  width: 100%;
  -webkit-transition: background-color 0.2s ease;
  transition: background-color 0.2s ease;
  cursor: text;
}

.form-group-default.required:after {
  color: #d83c31;
  content: "*";
  font-family: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
  font-size: 20px;
  position: absolute;
  right: 10px;
  top: 2px;
}

.form-group-default.disabled {
  background: #fafafa;
  color: rgba(75, 75, 75, 0.23);
  cursor: not-allowed;
}

.form-group-default.disabled.focused {
  border: 1px solid var(--primary-color);
}

.form-group-default.disabled.focused label {
  opacity: 1;
}

.form-group-default.disabled label {
  opacity: 0.57;
  cursor: not-allowed;
}

.form-group-default.focused {
  border: 1px solid var(--primary-color) !important;
}

.form-group-default.focused label {
  opacity: 0.67;
  -webkit-backface-visibility: hidden;
  -moz-backface-visibility: hidden;
  backface-visibility: hidden;
  font-size: 12px;
  letter-spacing: normal;
}

.form-group-default.input-group {
  padding: 0;
}

.form-group-default.input-group > label {
  margin-top: 6px;
  padding-left: 13px;
}

.form-group-default.input-group > .form-control {
  margin-top: -2px;
  margin-bottom: 3px;
  padding-left: 13px;
}

.form-group-default.input-group label {
  margin-top: 6px;
  padding-left: 13px;
}

.form-group-default.input-group .form-control {
  margin-top: -2px;
  margin-bottom: 3px;
  padding-left: 13px;
  width: 100%;
}

.form-group-default.input-group .input-group-text {
  border: 0;
  border-radius: 0;
  min-width: 50px;
}

.form-group-default.input-group .input-group-text i {
  width: 100%;
}

.form-group-default.input-group .btn {
  border-top: 0;
  border-bottom: 0;
}

.form-group-default .form-control {
  border: none;
  height: 25px;
  min-height: 25px;
  padding: 0;
  margin-top: -4px;
  background: none;
  -webkit-box-shadow: none;
  box-shadow: none;
}

.form-group-default .form-control:focus {
  background: none;
}

.form-group-default .form-control:focus::-moz-placeholder {
  color: inherit;
  opacity: 0.77;
  font-weight: 400;
  font-family: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
}

.form-group-default .form-control:focus:-ms-input-placeholder {
  color: inherit;
  opacity: 0.77;
  font-weight: 400;
  font-family: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
}

.form-group-default .form-control:focus::-webkit-input-placeholder {
  color: inherit;
  opacity: 0.77;
  font-weight: 400;
  font-family: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
}

.form-group-default .form-control:focus::placeholder {
  color: inherit;
  opacity: 0.77;
  font-weight: 400;
  font-family: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
}

.form-group-default .form-control:-webkit-autofill, .form-group-default .form-control.error:-webkit-autofill {
  -webkit-box-shadow: 0 0 0 1000px #fff inset !important;
}

.form-group-default .form-control:-webkit-autofill:focus, .form-group-default .form-control.error:-webkit-autofill:focus {
  -webkit-box-shadow: 0 0 0 1000px #fff inset !important;
}

.form-group-default textarea.form-control {
  padding-top: 5px;
}

.form-group-default label {
  margin: 0;
  display: block;
  opacity: 1;
  cursor: text;
  -webkit-transition: opacity 0.2s ease;
  transition: opacity 0.2s ease;
}

.form-group-default label.fade {
  opacity: 0.5;
  -webkit-backface-visibility: hidden;
  -moz-backface-visibility: hidden;
  backface-visibility: hidden;
  font-size: 12px;
  letter-spacing: normal;
}

/* Form validation
--------------------------------------------------
*/

/* Plugins
--------------------------------------------------
Datepicker
https://github.com/eternicode/bootstrap-datepicker
*/

/* Timepicker
https://github.com/m3wolf/bootstrap3-timepicker
*/

/* Daterangepicker
http://www.dangrossman.info/
*/

/* Select2
http://ivaynberg.github.io/select2/
*/

/* Bootstrap Tags input
https://github.com/timschlechter/bootstrap-tagsinput
*/

/* Quill Editor v1.3.6
https://github.com/HackerWins/summernote
*/

/* Pages SelectFx */
/* Default custom select styles */

/* Placeholder and selected option */

/* Options */

/* Optgroup and optgroup label */

/* more options need more delay declaration */

/* Pages Select  overriding */

/* Dropzone for Angular */

/* end overrides */
/* Demo specifc classes */


/*------------------------------------------------------------------
[28. Shell « boxed » — sidebar détachée, décalage du contenu]

Remplace la géométrie de shell de theme.css. Tout est piloté par les
variables de tokens.css (section « Shell boxed ») : cette section ne fait
que les appliquer.

TROIS RÉGIMES, à ne pas confondre :

  1. DESKTOP REPLIÉ (défaut)    rail d'icônes, contenu décalé de sa largeur
  2. DESKTOP DÉPLIÉ (menu-pin)  boîte large, contenu décalé d'autant
  3. MOBILE (≤991px)            boîte hors écran, elle POUSSE le contenu

DEUX ÉTATS SUR LE BUREAU, PAS TROIS. L'ouverture au survol (body.sidebar-visible)
a été retirée : elle créait un troisième état, transitoire et non choisi, qui
recouvrait le contenu dès que le curseur frôlait le bord gauche — au passage de
la souris vers un onglet, vers la barre de défilement d'un tableau, ou en
revenant du bureau. La bascule est désormais explicite (.kd-sidebar-toggle) et
l'état persiste dans localStorage. Un seul sélecteur d'état subsiste donc :
`body:not(.menu-pin)` = replié.

CE QUI A CHANGÉ PAR RAPPORT À L'ANCIEN SHELL, et pourquoi :

- L'ancien ne réduisait jamais la sidebar. C'était une boîte de 280 px calée
  à `left:-210px` dont seuls les 70 px de droite dépassaient, ramenée par un
  `transform: translate(210px)` déclaré `!important` à DEUX endroits de
  theme.css, plus un `transform` EN LIGNE écrit par le JS au survol. Une
  boîte détachée à coins arrondis ne peut pas fonctionner ainsi : repliée,
  elle doit rester une boîte visible, pas un panneau à moitié hors champ.
  D'où le passage à une largeur animée, et la suppression des trois
  écritures concurrentes de position.

- Le décalage du contenu changeait de porteur selon l'état : `.page-container`
  en replié, `.content` en épinglé. Il est désormais porté par
  `.page-container` seul, dans tous les états, via --kd-shell-offset.

- Les icônes de menu étaient flottées à DROITE, parce que cette colonne de
  droite était justement la bande visible en mode replié. La boîte étant
  maintenant étroite et non plus tronquée, elles passent à GAUCHE, position
  naturelle et lisible dans les deux états.
*/

/* --- La boîte ---------------------------------------------------------- */
.page-sidebar {
    position: fixed;
    /* ⚠️ BORD À BORD. La boîte flottante à coins arrondis a vécu : le menu part du
       bord de la fenêtre et descend du haut au bas, comme le rail des explorateurs
       et le rail d'outils. Ce qui les sépare est un FILET, pas une gouttière — et
       le retrait qui aérait le contenu est passé sur `.content`, où il n'aère plus
       que le contenu. */
    top: 0;
    bottom: 0;
    left: 0;
    right: auto;
    width: var(--kd-sidebar-w);
    /* Colonne flex explicite, à la place du `height: calc(100% - 50px)` que
       theme.css posait sur .sidebar-menu. Ce 50 ne correspondait à rien de
       déclaré — ni la hauteur de l'en-tête (60) ni celle du pied — et figeait la
       répartition : changer la hauteur de l'en-tête faisait déborder le pied hors
       de la boîte. En flex, les trois régions se répartissent d'elles-mêmes. */
    display: flex;
    flex-direction: column;
    /* Ni rayon ni ombre : une surface adossée au bord de la fenêtre n'a rien
       derrière quoi porter une ombre, et un arrondi y creuse une encoche.
       Le filet de droite, lui, est la COUTURE : c'est le même trait de structure
       qui sépare le panneau contextuel de la vue et le rail d'outils du contenu.
       Bord à bord, toutes les surfaces se touchent — c'est lui qui dit où l'une
       finit et où l'autre commence, y compris sur l'ardoise sombre. */
    border-radius: 0;
    box-shadow: none;
    border-right: 1px solid var(--kd-border);
    overflow: hidden;
    /* Annule le transform de l'ancien mécanisme. Nécessaire tant que les
       blocs `body.menu-pin .page-sidebar{transform:… !important}` de
       theme.css n'ont pas disparu — ils sont en !important, donc seul un
       !important les bat. À retirer avec eux. */
    transform: none !important;
    transition: width var(--kd-shell-transition);
}

/* La règle `body:has(.railnav) .page-sidebar { border-radius: 0 }` a disparu : elle
   ne servait qu'à effacer les coins droits du menu là où le rail venait s'y coller.
   Le shell étant bord à bord, le menu n'a plus d'arrondi du tout, et la couture
   vaut partout — pas seulement sur les quatre explorateurs. */

/* --- La pastille de bascule -------------------------------------------- */
/* DANS la boîte, sur la ligne de l'en-tête, dans les deux états.

   ⚠️ ELLE ÉTAIT À CHEVAL sur le bord droit en replié, et ce n'est plus le cas :
   repliée, la boîte ne fait que 72 px et la pastille PREND LA PLACE DU LOGO,
   centrée — c'est ce que demande la maquette, et c'est aussi ce qui permet au
   rail de venir se coller au menu (plus rien ne dépasse du bord droit, donc plus
   de gouttière à réserver pour le débord : voir --kd-shell-offset dans
   tokens.css, qui a perdu son terme --kd-sidebar-toggle-overhang).

   POURQUOI ELLE N'EST PAS DANS <nav class="page-sidebar"> (cf. NavBar.razor) :
   le § 43 impose `.page-sidebar button:hover{background:transparent !important}`
   — à l'intérieur, le moindre état de survol demanderait un !important de plus.
   (La première raison, le rognage par overflow:hidden, est tombée avec le
   débord ; celle-ci suffit à l'y laisser.)

   Elle est en position:fixed et suit --kd-sidebar-w : elle se déplace donc avec
   la boîte, à la même cadence, sans que le JS ait à la positionner. */
.kd-sidebar-toggle {
    position: fixed;
    top: calc((var(--kd-sidebar-header-h) - var(--kd-sidebar-toggle-size)) / 2);
    /* Repliée : centrée dans la boîte, à la place du logo. */
    left: calc((var(--kd-sidebar-w) - var(--kd-sidebar-toggle-size)) / 2);
    z-index: 1001;                    /* .page-sidebar est à 1000 */
    display: flex;
    align-items: center;
    justify-content: center;
    width: var(--kd-sidebar-toggle-size);
    height: var(--kd-sidebar-toggle-size);
    padding: 0;
    /* Voile clair et pas de cercle sombre : la pastille est désormais TOUJOURS
       posée sur l'ardoise du menu, jamais sur le fond de page. Et pas d'ombre —
       une ombre portée à l'intérieur d'un panneau ne veut rien dire. */
    border: 1px solid var(--kd-nav-toggle-border);
    border-radius: 50%;
    background-color: var(--kd-nav-toggle-bg);
    color: var(--kd-nav-fg-active);
    box-shadow: none;
    cursor: pointer;
    transition: left var(--kd-shell-transition), background-color 160ms ease, border-color 160ms ease;
}

.kd-sidebar-toggle:hover {
    background-color: var(--kd-nav-toggle-bg-hover);
    border-color: var(--kd-nav-toggle-border-hover);
}

.kd-sidebar-toggle:focus-visible {
    outline: 2px solid var(--kd-primary);
    outline-offset: 2px;
}

.kd-sidebar-toggle > i {
    font-size: 14px;
    line-height: 1;
    transition: transform var(--kd-shell-transition);
}

/* Le chevron pointe vers l'action à venir : vers la droite pour déplier,
   vers la gauche pour replier. Une seule icône, retournée. */
body.menu-pin .kd-sidebar-toggle > i {
    transform: rotate(180deg);
}

/* Dépliée, elle quitte le centre pour la DROITE de l'en-tête, sur la ligne du
   logo — les deux en-têtes ayant la même hauteur, les centres coïncident. Elle
   ne fait plus que se déplacer : son habillage est le même dans les deux états
   depuis qu'elle ne sort plus de la boîte. */
body.menu-pin .kd-sidebar-toggle {
    left: calc(var(--kd-sidebar-w) - var(--kd-sidebar-toggle-size) + var(--kd-sidebar-toggle-inset));
}

/* --- Le décalage du contenu -------------------------------------------- */
/* MARGE et non rembourrage, avec width:auto.
   Deux raisons, toutes deux vérifiées à la mesure :
   1. .header est en position:absolute dans .page-container. Un élément absolu
      se cale sur la BOÎTE DE PADDING de son bloc conteneur : un padding-left
      ne déplacerait donc pas son origine, mais son `width:100%` le ferait
      déborder à droite de la valeur du décalage.
   2. Avec un padding, le fond de .page-container continue de courir SOUS la
      sidebar — invisible tant que la sidebar était collée au bord, mais la
      gouttière du mode « boxed » le révélerait.
   Le shell hérité posait ici `width:100%` et `padding-left:70px` (le décalage
   de l'ancien rail) : il fallait les contrer par `width:auto` et
   `padding-left:0`. Les deux ont été RÉSORBÉS du § 43 — la paire entière a
   disparu, il ne reste que l'intention. */
.page-container {
    margin-left: var(--kd-shell-offset);
    transition: margin-left var(--kd-shell-transition);
}

/* La neutralisation `body.menu-pin .page-container .page-content-wrapper .content
   { padding-left: 0 }` a disparu (14/09/2026). Elle défaisait le `padding-left: 250px`
   que l'ancien template posait sur .content menu épinglé ; ce décalage est parti avec
   theme.css, elle lui a survécu.
   ⚠️ NE PAS LA REMETTRE. Depuis que le shell est bord à bord, .content porte le retrait
   gauche du contenu (« Le contenu commence à la gouttière », plus bas), et à (0,4,1)
   contre (0,3,0) elle l'effaçait dès que body portait `menu-pin` — l'état d'usine :
   contenu collé au menu sur /alarms, /reports-center, /monitoring-center… Mesuré sur
   les modes de PageWrapper à 1400, 700 et 400 px : 0 px menu déplié, 10 px replié. */

/* --- En-tête de la boîte ----------------------------------------------- */
/* Le fond propre de .sidebar-header (#272b35) dessinerait des angles vifs
   dans les coins arrondis de la boîte : on le rend transparent et c'est la
   boîte qui porte la couleur. */
.page-sidebar .sidebar-header {
    flex: none;
    height: var(--kd-sidebar-header-h);
    background: transparent;
    border-bottom-color: var(--kd-nav-header-rule);
    display: flex;
    align-items: center;
    justify-content: flex-start;
    padding-left: 16px;
    /* Réserve la place de la pastille, qui chevauche le bord droit de la boîte.
       L'en-tête ne contient plus que le logo, mais tout contrôle qu'on y
       remettrait à droite passerait sous elle — c'est ce qui arrivait au
       chevron du panneau #appMenu. */
    padding-right: calc(var(--kd-sidebar-toggle-size) + 8px);
}

/* Le logo disparaît en mode replié : 140 px ne tiennent pas dans 72 px, et le
   rogner donnerait un fragment illisible. Le picto de marque prend le relais
   (cf. --kd-logo-collapsed plus bas). */
.page-sidebar .sidebar-header .kd-logo--sidebar {
    transition: width 160ms ease;
}

/* La zone de menu prend tout l'espace restant ; min-height:0 est indispensable
   pour qu'un élément flex accepte de se contracter et laisse .menu-items défiler
   au lieu de pousser le pied hors de la boîte. */
.page-sidebar .sidebar-menu {
    flex: 1 1 auto;
    height: auto;
    min-height: 0;
}

.page-sidebar .sidebar-footer {
    flex: none;
}

/* Le bloc Nouveautés, au-dessus du compte (NavBar.razor) — il portait Réglages, passé
   dans le panneau du compte le 14/09/2026. Il porte `sidebar-menu`
   pour hériter de TOUTE la grammaire d'entrée de menu — pilule, icône à gauche,
   libellé retiré du flux en replié — mais pas de sa part de hauteur : sans ce
   `flex: none`, la règle `.page-sidebar .sidebar-menu { flex: 1 }` le ferait
   partager la boîte à égalité avec la liste principale, et les entrées du menu
   n'auraient plus que la moitié de la hauteur avant de défiler.
   (0,3,0) contre (0,2,0) : il gagne quel que soit l'ordre des blocs. */
.page-sidebar .sidebar-menu.kd-sidebar-menu-foot {
    flex: none;
    padding-bottom: 4px;
}

/* --- Items de menu : icône à GAUCHE, libellé à droite, actif en pilule -- */
.page-sidebar .sidebar-menu .menu-items > li {
    position: relative;
    padding: 0 8px;
}

/* .icon-thumbnail est un FRÈRE du lien dans le markup (NavBarMenuLink.razor),
   posé après lui. On le sort du flux et on le cale à gauche : le lien reste
   ainsi la cible de clic sur toute la largeur, comme la pilule de Katalyst.

   ⚠️ `> li >` et non un descendant quelconque. Un élément absolu se cale sur le
   premier ancêtre POSITIONNÉ : avec `.menu-items .icon-thumbnail`, les icônes
   des sous-menus se calaient sur le <li> de premier niveau — toutes empilées
   dans le même angle, par-dessus l'icône du parent. Chaque niveau doit poser
   sa propre référence (voir le bloc « sous-menus » plus bas). */
.page-sidebar .sidebar-menu .menu-items > li > .icon-thumbnail {
    position: absolute;
    left: 8px;
    top: 0;
    bottom: auto;
    width: 56px;
    /* Hauteur d'UNE ligne, et non top:0/bottom:0. Le <li> s'étire à la hauteur
       de sa branche quand un sous-menu est ouvert : l'icône, centrée sur toute
       cette hauteur, dérivait au milieu du sous-arbre (mesuré : 78 px de haut
       pour une ligne de 36). */
    height: var(--kd-nav-item-h);
    margin: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
    z-index: 1;
}

/* L'icône reste centrée dans sa colonne de 56 px — donc au milieu du rail replié
   de 72 : c'est le libellé qui se rapproche, pas l'icône qui bouge. 48 et non 56
   (14/09/2026, « réduit la marge entre les icônes et les textes ») : 11 px entre
   le bord de l'icône de 18 px et le libellé, là où il y en avait 20. */
.page-sidebar .sidebar-menu .menu-items > li > a {
    display: flex;
    align-items: center;
    min-height: var(--kd-nav-item-h);
    padding-left: 48px;
    padding-right: 12px;
    font-size: var(--kd-menu-font);
    border-radius: 10px;
    transition: background-color 160ms ease, color 160ms ease;
}

.page-sidebar .sidebar-menu .menu-items > li > a > .title {
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* La pilule active — le repère visuel principal du menu. */
.page-sidebar .sidebar-menu .menu-items > li.active > a,
.page-sidebar .sidebar-menu .menu-items > li > a:hover {
    background-color: var(--kd-nav-pill-bg-hover);
}

.page-sidebar .sidebar-menu .menu-items > li.active > a {
    background-color: var(--kd-nav-pill-bg-active);
    color: var(--kd-nav-fg-active);
}

/* --- Sous-menus --------------------------------------------------------- */
/* Même grammaire qu'au premier niveau — icône à gauche, libellé à droite,
   pilule — décalée d'un cran. Chaque <li> porte sa propre référence de
   positionnement, sinon les icônes remontent au <li> racine (cf. ci-dessus).

   ⚠️ LES SÉLECTEURS RECOPIENT EXACTEMENT CEUX DE theme.css, `> li` compris.
   Écrits plus court (`.menu-items ul.sub-menu > li > a`), ils pesaient (0,4,3)
   contre les (0,4,4) du thème et n'appliquaient RIEN : fond, rembourrages et
   dimensions d'icône restaient ceux de l'ancien design. À spécificité égale
   c'est l'ordre de chargement qui tranche, et app.css vient après theme.css. */
.page-sidebar .sidebar-menu .menu-items > li ul.sub-menu {
    /* Le fond #21252d de theme.css dessinait une bande à angles vifs en travers
       de la boîte arrondie. */
    background-color: transparent;
    margin: 0;
    padding: 0 0 6px;
}

.page-sidebar .sidebar-menu .menu-items > li ul.sub-menu > li {
    position: relative;
    padding: 0 8px 0 20px;
    margin-top: 0;
}

.page-sidebar .sidebar-menu .menu-items > li ul.sub-menu > li > .icon-thumbnail {
    position: absolute;
    left: 20px;
    top: 0;
    bottom: auto;
    width: 44px;
    height: var(--kd-nav-subitem-h);   /* une ligne, cf. premier niveau */
    margin: 0;
    float: none;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: transparent;   /* la puce sombre devenait une barre pleine hauteur */
    pointer-events: none;
    transform: none !important;
    z-index: 1;
}

.page-sidebar .sidebar-menu .menu-items > li ul.sub-menu > li > a {
    display: flex;
    align-items: center;
    width: auto;
    min-height: var(--kd-nav-subitem-h);
    /* 42 et non 44 : même écart de 11 px entre l'icône et le libellé qu'au premier
       niveau. Les 12 px de theme.css sont battus ici, à sélecteur égal et plus bas. */
    padding: 0 12px 0 42px;
    font-size: var(--kd-menu-font);
    border-radius: 8px;
    transition: background-color 160ms ease, color 160ms ease;
}

.page-sidebar .sidebar-menu .menu-items > li ul.sub-menu > li.active > a,
.page-sidebar .sidebar-menu .menu-items > li ul.sub-menu > li > a:hover {
    background-color: var(--kd-nav-pill-bg-hover);
}

/* --- Pied de la boîte : le compte --------------------------------------- */
/* Le rembourrage vient d'ici et non des helpers p-l-20/p-b-15/p-t-10 qui
   étaient sur le markup : ceux de theme.css sont en !important et refusaient
   d'être remis à zéro pour centrer l'avatar dans le rail. */
.page-sidebar .sidebar-footer {
    padding: 8px;
    border-top: 1px solid var(--kd-nav-footer-rule);
}

/* Le déclencheur. Il vit DANS la boîte : c'est lui qui en réserve le bas et qui
   se centre dans le rail replié. Le panneau, lui, est un frère du <nav>. */
.page-sidebar .sidebar-footer .kd-account-trigger {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 6px;
    border: 0;
    border-radius: 10px;
    background: transparent;
    color: var(--kd-nav-fg);
    text-align: left;
    cursor: pointer;
    transition: background-color 160ms ease;
}

.page-sidebar .sidebar-footer .kd-account-trigger:hover,
.page-sidebar .sidebar-footer .kd-account-trigger.open {
    /* !important : le § 43 impose `.page-sidebar button:hover{background:
       transparent !important}`, hérité du thème acheté. Non résorbé : la sonde
       de masquage neutralise les pseudo-classes dynamiques avant d'interroger
       le DOM, elle ne peut donc rien conclure sur un :hover. À reprendre avec
       un relevé en survol réel. */
    background-color: var(--kd-nav-account-bg-hover) !important;
}

.page-sidebar .sidebar-footer .kd-account-trigger:focus-visible {
    outline: 2px solid var(--kd-primary);
    outline-offset: -2px;
}

.kd-avatar {
    position: relative;
    flex: none;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    display: grid;
    place-items: center;
    background: var(--kd-avatar-bg);
    color: var(--kd-avatar-fg);
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .01em;
}

/* La pastille « nouvelle version » a quitté l'avatar avec « Nouveautés » (14/09/2026) :
   elle signalait ce que le panneau du compte contenait. Elle se pose désormais sur
   l'entrée elle-même, au pied du menu — badge NEW déplié, point sur l'icône replié. */
.kd-nav-badge {
    display: inline-block;
    margin-left: 8px;
    padding: 2px 6px;
    border-radius: 6px;
    background: var(--kd-warning);
    color: var(--kd-warning-text);
    font-size: 10px;
    font-weight: 700;
    line-height: 1.2;
    vertical-align: middle;
}

/* Masqué déplié, où le badge dit la même chose en toutes lettres ; affiché par les deux
   blocs du rail replié (§ 28, « Mode replié » et « Verrou du rail »). La bordure le
   détache de l'ardoise. */
.kd-nav-dot {
    display: none;
    position: absolute;
    top: 10px;
    left: calc(50% + 4px);
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--kd-warning);
    border: 2px solid var(--kd-nav-bg);
}

.kd-account-who {
    min-width: 0;
    line-height: 1.25;
}

.kd-account-who b {
    display: block;
    font-size: 11.5px;
    font-weight: 620;
    color: var(--kd-nav-fg-active);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.kd-account-who > span {
    display: block;
    font-size: 10px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* --- Le panneau du compte ----------------------------------------------- */
/* Hors du <nav>, en fixed, comme la pastille de bascule : la boîte est en
   overflow:hidden. Ancré en bas et calé sur --kd-sidebar-w, il suit donc le
   bord de la boîte dans les deux états sans un octet de JS. */
.kd-account-backdrop {
    position: fixed;
    inset: 0;
    z-index: 1002;
    background: transparent;
}

.kd-account-panel {
    position: fixed;
    left: calc(var(--kd-sidebar-w) + 8px);
    bottom: var(--kd-shell-gap);
    z-index: 1003;
    width: 236px;
    padding: 14px 0 6px;
    border-radius: 12px;
    background: var(--kd-surface);
    box-shadow: 0 10px 34px rgba(15, 18, 26, .20);
    transition: left var(--kd-shell-transition);
}

.kd-account-id {
    padding: 0 16px 12px;
}

.kd-account-id b {
    display: block;
    font-size: 14px;
    font-weight: 640;
    letter-spacing: -.01em;
    color: var(--bs-body-color);
}

.kd-account-id > span {
    display: block;
    font-size: 11px;
    color: var(--bs-secondary-color);
    word-break: break-all;
}

.kd-account-role {
    margin-top: 6px;
    font-size: 9.5px;
    letter-spacing: .09em;
    text-transform: uppercase;
    color: var(--kd-primary-dark);
}

.kd-account-panel hr {
    margin: 0 0 6px;
    border: 0;
    border-top: 1px solid var(--kd-border);
    opacity: 1;
}

.kd-account-label {
    padding: 4px 16px;
    font-size: 9.5px;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--bs-secondary-color);
}

.kd-account-item {
    display: flex;
    align-items: center;
    gap: 11px;
    width: 100%;
    padding: 7px 16px;
    border: 0;
    background: transparent;
    font-size: 12.5px;
    color: var(--bs-body-color);
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}

.kd-account-item > i {
    font-size: 15px;
    color: var(--bs-secondary-color);
    flex: none;
}

.kd-account-item:hover {
    background-color: var(--kd-hover);
    color: var(--bs-body-color);
}

.kd-account-item.on {
    font-weight: 600;
}

.kd-account-item.on > i {
    color: var(--kd-primary-dark);
}

.kd-account-item.danger,
.kd-account-item.danger > i {
    color: var(--kd-danger);
}

.kd-account-item.danger:hover {
    background-color: var(--kd-danger-soft);
}

/* --- Mode replié : le rail d'icônes ------------------------------------- */
/* TOUT le mode replié tient dans cette requête média, et rien d'autre n'y
   entre. C'est délibéré : « replié » n'existe que sur le bureau — en mobile la
   boîte est large par nature et menu-pin n'y veut rien dire. Écrire ces règles
   hors média obligerait à les DÉFAIRE une par une dans le bloc mobile, et l'une
   d'elles ne se défait pas : `display:none !important` sur les sous-menus ne
   peut pas être annulé sans réimposer une valeur, ce qui figerait les branches
   ouvertes que Blazor pilote par style en ligne.
   LE SEUIL EST md (768 px), PAS lg. Il était à lg : une fenêtre de bureau
   réduite — 900 px, un portable en écran partagé — retombait sur le tiroir
   plein écran, sans coins ni ombre, alors qu'on est très loin du téléphone.
   Les NEUF blocs du shell (celui-ci, le régime mobile, la carte de barre, le
   § 43 hérité) forment UN SEUL interrupteur : ils se déplacent ensemble ou
   pas du tout. Le bloc `.card{height:auto}` reste à 991 — il concerne les
   dispositions GridStack, pas la géométrie du shell. */
@media (min-width: 576px) {
    /* En-tête : le picto seul, centré.
       padding:0 et non padding-left:0 seulement — le § 43 pose `padding: 0 20px`,
       dont les 20 px de DROITE décalaient le picto de 10 px vers la gauche dans
       une boîte de 72 (mesuré : centre du logo à 36, centre de la boîte à 46). */
    body:not(.menu-pin) .page-sidebar .sidebar-header {
        padding: 0;
        justify-content: center;
    }

    body:not(.menu-pin) .page-sidebar .sidebar-header .kd-logo--sidebar {
        width: 32px;
        background-image: var(--kd-logo-collapsed);
    }

    /* Repliée, LA PASTILLE PREND LA PLACE DU LOGO — le picto de marque s'efface.
       Deux ronds de 26 et 32 px l'un sur l'autre dans une boîte de 72 n'ont pas
       la place, et c'est la COMMANDE qui doit rester : l'application est déjà
       nommée par l'onglet du navigateur et par l'entrée active du menu.
       ⚠️ Le garde de largeur : à partir de 768 px seulement. Entre 576 et 767 la
       boîte est verrouillée en rail et la bascule est MASQUÉE — effacer le picto
       en plus y laisserait un en-tête vide. */
    @media (min-width: 768px) {
        body:not(.menu-pin) .page-sidebar .sidebar-header .kd-logo--sidebar {
            display: none;
        }
    }

    /* Les libellés sortent du FLUX au lieu d'être rendus transparents.
       Un `opacity:0` laisse l'élément occuper sa place : le lien gardait sa
       largeur de libellé et débordait la boîte (mesuré : scrollWidth 76 pour
       clientWidth 72), ce que `.menu-items{overflow:auto}` traduisait par une
       barre de défilement horizontale. La recette est celle de .visually-hidden
       de Bootstrap : invisible et sans encombrement, mais TOUJOURS LU par les
       lecteurs d'écran — un `display:none` priverait les liens de leur nom
       accessible, l'icône étant un frère sans texte.

       ⚠️ `.menu-items` dans le sélecteur n'est pas décoratif : sans lui la règle
       pèse (0,4,1) contre les (0,4,2) de
       `.page-sidebar .sidebar-menu .menu-items > li > a > .title{width:auto}`,
       et la largeur du libellé survivait au masquage (mesuré : rect de 40 px de
       large pour une règle qui en demande 1). */
    body:not(.menu-pin) .page-sidebar .sidebar-menu .menu-items .title,
    body:not(.menu-pin) .page-sidebar .sidebar-footer .title {
        position: absolute;
        width: 1px;
        height: 1px;
        padding: 0;
        margin: -1px;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
        border: 0;
    }

    /* La flèche d'ouverture n'a plus d'objet : les sous-menus sont fermés. */
    body:not(.menu-pin) .page-sidebar .sidebar-menu .arrow {
        display: none;
    }

    /* Le badge NEW de Nouveautés part avec son libellé : le point sur l'icône le relaie. */
    body:not(.menu-pin) .page-sidebar .kd-nav-dot {
        display: block;
    }

    /* !important : NavBarSubMenu.razor écrit `style="display:block"` EN LIGNE
       selon la branche ouverte, et un style en ligne bat toute règle non
       !important. Déplier à nouveau le menu rouvre la branche — l'état vit dans
       le StateContainer, pas dans le CSS. */
    body:not(.menu-pin) .page-sidebar .sidebar-menu ul.sub-menu {
        display: none !important;
    }

    /* Le lien devient un carré centré sur l'icône : sans cela il conserve ses
       68 px de rembourrage dans une boîte qui n'en offre que 56. */
    body:not(.menu-pin) .page-sidebar .sidebar-menu .menu-items > li > a {
        padding-left: 0;
        padding-right: 0;
    }

    /* Pied : l'avatar seul, centré. Le nom et l'adresse portent .title et sont
       déjà retirés du flux par la règle ci-dessus ; il ne reste qu'à recentrer. */
    body:not(.menu-pin) .page-sidebar .sidebar-footer .kd-account-trigger {
        justify-content: center;
        gap: 0;
    }
}

/* --- Verrou du rail entre 576 et 767 px --------------------------------- */
/* Le shell reste boxé jusqu'à 576 px, mais à ces largeurs un menu DÉPLIÉ ne
   laisserait que 287 px de contenu (576 − 289). Le rail est donc IMPOSÉ dans
   cette bande : la largeur est verrouillée sur la valeur repliée et la pastille
   de bascule disparaît, faute d'objet. Le choix de l'utilisateur n'est pas
   effacé — body.menu-pin reste posé, et le menu se rouvre dès que la fenêtre
   repasse au-dessus de 768.

   ⚠️ CES RÈGLES DOUBLENT CELLES DU BLOC CI-DESSUS, à dessein. La condition
   réelle du rail est « (576–767) OU (≥768 ET non épinglé) », et une requête
   média ne s'exprime pas dans un sélecteur : il n'y a pas de préfixe unique qui
   couvre les deux. TOUTE MODIFICATION DU RAIL DOIT ÊTRE REPORTÉE ICI AUSSI.
   Les commentaires qui expliquent chaque règle sont au-dessus, pas recopiés.

   ⚠️ Les deux préfixes `body.menu-pin` / `body:not(.menu-pin)` ne sont pas une
   coquetterie : ils pèsent (0,1,1) comme le garde du bloc ci-dessus. Écrire
   `body` seul retomberait à (0,0,1), et le masquage des libellés perdrait
   contre `.page-sidebar .sidebar-menu .menu-items > li > a > .title{width:auto}`
   qui pèse (0,4,2) — le libellé garderait sa largeur dans une boîte de 72. */
@media (min-width: 576px) and (max-width: 767px) {
    /* Neutralise le `body.menu-pin{--kd-sidebar-w: expanded}` de tokens.css :
       même spécificité, mais tokens.css est chargé avant app.css. */
    body.menu-pin {
        --kd-sidebar-w: var(--kd-sidebar-w-collapsed);
    }

    .kd-sidebar-toggle {
        display: none;
    }

    /* Le rattrapage `body:not(.menu-pin){--kd-shell-offset}` a disparu : il ne
       servait qu’à retrancher le débord de la pastille, qui n’existe plus nulle
       part. La formule de tokens.css vaut telle quelle ici aussi. */

    body.menu-pin .page-sidebar .sidebar-header,
    body:not(.menu-pin) .page-sidebar .sidebar-header {
        padding: 0;
        justify-content: center;
    }

    body.menu-pin .page-sidebar .sidebar-header .kd-logo--sidebar,
    body:not(.menu-pin) .page-sidebar .sidebar-header .kd-logo--sidebar {
        width: 32px;
        background-image: var(--kd-logo-collapsed);
    }

    body.menu-pin .page-sidebar .sidebar-menu .menu-items .title,
    body.menu-pin .page-sidebar .sidebar-footer .title,
    body:not(.menu-pin) .page-sidebar .sidebar-menu .menu-items .title,
    body:not(.menu-pin) .page-sidebar .sidebar-footer .title {
        position: absolute;
        width: 1px;
        height: 1px;
        padding: 0;
        margin: -1px;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
        border: 0;
    }

    body.menu-pin .page-sidebar .sidebar-menu .arrow,
    body:not(.menu-pin) .page-sidebar .sidebar-menu .arrow {
        display: none;
    }

    body.menu-pin .page-sidebar .kd-nav-dot,
    body:not(.menu-pin) .page-sidebar .kd-nav-dot {
        display: block;
    }

    body.menu-pin .page-sidebar .sidebar-menu ul.sub-menu,
    body:not(.menu-pin) .page-sidebar .sidebar-menu ul.sub-menu {
        display: none !important;
    }

    body.menu-pin .page-sidebar .sidebar-menu .menu-items > li > a,
    body:not(.menu-pin) .page-sidebar .sidebar-menu .menu-items > li > a {
        padding-left: 0;
        padding-right: 0;
    }

    body.menu-pin .page-sidebar .sidebar-footer .kd-account-trigger,
    body:not(.menu-pin) .page-sidebar .sidebar-footer .kd-account-trigger {
        justify-content: center;
        gap: 0;
    }
}

/* --- La ligne de titre, dans le contenu --------------------------------- */
/* Elle remplace la barre supérieure. Ce n'est plus du chrome mais un TITRE DE
   PAGE : aucun bloc, aucun fond, aucune carte — de la typographie posée sur le
   fond, et un filet pour la détacher du contenu.

   ⚠️ ELLE EST DANS LE DÉFILEUR, ET C'EST LE POINT. Sur les modes Normal /
   WithSidebar / WithRail c'est le `container-fluid overflow-y-auto` de la page,
   frère droit de la sidebar de page dans une rangée flex. Deux conséquences,
   toutes deux voulues :
     - le titre DÉFILE : ses ~90 px reviennent au contenu dès qu'on descend, et
       seule la gouttière de 10 px reste prise en permanence ;
     - il NE COIFFE QUE LE CONTENU. Sur les trois écrans à sidebar de page
       (/file-explorer 400 px, /sites/assets/explorer 450 px, les réglages) cette
       sidebar démarre donc tout en haut. Leur premier élément étant déjà une
       bande — un DxTabs sur l'explorateur d'équipements — les deux colonnes
       partent à peu près de niveau au repos. Arbitrage tranché, pas un oubli.

   Le grand corps léger vient d'une référence apportée par Jérémy : un titre
   d'écran, pas une étiquette de barre. */
.kd-page-head {
    /* `0 0 auto` : la ligne de titre ne s'étire ni ne se comprime dans la pile
       du défileur — c'est .kd-page-body qui prend le reste. */
    flex: 0 0 auto;
    display: flex;
    align-items: flex-start;
    /* ⚠️ `wrap` N'EST PAS COSMÉTIQUE. Sur un écran à sidebar de page, la colonne
       de contenu est étroite — 430 px sur l'explorateur d'équipements, qui lui en
       prend 450. Sans le retour à la ligne, les outils d'écran comprimaient le
       titre jusqu'à l'élider (« Explorateur d'équipeme… ») et s'empilaient
       eux-mêmes en colonne, la ligne montant à 117 px de haut. Ils descendent
       maintenant sur leur propre ligne, où ils retrouvent toute la largeur. */
    flex-wrap: wrap;
    gap: 8px 12px;
    /* ⚠️ LE RETRAIT HAUT EST PORTÉ ICI, ET PAS PAR LE CONTENEUR. Chaque mode de
       PageWrapper a le sien — 16 px sur l'accueil (py-3), 15 sur kd-content-pad,
       ZÉRO sur ContentNoPadding — si bien que le titre changeait de hauteur d'un
       écran à l'autre : mesuré à 26, 25 et 10 px. Le titre est un repère : il se
       pose au même endroit partout, quel que soit le rembourrage que la page
       choisit pour SON contenu. */
    padding-top: 15px;
    /* Pas de filet sous le titre : c'est l'ESPACE qui le sépare du contenu, pas un
       trait. Le trait rejouait la barre supérieure qu'on vient de retirer — une
       ligne en travers de la colonne, exactement ce dont on voulait se défaire. */
    padding-bottom: 14px;
    margin-bottom: 16px;
}

/* Rien à titrer — l'accueil ne passe aucun PageTitle. La ligne reste rendue,
   parce qu'elle porte le hamburger du mobile et l'outlet des outils d'écran, mais
   elle ne réserve plus l'espace d'un titre absent : mesuré avant garde, 17 px de
   haut et 33 px de contenu repoussé pour une ligne vide. */
.kd-page-head--bare {
    padding-bottom: 0;
    margin-bottom: 0;
}

/* Le hamburger n'existe que là où le menu est un tiroir (sous 576 px). */
.kd-page-burger {
    flex: none;
    display: grid;
    place-items: center;
    width: 34px;
    height: 34px;
    margin-top: 2px;
    border: 0;
    border-radius: 9px;
    background: var(--kd-surface);
    color: var(--kd-text);
    font-size: 17px;
    cursor: pointer;
}

/* Le retour remplace le chemin que le fil d'Ariane assurait sur les écrans de
   détail — la seule fonction de navigation qu'il rendait vraiment. */
.kd-page-back {
    flex: none;
    display: grid;
    place-items: center;
    width: 34px;
    height: 34px;
    margin-top: 2px;
    border-radius: 9px;
    background: var(--kd-surface);
    color: var(--kd-text-muted);
    font-size: 17px;
}

.kd-page-back:hover {
    background: var(--kd-border-light);
    color: var(--kd-text-strong);
}

/* min-width:0 est indispensable : le défaut `auto` d'un élément flex l'empêche de
   se contracter sous la taille de son contenu, et un titre long pousserait les
   outils hors de la ligne au lieu de s'élider. */
/* La base est le SEUIL DE RETOUR À LA LIGNE : en dessous, les outils d'écran
   passent dessous au lieu de rogner le titre. 300 px et non 240 — mesuré sur
   l'explorateur d'équipements, dont la sidebar de page laisse 430 px : à 240, les
   outils (148) tenaient encore sur la ligne et « Explorateur d'équipements »
   s'élidait de 13 px. À 300 la ligne déborde, les outils passent dessous, et le
   titre récupère les 430. */
.kd-page-titles {
    flex: 1 1 300px;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

/* Léger et large, pas gras et petit : c'est un titre de page. Le `clamp` évite
   d'écrire deux paliers — il grandit avec la fenêtre et s'arrête à 30 px, au-delà
   desquels il mangerait la première rangée de contenu. */
.kd-page-title {
    margin: 0;
    font-size: clamp(22px, 2.2vw, 30px);
    font-weight: 300;
    line-height: 1.15;
    letter-spacing: -.02em;
    color: var(--kd-text-strong);
    /* Il s'élide plutôt que de pousser les outils d'écran hors de la ligne. */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.kd-page-subtitle {
    margin: 0;
    font-size: 13.5px;
    line-height: 1.45;
    color: var(--kd-text-muted);
    /* Une phrase, pas un paragraphe : au-delà elle concurrence le titre. */
    max-width: 70ch;
}

/* Les outils d'écran s'alignent sur le TITRE et non sur le bloc : avec un
   sous-titre, `align-items: flex-start` les laisserait flotter en haut, mais leur
   propre marge les recale sur la première ligne. */
.kd-page-head-tools {
    flex: 0 1 auto;
    min-width: 0;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 2px;
    /* Colle les outils à droite dans les deux cas : sur la ligne du titre quand
       ils y tiennent, et sur leur propre ligne quand ils sont passés dessous. */
    margin-left: auto;
}

/* --- La pile du défileur ------------------------------------------------- */
/* ⚠️ CES DEUX RÈGLES SONT LE PRIX DU TITRE DÉFILANT, et sans elles les
   visualiseurs débordent.
   `height: 100%` se résout contre la hauteur du BLOC CONTENEUR, pas contre
   l'espace restant. Une racine de page en `height: 100%` posée sous un titre de
   ~90 px donnait donc 90 px de débord — mesuré à 749 px de fenêtre sur
   /explorer-plans : défileur 681, contenu 719, débord 38 pour un titre de 38.
   L'écran se mettait à défiler de la hauteur du titre, POUR RIEN, et les
   sélecteurs de site et de bâtiment partaient avec.
   .kd-page-body prend l'espace restant par le flex ; les racines de page gardent
   alors leur `height: 100%` sans qu'aucune n'ait à être touchée. */
.kd-scroll-col {
    display: flex;
    flex-direction: column;
}

/* `1 0 auto` et non `1 1 auto` : il doit GRANDIR pour remplir quand le contenu
   est court (un visualiseur), et NE PAS RÉTRÉCIR quand il est long (une grille) —
   sinon le contenu serait rogné au lieu de faire défiler. */
.kd-page-body {
    flex: 1 0 auto;
}

/* --- Impression --------------------------------------------------------- */
/* Le § 43 masque déjà .header/.page-sidebar/.overlay et remet
   `.page-container{padding-left:0}` — mais le décalage est désormais une MARGE.
   Sans cette règle, chaque page imprimée garderait 280 px de blanc à gauche et
   perdrait d'autant à droite. Les dashboards Pictet s'appuient sur ce contrat
   (pictet-shared.css). */
@media print {
    .page-container {
        margin-left: 0;
        width: 100%;
    }

    /* Le § 43 masque .page-sidebar/.overlay mais ne connaît pas la pastille :
       elle s'imprimerait, seule, dans la marge gauche. */
    .kd-sidebar-toggle {
        display: none;
    }

    /* Le rail n'est pas imprimé — mais le rembourrage droit du contenu vaut
       toujours sa largeur. Sans cette remise à zéro, chaque page sortie garderait
       80 px de blanc à droite, exactement la symétrie du défaut que la règle
       `margin-left: 0` ci-dessus corrige à gauche. */
    :root {
        --kd-tools-offset: 0px;
        /* Le menu n'est pas imprimé non plus : le retrait gauche, élargi parce
           qu'il le borde à l'écran, redevient la gouttière de la droite. */
        --kd-content-left: var(--kd-shell-gap);
    }
}

/* --- Régime mobile ------------------------------------------------------ */
/* La boîte sort de l'écran par la gauche et POUSSE le contenu — elle ne le
   survole pas. On garde ce modèle : c'est le geste attendu sur téléphone. */
@media (max-width: 575px) {
    .page-sidebar {
        top: 0;
        bottom: 0;
        left: 0;
        width: var(--kd-sidebar-w-expanded);
        border-radius: 0;
        box-shadow: none;
        transition: none;
        /* ⚠️ LE TIROIR EST FERMÉ PAR DÉFAUT, ET C'EST ICI QUE ÇA SE JOUE.
           Le § 43 pose bien `.page-sidebar{display:none}` à ce palier, mais la
           règle « La boîte » plus haut déclare `display:flex` SANS média et plus
           bas dans le fichier : à spécificité égale elle l'emportait, et le
           tiroir restait affiché en permanence par-dessus le contenu — ouvert
           comme fermé (mesuré à 900 px avant correction : rect identique dans
           les deux états, .visible et body.sidebar-open sans aucun effet).
           Le pendant `.page-sidebar.visible{display:flex}` est plus bas. */
        display: none;
    }

    .page-container {
        margin-left: 0;
        width: 100%;
        transition: transform var(--kd-shell-transition);
    }

    body.sidebar-open .page-container {
        transform: translate3d(var(--kd-sidebar-w-expanded), 0, 0);
    }

    /* ⚠️ Le contenu est POUSSÉ, pas rétréci : il garde `width: 100%` et sort donc
       de l'écran par la droite d'exactement la largeur du tiroir. Sans cette
       ligne le document déborde et le navigateur offre une barre de défilement
       HORIZONTALE tant que le menu est ouvert — mesuré à 576 px : scrollWidth 836
       pour un clientWidth de 576, soit 260 px de dérive latérale possible.
       Bornée à l'état ouvert : un `overflow-x: hidden` permanent sur body
       masquerait des débordements légitimes (grilles larges, graphiques) et crée
       un bloc de défilement qui casse `position: sticky`. */
    body.sidebar-open {
        overflow-x: hidden;
    }

    /* La bascule replié/déplié n'a pas d'objet ici : la boîte est large par
       nature et c'est le hamburger de la barre supérieure qui l'ouvre. */
    .kd-sidebar-toggle {
        display: none;
    }

    /* --kd-content-top ne se redéfinit plus ici. Il valait 48 px pour dégager la
       barre supérieure collée en haut ; la barre n'existe plus, le titre est dans
       le contenu et défile avec lui. La valeur de :root (une gouttière) vaut donc
       aussi bien en mobile — et 48 px y laisseraient une bande blanche. */
    :root {
        /* Le rail cède la place à un bouton flottant, qui ne prend aucune
           largeur : le contenu ne garde que son retrait, ajouté par `.content`.
           Sans cette ligne il garderait les 60 px du rail en vide à droite sur un
           téléphone. */
        --kd-tools-offset: 0px;
        /* Le menu est un tiroir : aucune surface ne borde plus le contenu à
           gauche. Le retrait élargi du bureau ne ferait que décentrer la page,
           10 px d'un côté contre 20 de l'autre sur un écran de téléphone. */
        --kd-content-left: var(--kd-shell-gap);
    }

    /* Le panneau du compte s'ouvre AU-DESSUS du déclencheur, dans la largeur du
       tiroir. Le calcul du bureau le calerait sur --kd-sidebar-w (valeur repliée)
       alors que la boîte fait ici 260 px, et un panneau de 236 px posé à sa
       droite déborderait d'un écran de téléphone. */
    .kd-account-panel {
        left: 8px;
        right: auto;
        bottom: 62px;
        width: calc(var(--kd-sidebar-w-expanded) - 16px);
        transition: none;
    }
}

/*------------------------------------------------------------------
[27. Cartes — ce que Bootstrap ne porte pas]

Fond, rayon, bordure et rembourrages viennent des --bs-card-* de tokens.css.
Ne restent ici que trois manques de Bootstrap 5.3 :

- l'OMBRE. Bootstrap déclare bien --bs-card-box-shadow, mais ne l'utilise dans
  aucune règle : la variable est inerte. Sans cette ligne, les cartes perdent
  toute élévation.
- la MARGE BASSE. Bootstrap 5 a retiré le margin-bottom des cartes (il attend
  un gap sur le conteneur). Les écrans de Cockpit empilent des cartes sans
  conteneur en flex/grid : sans marge, elles se touchent.
- la hauteur minimale de l'EN-TÊTE, qui aligne les titres d'une rangée de
  cartes de contenus différents.
*/

.card {
    box-shadow: var(--kd-shadow-card);
    margin-bottom: 20px;
    transition: all .2s ease;
}

.card .card-header {
    min-height: 50px;
    z-index: 3;
}

/* En mobile, une carte reprend sa hauteur naturelle : les dispositions de
   l'accueil sont posées par GridStack, qui écrit une hauteur en ligne calculée
   pour le grand écran. Sans ceci, les cartes gardent cette hauteur et laissent
   de grands vides sur téléphone. (Règle héritée du thème, conservée telle
   quelle — le !important combat un style en ligne, pas une autre feuille.) */
@media (max-width: 991px) {
    .card {
        height: auto !important;
    }
}

/* Le titre en capitales espacées fait partie de l'identité de Cockpit, et
   Bootstrap n'expose ni casse ni interlettrage. L'ellipse évite qu'un titre
   long ne pousse les contrôles de la carte hors de l'en-tête. */
.card .card-title {
    font-family: var(--kd-font-title);
    font-size: var(--kd-fs-base);
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: var(--kd-tracking-caps);
    line-height: normal;
    display: inline-block;
    margin: 0;
    padding: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* --- Contrôles d'en-tête de carte (déménagés de theme.css, tranche 5) ---

   Bootstrap 5.3 n'a aucune notion de « contrôles de carte » : ni variable
   --bs-card-*, ni classe. Tout ce bloc est donc irréductible.

   ⚠️ Les trois glyphes viennent de la police « pages-icon », déclarée par un
   @font-face, qui a déménagé avec elles au § 41 (theme.css n'existe plus).
   Il reste solidaire de ces
   règles le jour où theme.css disparaîtra, sinon les boutons replier /
   rafraîchir / agrandir deviennent des carrés vides — panne silencieuse.
   (Glyphes vérifiés présents dans la table cmap du .woff : \e986, \e987,
   \e98c, \e98d, \e98e.)

   ⚠️ .card-icon impose font-family "pages-icon", et CardExtended.razor:40 pose
   `card-icon ph-duotone ph-arrow-square-out` sur le MÊME <i>. Si l'icône de
   lien externe s'affiche encore, c'est uniquement parce que la feuille
   Phosphor déclare sa famille en !important. Ne pas « nettoyer » ce
   !important côté Phosphor.
*/

.card .card-header .card-controls ul {
    margin: 0;
    padding: 0;
}

.card .card-header .card-controls ul li {
    display: inline-block;
    list-style: none;
    line-height: 0;
}

.card .card-header .card-controls ul li:last-child button,
.card .card-header .card-controls ul li:last-child a {
    padding-right: 0;
}

/* Bascule des icônes selon l'état, posée par cardextended.js (card-collapsed
   l.47, card-maximized l.75). Ces deux règles n'ont de sens qu'avec les
   .card-icon-* ci-dessous : les couper séparément ferait disparaître le
   changement d'icône sans rien signaler. */
.card.card-collapsed .card-header .card-controls .card-icon-collapse:before {
    content: "\e986";
}

.card.card-maximized .card-header .card-controls .card-icon-maximize:before {
    content: "\e98d";
}

.card.card-bordered {
    border: 1px solid rgba(224, 224, 224, .7);
}

/* Rembourrage haut resserré (5px contre 15px) : --bs-card-spacer-y vaut pour
   le haut ET le bas, l'asymétrie n'est pas exprimable en variable. */
.card .card-body {
    padding: 5px 15px 15px 15px;
}

/* Liens d'en-tête : atténués au repos, pleins au survol. CardExtended.razor.css
   s'appuie explicitement sur cette règle et ne redéclare que sa transition. */
.card-header a:not(.btn) {
    color: inherit;
    opacity: .4;
    padding-top: 10px;
    padding-bottom: 10px;
}

.card-header a[data-toggle]:hover {
    opacity: 1;
}

.card-icon {
    font-size: 20px;
    font-family: "pages-icon";
    font-style: normal;
    font-variant: normal;
    font-weight: normal;
    line-height: 1;
    text-transform: none;
    color: inherit;
    -moz-osx-font-smoothing: grayscale;
    -webkit-font-smoothing: antialiased;
}

.card-icon-maximize:before { content: "\e98e"; }
.card-icon-refresh:before  { content: "\e98c"; }
.card-icon-collapse:before { content: "\e987"; }


/*------------------------------------------------------------------
[26. Logo — emplacements de marque pilotés par tokens]

Le logo est une IMAGE DE FOND et non une balise <img>, pour qu'un client
puisse le remplacer en redéfinissant --kd-logo-* sans toucher au markup
(cf. l'en-tête de tokens.css).

Contrepartie : un fond n'a pas de texte alternatif. Les éléments porteurs
déclarent donc role="img" et aria-label côté Razor — sans quoi le nom de
l'application disparaîtrait des lecteurs d'écran.
*/

/* inline-block et non block : l'en-tête de la sidebar centre son contenu par
   `line-height: 60px`, ce qu'un élément de bloc ignorerait — le logo se
   collerait en haut et les boutons de contrôle passeraient à la ligne.
   L'ancien <img> était inline pour la même raison. */
.kd-logo {
    display: inline-block;
    vertical-align: middle;
    background-repeat: no-repeat;
    background-position: left center;
    background-size: contain;
    flex: none;
}

.kd-logo--sidebar {
    width: var(--kd-logo-sidebar-width);
    height: var(--kd-logo-sidebar-height);
    background-image: var(--kd-logo-sidebar);
}

/*------------------------------------------------------------------
[25. Boutons — géométrie non exprimable en variables Bootstrap]

Tout ce qui est COULEUR, taille de police, rayon ou rembourrage vit dans
tokens.css sous forme de --bs-btn-*. Ne subsiste ici que ce que Bootstrap
n'expose par aucune variable : la géométrie du bouton de Cockpit.

⚠️ NE RIEN AJOUTER ICI QUI SOIT EXPRIMABLE EN VARIABLE. Bootstrap 5.3
déclare `background-color: var(--bs-btn-bg)` sur `.btn` lui-même, et les
variantes (.btn-primary…) ne posent que les variables. Une propriété
directe écrite sur `.btn` gagne donc sur la variable et NEUTRALISE toutes
les variantes d'un coup. C'est exactement ce que faisait l'ancien thème :
son `.btn { background-color: #fff }` rendait tous les boutons blancs,
verts et rouges compris, dès qu'on retirait ses surcharges de variante.
*/

.btn {
    /* Centrage flex : les boutons portent souvent une icône Phosphor à côté
       du libellé, et l'alignement par ligne de base les décalait. */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* Hauteur constante quel que soit le contenu (icône seule, texte, ou les
       deux) — c'est ce qui aligne les barres d'outils. */
    min-height: 32px;
    min-width: 28px;
    position: relative;
    margin-bottom: 0;
    text-align: center;
    white-space: nowrap;
    cursor: pointer;
    letter-spacing: -.006em;
    background-image: none;
    transition: background 120ms ease-in, box-shadow 120ms ease-in,
                opacity .12s cubic-bezier(.4, 0, .6, 1),
                transform .12s cubic-bezier(.4, 0, .6, 1);
}

/* Bordure basse légèrement plus soutenue : le relief discret du bouton de
   Cockpit. Bootstrap n'a qu'un --bs-btn-border-color unique, d'où cette règle.
   Seulement sur le bouton par défaut : sur une variante pleine, la nuance
   passerait pour un défaut d'affichage. */
.btn:not([class*="btn-primary"]):not([class*="btn-success"]):not([class*="btn-danger"]):not([class*="btn-warning"]):not([class*="btn-info"]):not([class*="btn-secondary"]):not([class*="btn-outline"]):not(.btn-link) {
    border-bottom-color: rgba(33, 33, 33, .24);
}


/* Basket — bouton désactivé (panier plein) : bordure transparente */
.btn-default {
    --bs-btn-disabled-border-color: transparent;
}

/* Icône dans un bouton. `.btn` est déjà en inline-flex (ci-dessus) : l'icône
   est donc un élément flex, mais son GLYPHE, lui, reste calé sur la ligne de
   base. L'inline-flex sur l'icône elle-même recentre ce glyphe dans sa boîte.
   (Le thème ajoutait ici `position:relative; left:0; top:0` — sans effet,
   `left`/`top` à 0 sur du relatif ne déplacent rien. Retiré.) */
.btn i {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

/* Bouton-icône du bandeau supérieur (cloche, panier, réglages, QR, burger).
   Ce n'est PAS une variante Bootstrap : pas de `.btn`, donc aucun jeton
   --bs-btn-* ne s'y applique. Le halo rond est un ::before qu'on fait grandir,
   plutôt qu'un background-color, pour que la transition parte du centre. */
.btn-icon-link {
    position: relative;
    min-width: 24px;
    min-height: 24px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    user-select: none;
    cursor: pointer;
    background: transparent;
    border: none;
    box-shadow: none;
    padding: 0;
    line-height: 0;
}

.btn-icon-link:before {
    content: "";
    display: block;
    opacity: 0;
    position: absolute;
    inset: -2px;
    background: none;
    border-radius: 50%;
    box-sizing: border-box;
    transform: scale(.89);
    transition: all cubic-bezier(.4, 0, .2, 1) 240ms;
}

.btn-icon-link:hover:not(.active) {
    background: transparent;
}

.btn-icon-link:hover:not(.active):before {
    background-color: rgba(33, 33, 33, .1);
    opacity: 1;
    transform: scale(1);
}

/* Enfoncé : halo plus soutenu, et transition raccourcie pour que l'appui
   réponde tout de suite. */
.btn-icon-link:hover:not(.active):active:before,
.btn-icon-link:hover:not(.active).active:before {
    background-color: rgba(117, 117, 117, .26);
    opacity: 1;
    transform: scale(1);
    transition: all cubic-bezier(.4, 0, .2, 1) 44ms;
}

.btn-icon-link.active {
    background: none;
}

.btn-icon-link.active:before {
    opacity: 1;
    transform: scale(1);
    background-color: rgba(117, 117, 117, .26);
}

/* .invert : le même bouton posé sur un fond sombre. */
.btn-icon-link.invert:hover:not(.active) {
    color: rgba(255, 255, 255, .72);
}

.btn-icon-link.invert:hover:not(.active):before {
    background-color: rgba(255, 255, 255, .12);
}

.btn-icon-link.invert:hover:not(.active):active {
    color: #fff;
}

.btn-icon-link.invert:hover:not(.active):active:before {
    background-color: rgba(255, 255, 255, .18);
}

.initials-badge {
    width: 30px; /* largeur fixe */
    min-width: 30px;
    max-width: 30px;
    height: 24px;
    border-radius: 0px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 600;
    font-size: 11px;
}

.querymetrics-icon-chart {
    width: 1.8em;
    height: 1.8em;
    margin: 0px 5px 0px 5px;
}

.table-card .card-body {
    padding: 5px 16px 5px 16px;
}

.tab-content > .tab-pane {
    padding-top: 10px;
}


.dxbl-btn {
    --dxbl-btn-border-radius: 0px !important;
}

offcanvas-backdrop {
    z-index: 1040;
}

.offcanvas {
    z-index: 1045;
}

    .offcanvas.bb-offcanvas-lg {
        width: 600px;
    }

.offcanvas-fullwidth-menupinned {
    width: calc(100vw - var(--sidebar-pinned-width)) !important;
    max-width: calc(100vw - var(--sidebar-pinned-width)) !important;
}

.offcanvas-fullwidth-menuunpinned {
    width: calc(100vw - var(--sidebar-unpinned-widthcalc)) !important;
    max-width: calc(100vw - var(--sidebar-unpinned-widthcalc)) !important;
}


.modal-backdrop {
    display: none !important;
}

.modal {
    z-index: 2055;
}


.offcanvas-body {
    padding-top: 0px;
}

.header-navlinks > li > a {
    padding: 3px 3px;
    text-align: center;
    min-width: 40px;
}

.header-navlinks:after {
    content: "";
    border-bottom: none;
}

.nav-pills {
    --bs-nav-pills-border-radius: var(--bs-border-radius);
    --bs-nav-pills-link-active-color: var(--kd-on-primary);
    /* La pilule ACTIVE marque une sélection : elle prend le secondaire (--kd-accent,
       tokens.css). Le survol, juste dessous, garde le primaire — c'est une affordance,
       pas un état. Rendu inchangé : --kd-accent vaut var(--kd-primary) par défaut. */
    --bs-nav-pills-link-active-bg: var(--kd-accent);
}

    .nav-pills .nav-link {
        color: var(--gray-color);
        background-color: rgba(33, 33, 33, 0.05);
        margin-right: 10px;
        padding: 0.4rem 0.8rem;
    }

        .nav-pills .nav-link:focus {
            color: var(--kd-on-primary);
        }

        .nav-pills .nav-link:hover {
            background-color: var(--primary-color);
            color: var(--kd-on-primary);
        }


.nav-underline {
    --bs-nav-underline-gap: 1rem;
    --bs-nav-underline-border-width: 0.125rem;
    /* Onglet COURANT : sélection, donc secondaire (--kd-accent). */
    --bs-nav-underline-link-active-color: var(--kd-accent);
    gap: var(--bs-nav-underline-gap);
}

    .nav-underline .nav-link {
        color: var(--gray-color);
    }

        .nav-underline .nav-link:focus, .nav-link:hover {
            color: var(--gray-color);
        }

.tab-title-badge {
    position: relative;
    display: inline-block;
}

    .tab-title-badge .badge {
        margin-left: 3px;
        /*        position: absolute;
        top: -13px;
        right: -15px;*/
    }

/* ⚠️ C'EST CE CONTENEUR QUI DÉFILE, PAS LA FENÊTRE. Conséquence sur la barre
   supérieure : elle est son FRÈRE, posée en absolu dans .page-container, donc
   calée sur une boîte qui, elle, va jusqu'au bord de la fenêtre. Sans rien de
   plus, son bord droit tombait 15 px (une largeur d'ascenseur) à droite de celui
   des cartes, et recouvrait les 5 premiers pixels de l'ascenseur — visible dès
   que la page défile.
   `scrollbar-gutter: stable` réserve la gouttière EN TOUTES CIRCONSTANCES : sans
   elle, la barre se recalerait selon que la page déborde ou non, et l'écart
   sauterait d'un écran à l'autre. Le décalage correspondant est rendu à la barre
   via --kd-scrollbar-w (cf. § 28). */
.page-container .page-content-wrapper {
    /* On RACCOURCIT ce conteneur de défilement de la hauteur de la barre repliée du bandeau
       applicatif (cf. catégorie 47 en fin de fichier), au lieu de lui ajouter un padding-bas : une
       boîte en height 100% occupe la fenêtre entière quoi qu'il arrive, le bandeau continuerait de
       peindre par-dessus, et en border-box le padding rogne le contenu — c'est ce qui faisait passer
       le fil d'Ariane sous l'en-tête lors d'une première tentative, depuis abandonnée.
       Le § 43 (ex-theme.css, déménagé en TÊTE de ce fichier) pose min-height: 100% sur ce même
       sélecteur. La hauteur utilisée valant
       max(height, min-height), ce min-height ramenait la boîte à la fenêtre entière et annulait
       purement et simplement le raccourcissement : les DEUX doivent être réduits. */
    height: calc(100% - var(--app-dock-bar-height));
    min-height: calc(100% - var(--app-dock-bar-height));
    overflow-y: auto;
    z-index: 0;
}

/* UNE SEULE GOUTTIÈRE D'ASCENSEUR DANS LA COLONNE, ET ELLE EST TOUJOURS RÉSERVÉE.
   C'est ce qui rend l'écart de la barre CONSTANT : elle s'écarte d'exactement une
   largeur d'ascenseur (--kd-scrollbar-w), quel que soit l'écran et que la page
   déborde ou non.

   Reste à savoir QUI défile, et ça dépend du mode de PageWrapper — d'où la classe
   qu'il pose lui-même :
     .kd-scroll-wrapper (Home, FullPage)      → c'est le wrapper ;
     .kd-scroll-inner   (Normal, WithSidebar, WithRail) → c'est leur propre
       `container-fluid overflow-y-auto h-100vh-withheader`.

   ⚠️ RÉSERVER SUR LES DEUX EST LE PIÈGE. La gouttière avait d'abord été posée sur
   le wrapper sans condition : sur les pages à défilement interne elle ne servait
   à rien — le wrapper n'y déborde jamais — et laissait 15 px de blanc entre
   l'ascenseur réel et le bord de la carte de barre. Mesuré à 1400 px : marge
   droite de 25 px contre 10 à gauche.

   ⚠️ ET NE PAS LA RÉSERVER DU TOUT NE MARCHE PAS NON PLUS : sans réservation,
   l'écart n'existe que quand la page déborde. Mesuré : /alarms et /reports-center
   alignés (rien à faire défiler) mais /monitoring-center et l'explorateur
   d'équipements à 15 px, selon la seule quantité de contenu. */
.page-container .page-content-wrapper.kd-scroll-wrapper {
    scrollbar-gutter: stable;
}

.page-container .page-content-wrapper.kd-scroll-inner .container-fluid.overflow-y-auto {
    scrollbar-gutter: stable;
}

.accordion-header {
    margin-top: 0;
}

.accordion-button:not(.collapsed) {
    --bs-accordion-active-bg: var(--bs-accordion-btn-bg);
}

.sticky-top {
    position: sticky;
    top: 0px;
}

.separator-with-text {
    display: flex;
    align-items: center;
    text-align: center;
    margin: 1.5rem 0;
}

    .separator-with-text::before,
    .separator-with-text::after {
        content: "";
        flex: 1;
        border-bottom: 1px dashed #888;
    }

    .separator-with-text::before {
        margin-right: 0.75em;
    }

    .separator-with-text::after {
        margin-left: 0.75em;
    }

    .separator-with-text span {
        /* optionnel : si votre fond n'est pas blanc */
        background-color: #fff;
        padding: 0 0.5em;
    }

.invisible {
    position: absolute !important;
    /*visibility: hidden;*/
    z-index: -100;
}

.flex-fullsize {
    width: 100%;
    height: 100%;
    display: flex;
    position: relative;
}

.palette-active {
    color: var(--dxbl-tabs-tab-selected-color);
}

.fit-content {
    height: fit-content;
}

.disable-tab-page {
    cursor: default;
    pointer-events: none;
    text-decoration: none;
    color: lightgrey !important;
}

.page-container {
    position: relative;
}

/* --- LA BARRE SUPÉRIEURE A DISPARU -------------------------------------- */
/* Tout ce qui la posait — position absolue, hauteur du jeton, carte flottante à
   partir de 576 px — est parti avec elle. Le titre de l'écran, son retour et ses
   outils sont rendus par PageWrapper DANS la colonne de contenu (voir plus bas,
   .kd-page-head) : ils défilent avec elle, et ne réservent plus 68 px en
   permanence sur les ~74 écrans.

   Les règles de PEINTURE en `.header ...` (fond, liens, pastille, listes de
   notification) qui restaient dans le § 43 ont été retirées depuis : zéro
   porteur mesuré. */

/* Le contenu commence à la gouttière. UNE seule valeur, --kd-content-top, là où
   le « 60 » était recopié à la main dans quatre fichiers.
   ⚠️ Son invariant avec h-100vh-withheader : ce rembourrage ÉGALE ce que la
   classe retranche à 100vh. Les désaccorder fait dépasser ou flotter la colonne
   de la différence. */
.page-container .page-content-wrapper .content {
    padding-top: var(--kd-content-top);
    /* Le retrait GAUCHE, qui était la gouttière du menu. Depuis que le shell est
       bord à bord, --kd-shell-offset ne vaut plus que la largeur du menu : ce qui
       aère le contenu se déclare donc ici, avec les trois autres côtés, au lieu
       d'être caché dans un décalage de conteneur.
       Son propre jeton et non la gouttière, et plus large qu'elle : c'est le côté
       qui borde le menu (tokens.css, --kd-content-left). */
    padding-left: var(--kd-content-left);
}

/* Le retrait DROIT appartient à la ZONE DE CONTENU, pas au conteneur d'un mode
   de PageWrapper. Il vivait sur .kd-content-pad, que seuls les modes Normal /
   WithSidebar / WithRail portent : les modes Home et FullPage n'en avaient donc
   aucun et leur contenu filait jusqu'au bord de la fenêtre. Mesuré à 869 px avant
   correction : contenu à 854 pour une barre à 844. Posé ici, il vaut pour les cinq
   modes — et pour ContentNoPadding, qui remplace kd-content-pad par px-0/py-0.
   Largeur du rail d'outils PLUS le retrait, le rail étant maintenant collé au bord
   de la fenêtre. */
.page-container .page-content-wrapper .content {
    padding-right: calc(var(--kd-tools-offset) + var(--kd-shell-gap));
}

/* ⚠️ SAUF LÀ OÙ LA COLONNE DÉFILE ELLE-MÊME : ELLE VA JUSQU'AU RAIL (15/09/2026).
   Les modes Normal et WithSidebar défilent dans leur propre `container-fluid` (.kd-scroll-col).
   Les pages y posent des `.row` Bootstrap, dont les marges de −12 px ne tombaient dans aucun
   rembourrage : 12 px de défilement horizontal, une barre en bas de page sur /alarms,
   /reports-center, /monitoring-center… Et pas seulement la rangée de premier niveau : toute
   `.row` rangée dans une colonne pleine largeur finit 12 px au-delà du bord.
   Seule une boîte de défilement dont le rembourrage couvre ces 12 px les absorbe. La colonne
   prend donc les 10 px de gouttière du shell, et .kd-content-pad lui rend la gouttière de
   Bootstrap (plus bas). Mesuré à 1596 et 1200 px : débord 12 → 0, bord gauche inchangé, bord
   droit des cartes 2 px plus à gauche (12 de gouttière pour 10 de shell), ascenseur accolé au
   rail au lieu d'en être écarté de 10 px.
   ÉCARTÉ : `overflow-x: clip` sur .kd-page-body ne déplaçait rien, mais rognait ce qui déborde
   VRAIMENT — à 400 px, la barre d'outils de /alarms dépasse de 148 px et ne s'atteint qu'en
   faisant défiler la page. Une règle de shell ne doit pas masquer ce qu'un écran a de trop large.
   Le `:has` exclut ContentNoPadding (`px-0` en !important, rien à absorber) : ses deux tableaux
   de bord perdraient sinon 10 px de retrait droit. Préfixe complet : il doit battre la règle
   ci-dessus. */
.page-container .page-content-wrapper.kd-scroll-inner > .content:not(.kd-bleed):has(> .d-flex > .kd-content-pad) {
    padding-right: var(--kd-tools-offset);
}

/* ⚠️ UN ÉCRAN À RAIL N'EST PAS DU CONTENU POSÉ SUR UN FOND, C'EST UNE SURFACE.
   Les quatre explorateurs remplissent tout ce que le menu et le rail d'outils
   laissent : aucun retrait en haut ni à gauche, et à droite la seule largeur du
   rail. C'est ce qui rend inutiles les marges négatives que `.railnav` posait
   pour rattraper la gouttière — et qui ont coûté deux pièges (25 et 26).
   Posé par PageWrapper sur le `.content` du mode WithRail, lui seul. */
.page-container .page-content-wrapper .content.kd-bleed {
    padding-top: 0;
    padding-left: 0;
    padding-right: var(--kd-tools-offset);
}

/* LA COLONNE DES RÉGLAGES SE LIT COMME LE PANNEAU D'UN EXPLORATEUR (13/09/2026).

   Demande de Jérémy : « le sous menu dans settings devrait ressembler au panneau qu'on a dans
   explorer 2D et 3D » — sans le rail, sans ligne de titre et sans repli (tranché le 13/09).

   Trois écarts seulement séparaient les deux, mesurés à l'écran : le fond, la bordure et le
   rayon étaient DÉJÀ identiques (les deux lisent --kd-surface et --kd-border). Restaient la
   gouttière de 10 px qui empêchait la colonne de monter au bord (panneau d'explorateur à y=0,
   colonne des réglages à y=10), le `p-2` uniforme de PageWrapper là où le panneau laisse son
   corps porter son propre rembourrage, et deux défileurs emboîtés au lieu d'un.

   ⚠️ Ciblé par une classe posée sur .page-content-wrapper, et surtout PAS sur .left-sidebar :
   celle-ci a QUATRE porteurs — les 29 écrans de réglages, l'Explorateur de fichiers (400 px),
   l'Explorateur d'équipements, et l'Explorateur de métriques qui pose la classe lui-même, hors
   PageWrapper. Seuls les réglages changent.

   La colonne est une surface et monte au bord ; le CONTENU de droite, lui, reste du contenu
   posé sur un fond, et se place exactement comme sur la console d'alarmes (mode Normal) : à
   --kd-content-left de ce qui le borde à gauche, et à --kd-content-top du haut. */
.page-container .page-content-wrapper.kd-settings-shell .content {
    padding-top: 0;
    padding-left: 0;
}

/* La marge gauche du contenu (demande de Jérémy, 13/09 : « reprends celle de la console
   d'alarmes »). Sur la console, elle vient du padding-left de .content — ici retiré, puisqu'il
   tombait à gauche de la colonne et non entre elle et le contenu. `kd-content-pad` n'en donne
   AUCUNE (0 à gauche, cf. plus bas) : sans cet écart, le titre touchait la bordure de la colonne.
   Posée en `column-gap` sur la rangée plutôt qu'en rembourrage du contenu : elle tient aussi si
   un écran passe un jour ContentNoPadding, dont le `px-0` est en !important. */
.page-container .page-content-wrapper.kd-settings-shell .content > .d-flex {
    column-gap: var(--kd-content-left);
}

/* Les deux colonnes prennent la fenêtre entière, comme .kd-bleed-col : h-100vh-withheader
   retranche --kd-content-top, que .content ne pose plus ici — l'invariant du § 28 laisserait
   sinon 10 px vides sous la colonne (mesuré : 618 px pour une fenêtre de 628). */
.page-container .page-content-wrapper.kd-settings-shell .h-100vh-withheader {
    height: var(--app-viewport-height);
}

/* …et le contenu rend ce retrait haut pour lui seul : son titre se pose à la même hauteur que
   sur les ~70 autres écrans. Il est DANS le défileur, donc l'ascenseur, lui, part du haut. */
.page-container .page-content-wrapper.kd-settings-shell .kd-scroll-col {
    padding-top: var(--kd-content-top);
}

/* ⚠️ Les deux !important NE SONT PAS une facilité : PageWrapper pose `p-2 overflow-y-auto`
   sur la colonne, deux utilitaires Bootstrap eux-mêmes en !important. Sans eux, cette règle ne
   gagnait rien malgré sa spécificité — mesuré à la première vérification : 8 px et `auto`
   restés en place. Retirer les classes du composant partagé toucherait les autres écrans
   à barre latérale. */
.page-content-wrapper.kd-settings-shell .left-sidebar {
    /* Le rembourrage descend dans la liste, qui le porte déjà (.kd-section-nav). Le garder ici
       ajouterait 8 px à l'extérieur d'une colonne qui déborde déjà. */
    padding: 0 !important;
    /* Un seul défileur, et c'est la liste. Sans cela, la colonne et la liste défilent toutes les
       deux — mesuré : deux ascenseurs emboîtés sur /settings/general. */
    overflow-y: hidden !important;
}

/* Téléphone : la navigation des réglages disparaît (tranché le 15/09/2026). Ses 264 px ne laissaient que
   91 px au contenu sur un écran de 375 px (mesuré sur la fiche d'un utilisateur) — une colonne de texte
   coupée à chaque mot. Compromis assumé : sans elle, un autre écran de réglages ne s'atteint plus que par son
   adresse (l'entrée « Réglages » n'ouvre qu'« Options générales ») ; une liste déroulante en tête de page
   serait la suite si les réglages doivent se faire sur téléphone. Le contenu reprend la marge gauche qu'il
   laissait à la colonne. `!important` : PageWrapper pose
   `d-flex` sur la colonne, utilitaire Bootstrap lui-même en !important. */
@media (max-width: 767.98px) {
    .page-content-wrapper.kd-settings-shell .left-sidebar {
        display: none !important;
    }

    .page-container .page-content-wrapper.kd-settings-shell .content {
        padding-left: var(--kd-shell-gap);
    }
}

/* Le contenu s'aligne EXACTEMENT sur la carte de la barre : même bord gauche,
   même bord droit.

   Rien à ajouter à gauche : .page-container commence après le menu
   (--kd-shell-offset) et .content y ajoute la gouttière. Les helpers hérités p-t-15 et
   p-l-20 y ajoutaient 20 px, si bien que les cartes de la page démarraient 20 px
   à droite de la barre — visible à leur coin supérieur gauche.

   ⚠️ LE ZÉRO DE GAUCHE EST ACTIF, pas décoratif : il annule la gouttière propre à
   `.container-fluid` de Bootstrap (12 px). Le retrait gauche est posé sur .content —
   l'omettre ici laissait revenir les 12 px de Bootstrap (mesuré : cartes à 817 pour
   une barre à 844).
   À DROITE, C'EST L'INVERSE : la gouttière de Bootstrap est rendue, et elle est le
   seul retrait droit de la colonne, .content n'y ajoutant plus la gouttière du shell
   (voir « Sauf là où la colonne défile elle-même »). C'est elle qui absorbe les marges
   de −12 px des `.row` : à 0, elles faisaient défiler la colonne en largeur. */
/* ⚠️ Le rembourrage HAUT est passé à .kd-page-head : c'est la ligne de titre qui
   ouvre la colonne, et elle doit s'ouvrir de la même hauteur sur les cinq modes.
   Le laisser ici le RAJOUTERAIT aux 15 px du titre sur ces écrans-là seulement. */
.kd-content-pad {
    padding-top: 0;
    padding-left: 0;
    padding-right: calc(var(--bs-gutter-x, 1.5rem) * .5);
}


/* Carte agrandie (cardextended.js) : le reste du bloc vient de theme.css
   (tranche 5). Le `top` se calait à 59 px, soit la hauteur du header moins sa
   bordure — laquelle n'existe plus.

   ⚠️ `left: 280px` était un jumeau CODÉ EN DUR de --kd-shell-offset. Il ne
   coïncidait déjà qu'en menu DÉPLIÉ ; depuis que le décalage intègre le débord de
   la pastille (offset = 101 replié / 289 déplié) il ne coïncide plus nulle part.
   Branché sur le jeton — voir la règle plus bas.

   Note : `margin: 0` ne gagne que sur trois côtés ; le bas reste à 20px,
   posé par le .card { margin-bottom } de cette même catégorie. */
.card-maximized {
    position: fixed !important;
    /* ⚠️ Ce `left` doit suivre le bord gauche du CONTENU, c'est-à-dire le
       `margin-left` de .page-container (§ 28). Il valait 280px en dur — un
       jumeau de --kd-shell-offset qui ne coïncidait que menu DÉPLIÉ. Menu replié
       l'offset tombe à 101px, et la carte maximisée laissait 179px de vide à sa
       gauche.
       Le bloc mobile ci-dessous n'est pas facultatif : sous 992px,
       .page-container repasse à `margin-left: 0` (§ 28, régime mobile) alors
       que --kd-shell-offset garde sa valeur bureau — mesuré. Les deux règles
       restent côte à côte pour qu'elles ne redivorcent pas.
       Le retrait est celui du contenu (--kd-content-left), pas la gouttière :
       agrandie, la carte part du même bord que les autres. */
    left: calc(var(--kd-shell-offset) + var(--kd-content-left));
    top: var(--kd-content-top);
    /* Symétrique du `left` : la carte agrandie s'arrête au rail d'outils, sinon
       elle passerait dessous — le rail est en z-index supérieur, la carte
       disparaîtrait sur ses 60 px de droite. Elle garde le retrait du contenu des
       deux côtés : agrandie, elle reste une CARTE, pas une surface du shell. */
    right: calc(var(--kd-tools-offset) + var(--kd-shell-gap));
    bottom: 0;
    z-index: 600;
    margin: 0;
    width: auto;
}

@media (max-width: 575px) {
    .card-maximized {
        left: 0;
    }
}

/*------------------------------------------------------------------
[2. Quickview]
*/
.offcanvas-quickview {
    visibility: visible !important;
    /* Bootstrap ancre l'offcanvas au bas de la fenêtre (fixed ; bottom: 0 ; z-index 1045) : le bas
       du panneau (catalogue de widgets, filtres) passait sous le bandeau, sans qu'aucun défilement
       permette d'y accéder. Cf. catégorie 47. */
    bottom: var(--app-dock-bar-height);
}

    .offcanvas-quickview:before {
        position: absolute;
        content: "";
        width: 48px;
        height: 50px;
        background-color: #fff;
        top: 20%;
        left: -47px;
        box-shadow: 0 0 9px rgba(191, 191, 191, 0.36);
        border-left: 1px solid rgba(33, 33, 33, 0.14);
        border-bottom-left-radius: 4px;
        border-top-left-radius: 4px;
    }

    .offcanvas-quickview .offcanvas-quickview-toggle {
        display: flex;
        justify-content: center;
        align-items: center;
        position: absolute;
        width: 54px;
        height: 50px;
        top: 20%;
        left: -48px;
        text-align: center;
        line-height: 50px;
        cursor: pointer;
    }

    .offcanvas-quickview .offcanvas-quickview-content {
        padding: 15px;
    }


/*------------------------------------------------------------------
[3. Sizes]
*/

.w-35px {
    width: 35px;
}

.w-80px {
    width: 80px;
}

.w-150px {
    width: 150px;
}

.w-200px {
    width: 200px !important;
}

.w-250px {
    width: 250px !important;
}

.w-400px {
    width: 400px !important;
}

.h-80px {
    height: 80px;
}

.h-120px {
    height: 120px;
}

.h-150px {
    height: 150px;
}

.h-500px {
    height: 500px;
}

.h-800px {
    height: 800px;
}

/* ⚠️ .h-100vh et .h-100vh-noheader n'ont AUCUN porteur dans le dépôt (recherche
   exhaustive .razor/.cs/.css/.js). Leurs valeurs viennent telles quelles du
   gabarit acheté, et leurs noms mentent : « noheader » retranche PLUS que
   « withheader ». Conservées le temps du lot 3 — la feuille d'impression Pictet
   les cible par motif de nom (`[class*="h-100vh"]`), les supprimer la rendrait
   silencieusement inopérante.

   Les trois dimensionnent des conteneurs de défilement de PageWrapper. Elles passent par
   --app-viewport-height et non par 100vh : ancrées au viewport, elles ignoreraient tout
   raccourcissement de leur ancêtre et descendraient sous le bandeau applicatif (cf. catégorie 47). */
.h-100vh {
    height: calc(var(--app-viewport-height) - 150px)
}

.h-100vh-noheader {
    height: calc(var(--app-viewport-height) - 175px)
}

/* Seule classe des trois réellement utilisée — ses trois porteurs sont dans
   PageWrapper.razor. Les 115 px valaient 60 (barre supérieure) + 55 (bandeau du
   fil d'Ariane). Le bandeau a disparu, et la barre est devenue une carte : ce
   qu'il faut retrancher, c'est donc l'ordonnée où commence le contenu. */
.h-100vh-withheader {
    height: calc(var(--app-viewport-height) - var(--kd-content-top))
}

/* Colonne d'un écran BORD À BORD (mode WithRail) : pleine fenêtre, rien à
   retrancher. C'est le même invariant que ci-dessus — le retrait haut ÉGALE ce
   qu'on retire à 100vh — vérifié ici avec 0 des deux côtés, puisque .kd-bleed
   annule le rembourrage de .content. */
.kd-bleed-col {
    /* --app-viewport-height et non 100vh : boîte ancrée au viewport, elle passerait sinon sous le
       bandeau applicatif (§ 47). Le repli du jeton vaut 100vh, la valeur est inchangée sans bandeau. */
    height: var(--app-viewport-height);
}

.h-60 {
    height: 60% !important
}

/*------------------------------------------------------------------
[4. Table]
*/

.table-responsive {
    margin-bottom: 25px;
}

.pagination {
    --bs-pagination-padding-x: 0.7rem;
    --bs-pagination-padding-y: 0.1rem;
    --bs-pagination-font-size: 0.7rem;
    --bs-pagination-color: var(--primary-color);
}

    .pagination .active > .page-link, .pagination .page-link.active {
        z-index: 3;
        color: var(--bs-pagination-active-color);
        /* La page COURANTE est la portion sélectionnée du jeu de données : secondaire.
           --bs-pagination-color, plus haut, reste au primaire — il colore les pages
           CLIQUABLES, qui sont des actions. */
        background-color: var(--kd-accent);
        border-color: var(--kd-accent);
    }

.bb-grid-pagination-text {
    font-size: 0.7rem;
}



/*------------------------------------------------------------------
[5. Reports]
*/

.dashboard-designer {
    height: 92vh;
}

.full-screen-dashboard {
    margin: -20px -40px 0px -40px;
}



/*------------------------------------------------------------------
[6. Left Sidebar]
*/

.left-sidebar {
    background: var(--kd-surface);
    /* flex:none — la barre est un élément flex dont la largeur vient d'un style
       en ligne (SideBarWidth) ; sans cela elle se contracte sous la pression du
       contenu (mesuré : 198 px pour 236 demandés) et les libellés se rognent. */
    flex: none;
    border-right: 1px solid var(--kd-border);
}

/* `.left-rail` a été retiré. C'était un rail d'icônes que PageWrapper rendait en
   mode WithRail à partir de SideBarContent — et que PERSONNE n'a jamais utilisé :
   les quatre explorateurs rendent leur propre rail (RailNav), qui a son CSS scopé.
   WithRail désigne désormais ce qu'ils sont vraiment : une page qui porte son
   chrome et son titre elle-même. */

/* Flex layout pour coller .sidebar-footer en bas.
   On scope `display: flex` : sinon il écrase le `display: none` de theme.css
   en mobile et la sidebar reste visible en permanence. */
.page-sidebar {
    flex-direction: column;
    /* theme.css ancre la sidebar au bas de la fenêtre (fixed ; top: 0 ; bottom: 0 ; z-index 1000) :
       sans ce raccourcissement, le bandeau recouvre entièrement le .sidebar-footer (NavBar.razor),
       qui devient invisible et dont les clics atterrissent sur lui. Cf. catégorie 47. */
    bottom: var(--app-dock-bar-height);
}

@media (min-width: 576px) {
    .page-sidebar {
        display: flex;
    }
}

@media (max-width: 575px) {
    .page-sidebar.visible {
        display: flex;
    }
}

    .page-sidebar .sidebar-menu {
        flex: 1;
        height: auto !important;
        overflow: hidden;
        min-height: 0;
    }

        .page-sidebar .sidebar-menu .menu-items {
            height: 100% !important;
            /* Le pouce de l'ascenseur suit la couleur de texte ACTIVE du menu : `white`
               en dur le rendait invisible sur un menu clair. Valeur identique à l'usine
               (--kd-nav-fg-active = #ffffff). */
            scrollbar-color: var(--kd-nav-fg-active) transparent;
            scrollbar-width: thin;
        }

/* Mobile : la sidebar doit passer au-dessus du wrapper .dxbl-drawer
   (sinon les clics sur les items du menu sont interceptés par le drawer).
   Le DxDrawer de LayoutMain (.layout-drawer) wrap tout le contenu en
   position: relative; width/height: 100%, donc il couvre le viewport et
   capte les clics dans la zone du menu si la sidebar n'a pas de z-index. */
@media (max-width: 575px) {
    .page-sidebar {
        z-index: 1000;
    }

    .dxbl-drawer.layout-drawer {
        z-index: 1;
    }
}


/*------------------------------------------------------------------
[9. Grid stack]
*/

.grid-stack {
    background: #f5f5f5;
    position: relative;
    width: 100%;
}

    .grid-stack.mode-config {
        /*        border: 1px solid red;
        width: 100%;*/
    }

.grid-stack-item {
    /* overflow: hidden retiré pour laisser passer le box-shadow des cards.
       Le scroll interne du contenu est géré par .grid-stack-item-content > * (overflow:auto).
       ⚠️ À CONSERVER même si --kd-shadow-widget vaut `none` par défaut : c'est
       ce débordement ouvert qui rend le jeton réellement paramétrable, sinon un
       habillage client qui remet l'ombre la verrait rognée. */
    overflow: visible !important;
}

.grid-stack-item-content {
    /* Don't force width/height — GridStack positions this absolutely via
       top/right/bottom/left CSS variables.
       overflow:visible (override gridstack default overflow-x:hidden) pour
       laisser passer l'ombre portée de la card — à conserver pour la même
       raison que sur .grid-stack-item ci-dessus. */
    overflow: visible !important;
    display: flex;
    flex-direction: column;
}

    .grid-stack-item-content > * {
        flex: 1;
        min-height: 0;
        overflow: auto;
    }

    /* L'ombre d'un widget est un JETON à part, pas celle des cartes : voir
       --kd-shadow-widget dans tokens.css, qui porte le pourquoi et vaut `none`
       par défaut. Remplace `.grid-stack-item-content .card-default`, morte
       depuis le commit defaa90c (08/01/2026) qui a ôté `card-default` du markup
       de CardExtended — les widgets portaient une ombre depuis, sans décision.
       On écrit bien `box-shadow` et non `--bs-card-box-shadow` : Bootstrap
       déclare cette variable mais ne la consomme dans aucune règle (cf. § 27),
       la poser ne peindrait donc rien. */
    .grid-stack-item-content .card {
        margin: 0px;
        box-shadow: var(--kd-shadow-widget);
    }

/* Édition des dispositions Tablette/Mobile : contraindre la grille à la
   largeur cible centrée (~820px iPad portrait, ~420px iPhone Pro Max).
   GridStack recalcule la taille de ses cellules sur cette largeur → l'admin
   voit en direct le rendu que verra l'utilisateur du device. */
.grid-stack.grid-tablet-preview,
.grid-stack.grid-mobile-preview {
    margin: 0 auto;
    border: 1px solid #d0d0d0;
    border-radius: 12px;
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
    background: #fafafa;
    padding: 8px;
}

.grid-stack.grid-tablet-preview {
    max-width: 820px;
}

.grid-stack.grid-mobile-preview {
    max-width: 420px;
}

.cursor-hand {
    cursor: pointer;
}


/*------------------------------------------------------------------
[10. DevExpress Grid]
*/

.dxbl-grid, .dxbl-grid.dxbl-sm {
    font-size: 13px;
    --dxbl-grid-font-size: 13px;
    /* ⚠️ Le rgb() est indispensable : --kd-border-rgb ne contient que les
       trois composantes (« 227, 228, 228 »), pas une couleur. Sans l'enveloppe,
       la variable était invalide, et comme DevExpress la consomme en
       `background-color: var(--dxbl-grid-header-bg, unset)`, la déclaration
       entière s'effondrait : l'en-tête des grilles n'avait AUCUN fond (mesuré
       transparent) et laissait voir le --dxbl-grid-bg #f4f4f4. Corrigé, il
       porte enfin la bande grise voulue. Les trois lignes voisines
       enveloppaient déjà correctement, d'où l'incohérence. */
    --dxbl-grid-header-bg: rgb(var(--kd-border-rgb));
    /* Titres de colonnes. Ils étaient à #909298 (= --kd-text-muted), ce qui
       donnait 2,4:1 sur la bande #E3E4E4 ci-dessus — et déjà 2,8:1 avant que
       cette bande ne soit réparée. Les deux échouaient le seuil AA de 4,5:1,
       qui s'applique ici sans tolérance : les en-têtes font 13px, donc du
       « petit texte ».
       #5c5f66 mesure 5,0:1 sur la bande. Le levier a été pris sur le TEXTE et
       non sur la bande : celle-ci est --kd-border, consommée 45 fois
       ailleurs (zébrage des lignes, fond de focus), donc l'éclaircir aurait
       déplacé bien plus que l'en-tête. Jeton dédié pour qu'un habillage client
       puisse suivre. */
    --kd-grid-header-color: #5c5f66;
    --dxbl-grid-header-color: var(--kd-grid-header-color);
    --dxbl-grid-focus-bg: rgba(var(--kd-border-rgb), 1);
    --dxbl-grid-focus-color: inherit;
    --dxbl-grid-text-cell-padding-y: 0.4rem !important;
    --dxbl-grid-text-cell-padding-x: 0.25rem !important;
    /* Zébrage + survol des lignes de données (cf. GridStyling.StripedRows) */
    --kd-grid-stripe-bg: rgba(var(--kd-border-rgb), 0.4);
    --kd-grid-hover-bg: rgba(var(--kd-border-rgb), 0.75);
}

    .dxbl-grid .dxbl-grid-table > thead > tr > th {
        text-transform: uppercase;
    }

.dxbl-toolbar {
    --dxbl-toolbar-font-size: 13px;
    font-size: 13px;
}

.dxbl-dropdown, .dxbl-itemlist-dropdown {
    --dxbl-dropdown-font-size: 13px;
    font-size: var(--dxbl-dropdown-font-size);
}

.dxbl-text {
    --dxbl-text-font-size: 13px;
    font-size: var(--dxbl-text-font-size);
}

.dxbl-calendar {
    --dxbl-calendar-font-size: 13px;
    font-size: var(--dxbl-calendar-font-size);
}

.dxbl-btn {
    --dxbl-btn-font-size: 13px;
    font-size: var(--dxbl-btn-font-size);
}

/* CONTRÔLE ADVERSE DU LOT C — la règle ci-dessus n'a JAMAIS peint les boutons.
   `.dxbl-btn` se pose sur un <button> (le thème DevExpress écrit
   `.dxbl-btn:disabled`, qui ne matche qu'un contrôle de formulaire), et le verrou
   `button { font-size: 12px !important }` le rabattait à 12. Lever le verrou sans
   rien dire faisait donc grandir d'un pixel TOUS les boutons DevExpress de
   l'application d'un coup — DxButton, barres d'outils, colonnes de commande des
   grilles, boutons internes des éditeurs.
   Même arbitrage que pour `.dxbl-text-edit` juste en dessous : on reste sur la
   taille RÉELLEMENT rendue aujourd'hui. Le sélecteur vise l'ÉLÉMENT (0-1-1), donc
   il ne touche que ce que le verrou touchait : un `.dxbl-btn` posé sur autre chose
   qu'un <button> garde les 13px de la règle ci-dessus.
   Pour passer réellement les boutons DevExpress à 13px, supprimer ce bloc — c'est
   un choix graphique, pas une correction. */
button.dxbl-btn {
    font-size: var(--kd-fs-base);
}

/* LOT C — épinglage rendu nécessaire par la levée du verrou de police
   (§ 44, `label, input, button, select, textarea`). C'est le SEUL conteneur
   DevExpress que cette section avait oublié, et le plus visible : le thème
   DevExpress pose `--dxbl-text-edit-font-size: 0.875rem` sur `.dxbl-text-edit`,
   et son champ interne est en `font-size: inherit`. Tant que le !important
   tenait, tous les éditeurs — DxTextBox, DxComboBox, DxDateEdit, DxSpinEdit,
   DxMemo — rendaient 12 px ; sans lui, ils seraient passés d'un coup à 14.
   13 px comme ses voisins de section serait déjà un changement : on reste sur
   la taille RÉELLEMENT rendue aujourd'hui, le jeton de base. */
.dxbl-text-edit {
    --dxbl-text-edit-font-size: var(--kd-fs-base);
}

/* CONTRÔLE ADVERSE DU LOT C — l'épinglage ci-dessus (0-1-0) ne tient PAS partout.
   Le thème DevExpress redéclare --dxbl-text-edit-font-size avec une spécificité
   SUPÉRIEURE dans quatre cas, où il gagne donc quel que soit l'ordre de chargement :
       .dxbl-text-edit.dxbl-sm                  0.75rem   (12px, sans effet)
       .dxbl-text-edit.dxbl-lg                  1rem      -> 16px
       .dxbl-grid .dxbl-text-edit               = --dxbl-grid-font-size, 13px ici
       .dxbl-pager .dxbl-pager-page-edit        = --dxbl-pager-font-size, 13px en grille
   Autrement dit, la ligne de filtre d'une grille, son formulaire d'édition, sa zone
   de recherche et le champ « aller à la page » du pager seraient tous passés de 12
   à 13, et un éditeur en SizeMode.Large de 12 à 16.
   On épingle donc le CHAMP lui-même, qui est exactement ce que le verrou visait.
   Spécificité identique à celle du `font-size: inherit` de DevExpress sur les mêmes
   sélecteurs, et cette feuille est chargée après la sienne : on gagne à l'ordre. */
.dxbl-text-edit > .dxbl-text-edit-input,
.dxbl-text-edit > .dxbl-text-edit-template .dxbl-text-edit-input,
.dxbl-text-edit > textarea {
    font-size: var(--kd-fs-base);
}

.dxbl-checkbox {
    --dxbl-checkbox-font-size: 13px;
    --dxbl-checkbox-check-element-size: 1rem;
    --dxbl-checkbox-radio-size: 1rem;
    --dxbl-checkbox-radio-check-size: 1rem;
    font-size: var(--dxbl-checkbox-font-size);
}

.dxbl-list-box, .dxbl-list-box-render-container {
    --dxbl-list-box-font-size: 13px;
    font-size: var(--dxbl-list-box-font-size);
}

/* Masque les bordures verticales dans le grid DevExpress */
.dxbl-grid td,
.dxbl-grid th {
    border-right: none !important;
    border-left: none !important;
}

.dxbl-grid .dxbl-grid-table > tbody > tr > td:first-child, .dxbl-grid .dxbl-grid-table > tbody > tr > th:first-child:not(.dxbl-grid-bordered-left), .dxbl-grid .dxbl-grid-table > tfoot > tr > td:first-child, .dxbl-grid .dxbl-grid-table > tfoot > tr > th:first-child:not(.dxbl-grid-bordered-left), .dxbl-grid .dxbl-grid-table > thead > tr > td:first-child, .dxbl-grid .dxbl-grid-table > thead > tr > th:first-child:not(.dxbl-grid-bordered-left) {
    border-left-width: 0;
    /*padding-left: 17px !important;*/
}

.dxbl-grid-table td {
    border-right: 1px solid #dee2e6 !important;
    border-left: 1px solid #dee2e6 !important;
    border-top: none !important;
    border-bottom: none !important;
}

.dxbl-grid-table th {
    border-right: 1px solid #dee2e6 !important;
    border-left: 1px solid #dee2e6 !important;
}

/* Zébrage + survol des grids marqués via GridStyling.StripedRows (classe kd-grid-striped-row).
   Posé en classe (et non en style inline) pour rester thémable ; on cible la cellule (> td) et
   le survol est déclaré APRÈS le zébrage : à spécificité égale, :hover l'emporte. */
.dxbl-grid .dxbl-grid-table > tbody > tr.kd-grid-striped-row:nth-child(even) > td {
    background-color: var(--kd-grid-stripe-bg);
}

.dxbl-grid .dxbl-grid-table > tbody > tr.kd-grid-striped-row:hover > td {
    background-color: var(--kd-grid-hover-bg);
}

/*------------------------------------------------------------------
[11. DevExpress Treeview]
*/

.dxbl .dxbl-image, .dxbl-sm .dxbl-image {
    --dxbl-image-height: 0.8rem;
    --dxbl-image-width: 0.8rem;
    --dxbl-image-margin-x: 0;
    --dxbl-image-margin-y: 0.0625rem;
}

/* ⚠️ CORRIGÉ (lot B) — CHANGEMENT DE RENDU VOULU. Les deux règles écrivaient
   `opacity: var(--dxbl-treeview-btn-hover-color, var(--primary-color, 70%))` :
   opacity n’accepte qu’un nombre ou un pourcentage, jamais une couleur. Quelle que
   soit la branche empruntée, la valeur substituée était une COULEUR, la déclaration
   devenait invalide au calcul et opacity retombait sur sa valeur initiale, 1 — le
   70 % écrit au fond du repli n’a jamais rien peint, et la règle de survol ne
   distinguait rien de celle de repos (même couleur, même opacité morte).
   Rétabli tel qu’écrit : 70 % au repos, plein au survol. La COULEUR ne bouge pas. */
.dxbl-treeview .dxbl-treeview-item > .dxbl-treeview-item-content > .dxbl-btn > svg.dxbl-image {
    color: var(--primary-color);
    opacity: .7;
}

.dxbl-treeview .dxbl-treeview-item > .dxbl-treeview-item-content > .dxbl-btn:not(.dxbl-disabled):not(:disabled):hover > svg.dxbl-image {
    color: var(--primary-color);
    opacity: 1;
}

.dxbl-treeview.dxbl-sm:not(.dxbl-treeview-no-expand-btn) .dxbl-treeview-checkbox-check-all {
    padding-left: 0rem;
}

.dxbl-treeview .dxbl-treeview-item > .dxbl-treeview-item-content > .dxbl-btn:not(.dxbl-nav-expand-btn-custom):not(.dxbl-disabled):not(:disabled):hover::before {
    content: "";
    position: absolute;
    z-index: -1;
    /* left: calc(0px - var(--dxbl-border-width-with-scaling,var(--dxbl-btn-border-width,0px))); */
    /* top: calc(0px - var(--dxbl-border-width-with-scaling,var(--dxbl-btn-border-width,0px))); */
    /* right: calc(0px - var(--dxbl-border-width-with-scaling,var(--dxbl-btn-border-width,0px))); */
    /* bottom: calc(0px - var(--dxbl-border-width-with-scaling,var(--dxbl-btn-border-width,0px))); */
    /* border-radius: inherit; */
    opacity: 10%;
}

.dxbl-treeview .dxbl-treeview-item > .dxbl-treeview-item-content > .dxbl-btn:hover::before {
    content: "";
    position: absolute;
    z-index: -1;
    left: calc(0px - var(--dxbl-border-width-with-scaling,var(--dxbl-btn-border-width,0px)));
    top: calc(0px - var(--dxbl-border-width-with-scaling,var(--dxbl-btn-border-width,0px)));
    right: calc(0px - var(--dxbl-border-width-with-scaling,var(--dxbl-btn-border-width,0px)));
    bottom: calc(0px - var(--dxbl-border-width-with-scaling,var(--dxbl-btn-border-width,0px)));
    border-radius: inherit;
    opacity: 10%;
}

.dxbl-treeview .dxbl-treeview-item > .dxbl-treeview-item-content > .dxbl-btn:not(.dxbl-nav-expand-btn-custom):not(.dxbl-disabled):not(:disabled):hover::before {
    content: "";
    position: absolute;
    z-index: -1;
    left: calc(0px - var(--dxbl-border-width-with-scaling,var(--dxbl-btn-border-width,0px)));
    top: calc(0px - var(--dxbl-border-width-with-scaling,var(--dxbl-btn-border-width,0px)));
    right: calc(0px - var(--dxbl-border-width-with-scaling,var(--dxbl-btn-border-width,0px)));
    bottom: calc(0px - var(--dxbl-border-width-with-scaling,var(--dxbl-btn-border-width,0px)));
    border-radius: inherit;
    opacity: 10%;
}



.dxbl-treeview.dxbl-sm .dxbl-treeview-item .dxbl-treeview-item-content .dxbl-checkbox-check-element {
    margin: 0;
}

.dxbl-treeview .dxbl-text, .dxbl-treeview label {
    --dxbl-text-font-size: 13px;
    font-size: var(--dxbl-text-font-size) !important;
}

.dxbl-treeview .dxbl-sm .dxbl-text, .dxbl-treeview .dxbl-sm.dxbl-text {
    --dxbl-text-padding-x: 0;
    --dxbl-text-padding-y: 0;
}

.dxbl-checkbox:not(.dxbl-checkbox-switch):not(.dxbl-checkbox-radio) .dxbl-checkbox-check-element {
    width: var(--dxbl-checkbox-check-element-size);
    min-width: var(--dxbl-checkbox-check-element-size);
    height: var(--dxbl-checkbox-check-element-size);
    min-height: var(--dxbl-checkbox-check-element-size);
    /* border-radius: .1875rem; */
}

.dxbl-checkbox:not(.dxbl-checkbox-switch):not(.dxbl-checkbox-radio).dxbl-checkbox-unchecked .dxbl-checkbox-check-element::before {
    content: "";
    position: absolute;
    z-index: 0;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    border-radius: 0px;
    background-color: var(--dxbl-checkbox-check-element-unchecked-bg,currentcolor);
    opacity: var(--dxbl-checkbox-check-element-unchecked-bg,50%);
    border: 1px solid var(--dxbl-checkbox-check-element-unchecked-border-color,currentcolor);
    opacity: var(--dxbl-checkbox-check-element-unchecked-border-color,50%);
}

.dxbl-treeview .dxbl-checkbox > .dxbl-checkbox-check-element {
    /* margin: .25rem; */
}

.dxbl-checkbox.dxbl-sm .dxbl-checkbox-check-element {
    /* margin: .25rem 0; */
}

.dxbl-treeview .dxbl-checkbox.dxbl-treeview-checkbox-check-all {
    --dxbl-checkbox-unchecked-focus-shadow-color: transparent;
    --dxbl-checkbox-checked-focus-shadow-color: transparent;
    display: -webkit-box;
    display: -webkit-flex;
    display: -ms-flexbox;
    display: flex;
    align-items: center;
    /* border-bottom: var(--dxbl-treeview-check-all-border-bottom-width) var(--dxbl-treeview-check-all-border-bottom-style) var(--dxbl-treeview-check-all-border-bottom-color,var(--bs-border-color,var(--bs-gray-400,#ced4da))); */
    padding-bottom: 0rem;
}

/* Arbres à cases (.ckp-treeview) : rendre l'état INTERMÉDIAIRE lisible. Le thème DevExpress lui donne le
   même fond plein que « coché » avec un trait blanc par-dessus — à la taille Small le trait se voit à peine
   et un parent partiellement sélectionné passe pour coché (cf. filtre de classes par rôle : on croit que
   cocher une sous-classe « recoche » le parent). Ici : case creuse + trait à la couleur d'accent, donc
   distincte au premier coup d'œil de « coché » (plein) comme de « décoché » (vide). Sélecteurs alignés sur
   la spécificité du thème (:not(switch):not(radio)) pour gagner sans !important. */
.ckp-treeview .dxbl-checkbox:not(.dxbl-checkbox-switch):not(.dxbl-checkbox-radio).dxbl-checkbox-indeterminate .dxbl-checkbox-check-element {
    background-color: transparent;
    border: 1px solid var(--dxbl-checkbox-check-element-checked-bg, currentcolor);
}

.ckp-treeview .dxbl-checkbox:not(.dxbl-checkbox-switch):not(.dxbl-checkbox-radio).dxbl-checkbox-indeterminate .dxbl-checkbox-check-element::before {
    background-color: var(--dxbl-checkbox-check-element-checked-bg, currentcolor);
}



/* Le § 12 (Choices.js) a été supprimé : Cockpit n'utilise pas cette
   bibliothèque. Son unique appel (KDCockpit.WebComponents/wwwroot/clock.js:16-17)
   est en commentaire, elle n'est pas dans wwwroot/assets/libs/ et App.razor ne la
   charge pas. Ses deux règles étaient les seules consommatrices de
   --primary-color-darker, retirée de tokens.css dans le même mouvement.
   Numérotation laissée à trou : renuméroter obligerait à reprendre chaque renvoi
   « cf. § N » du fichier et des .razor.css. */

/*------------------------------------------------------------------
[15. DevExpress Tabs from blazering template]
*/

.dxbl-tabs {
    --dxbl-tabs-color: #212529;
    --dxbl-tabs-bg: var(--kd-surface);
    --dxbl-tabs-font-family: inherit;
    --dxbl-tabs-font-size: 0.875rem;
    --dxbl-tabs-line-height: 1.4285;
    --dxbl-tabs-padding-y: 0;
    --dxbl-tabs-padding-top: var(--dxbl-tabs-padding-y);
    --dxbl-tabs-padding-bottom: var(--dxbl-tabs-padding-y);
    --dxbl-tabs-padding-x: 0;
    --dxbl-tabs-padding-left: var(--dxbl-tabs-padding-x);
    --dxbl-tabs-padding-right: var(--dxbl-tabs-padding-x);
/*    --dxbl-tabs-separator-border-color: #d2d2d2;
    --dxbl-tabs-separator-border-style: solid;
    --dxbl-tabs-separator-border-width: 1px;*/
    --dxbl-tabs-tab-padding-x: 1rem;
    --dxbl-tabs-tab-padding-y: 0.25rem;
    --dxbl-tabs-tab-border-style: solid;
    --dxbl-tabs-tab-border-color: transparent;
    --dxbl-tabs-tab-border-width: 1px;
    --dxbl-tabs-tab-selected-bg: unset;
    /* SÉLECTION → secondaire. Le contour de focus et l'indicateur de dépôt, plus bas,
       restent au primaire : ils signalent une action, pas un état choisi. */
    --dxbl-tabs-tab-selected-color: var(--kd-accent);
    --dxbl-tabs-tab-hover-color: inherit;
    --dxbl-tabs-tab-hover-bg: unset;
    --dxbl-tabs-tab-width: unset;
    --dxbl-tabs-tab-btn-bg: transparent;
    --dxbl-tabs-tab-btn-color: unset;
    --dxbl-tabs-image-spacing: 0.375rem;
    --dxbl-tabs-scroll-btn-color: unset;
    --dxbl-tabs-scroll-btn-hover-color: unset;
    --dxbl-tabs-scroll-btn-disabled-color: unset;
    --dxbl-tabs-tab-focus-outline-width: 0.125rem;
    --dxbl-tabs-tab-focus-outline-color: var(--primary-color);
    --dxbl-tabs-close-btn-color: #888;
    --dxbl-tabs-close-btn-hover-bg: #dedede;
    --dxbl-tabs-close-btn-hover-color: #222;
    --dxbl-tabs-button-spacing: 0.375rem;
    --dxbl-tabs-padding-right-close-btn: calc(var(--dxbl-tabs-tab-padding-x) - 0.375rem);
    --dxbl-tabs-tab-drag-bg-color: var(--kd-surface);
    --dxbl-tabs-close-btn-width: 1.5rem;
    --dxbl-tabs-close-btn-height: 1.5rem;
    --dxbl-tabs-tab-drag-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.12);
    --dxbl-tabs-tab-drop-indicator-color: color-mix(in srgb, var(--primary-color) 95%, white 5%);
    --dxbl-tabs-content-color: #212529;
    --dxbl-tabs-content-font-size: 0.875rem;
    --dxbl-tabs-content-line-height: 1.4285;
    display: block
}

    .dxbl-tabs.dxbl-tabs-top > .dxbl-tabs-tablist {
        border-bottom: var(--dxbl-tabs-separator-border-width) var(--dxbl-tabs-separator-border-style) var(--dxbl-tabs-separator-border-color);
        border-top-left-radius: 0;
        border-top-right-radius: 0;
        padding-bottom: 0
    }

        .dxbl-tabs.dxbl-tabs-top > .dxbl-tabs-tablist > .dxbl-scroll-viewer,
        .dxbl-tabs.dxbl-tabs-top > .dxbl-tabs-tablist > ul {
            margin-bottom: calc(-1 * var(--dxbl-tabs-separator-border-width))
        }

            .dxbl-tabs.dxbl-tabs-top > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl),
            .dxbl-tabs.dxbl-tabs-top > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl) {
                border-top-left-radius: 0;
                border-top-right-radius: 0
            }

                .dxbl-tabs.dxbl-tabs-top > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active,
                .dxbl-tabs.dxbl-tabs-top > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active {
                    border-bottom-color: var(--dxbl-tabs-bg)
                }

                    .dxbl-tabs.dxbl-tabs-top > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active::after,
                    .dxbl-tabs.dxbl-tabs-top > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active::after {
                        height: 3px;
                        left: 0;
                        right: 0;
                        margin-bottom: -1px;
                        bottom: 0
                    }

                    .dxbl-tabs.dxbl-tabs-top > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active:not(.dxbl-disabled):not(:disabled):hover,
                    .dxbl-tabs.dxbl-tabs-top > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active:not(.dxbl-disabled):not(:disabled):hover {
                        border-bottom-color: var(--dxbl-tabs-bg)
                    }

        .dxbl-tabs.dxbl-tabs-top > .dxbl-tabs-tablist > .dxbl-btn {
            margin-bottom: calc(-1 * var(--dxbl-tabs-separator-border-width))
        }

    .dxbl-tabs.dxbl-tabs-right {
        display: -webkit-box;
        display: -webkit-flex;
        display: -ms-flexbox;
        display: flex;
        --dxbl-tabs-tab-width: 120px;
        height: 100%;
        -webkit-box-orient: horizontal;
        -webkit-box-direction: reverse;
        -webkit-flex-direction: row-reverse;
        -ms-flex-direction: row-reverse;
        flex-direction: row-reverse
    }

        .dxbl-tabs.dxbl-tabs-right > .dxbl-tabs-tablist > ul {
            margin-bottom: 0
        }

        .dxbl-tabs.dxbl-tabs-right > .dxbl-tabs-tablist {
            border-left: var(--dxbl-tabs-separator-border-width) var(--dxbl-tabs-separator-border-style) var(--dxbl-tabs-separator-border-color);
            border-top-right-radius: 0;
            border-bottom-right-radius: 0;
            padding-left: 0;
            display: -webkit-box;
            display: -webkit-flex;
            display: -ms-flexbox;
            display: flex;
            -webkit-box-orient: vertical;
            -webkit-box-direction: normal;
            -webkit-flex-direction: column;
            -ms-flex-direction: column;
            flex-direction: column
        }

            .dxbl-tabs.dxbl-tabs-right > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul,
            .dxbl-tabs.dxbl-tabs-right > .dxbl-tabs-tablist > ul {
                -webkit-box-orient: vertical;
                -webkit-box-direction: normal;
                -webkit-flex-direction: column;
                -ms-flex-direction: column;
                flex-direction: column
            }

                .dxbl-tabs.dxbl-tabs-right > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl) > .dxbl-tabs-text-container,
                .dxbl-tabs.dxbl-tabs-right > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl) > .dxbl-tabs-text-container {
                    overflow: hidden;
                    width: calc(100% + var(--dxbl-tabs-separator-border-width))
                }

            .dxbl-tabs.dxbl-tabs-right > .dxbl-tabs-tablist > .dxbl-btn {
                width: 100%
            }

            .dxbl-tabs.dxbl-tabs-right > .dxbl-tabs-tablist > .dxbl-scroll-viewer,
            .dxbl-tabs.dxbl-tabs-right > .dxbl-tabs-tablist > ul {
                margin-left: calc(-1 * var(--dxbl-tabs-separator-border-width))
            }

                .dxbl-tabs.dxbl-tabs-right > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl),
                .dxbl-tabs.dxbl-tabs-right > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl) {
                    border-top-right-radius: 0;
                    border-bottom-right-radius: 0
                }

                    .dxbl-tabs.dxbl-tabs-right > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active,
                    .dxbl-tabs.dxbl-tabs-right > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active {
                        border-left-color: var(--dxbl-tabs-bg)
                    }

                        .dxbl-tabs.dxbl-tabs-right > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active::after,
                        .dxbl-tabs.dxbl-tabs-right > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active::after {
                            width: 3px;
                            top: -1px;
                            bottom: -1px;
                            margin-left: -1px;
                            left: 0
                        }

                        .dxbl-tabs.dxbl-tabs-right > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active:not(.dxbl-disabled):not(:disabled):hover,
                        .dxbl-tabs.dxbl-tabs-right > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active:not(.dxbl-disabled):not(:disabled):hover {
                            border-left-color: var(--dxbl-tabs-bg)
                        }

            .dxbl-tabs.dxbl-tabs-right > .dxbl-tabs-tablist > .dxbl-btn {
                margin-left: calc(-1 * var(--dxbl-tabs-separator-border-width))
            }

    .dxbl-tabs.dxbl-tabs-bottom {
        display: -webkit-box;
        display: -webkit-flex;
        display: -ms-flexbox;
        display: flex;
        -webkit-box-orient: vertical;
        -webkit-box-direction: reverse;
        -webkit-flex-direction: column-reverse;
        -ms-flex-direction: column-reverse;
        flex-direction: column-reverse
    }

        .dxbl-tabs.dxbl-tabs-bottom > .dxbl-tabs-tablist > ul {
            margin-bottom: 0
        }

        .dxbl-tabs.dxbl-tabs-bottom > .dxbl-tabs-tablist {
            border-top: var(--dxbl-tabs-separator-border-width) var(--dxbl-tabs-separator-border-style) var(--dxbl-tabs-separator-border-color);
            border-bottom-left-radius: 0;
            border-bottom-right-radius: 0;
            padding-top: 0
        }

            .dxbl-tabs.dxbl-tabs-bottom > .dxbl-tabs-tablist > .dxbl-scroll-viewer,
            .dxbl-tabs.dxbl-tabs-bottom > .dxbl-tabs-tablist > ul {
                margin-top: calc(-1 * var(--dxbl-tabs-separator-border-width))
            }

                .dxbl-tabs.dxbl-tabs-bottom > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl),
                .dxbl-tabs.dxbl-tabs-bottom > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl) {
                    border-bottom-left-radius: 0;
                    border-bottom-right-radius: 0
                }

                    .dxbl-tabs.dxbl-tabs-bottom > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active,
                    .dxbl-tabs.dxbl-tabs-bottom > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active {
                        border-top-color: var(--dxbl-tabs-bg)
                    }

                        .dxbl-tabs.dxbl-tabs-bottom > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active::after,
                        .dxbl-tabs.dxbl-tabs-bottom > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active::after {
                            height: 3px;
                            left: 0;
                            right: 0;
                            margin-top: -1px;
                            top: 0
                        }

                        .dxbl-tabs.dxbl-tabs-bottom > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active:not(.dxbl-disabled):not(:disabled):hover,
                        .dxbl-tabs.dxbl-tabs-bottom > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active:not(.dxbl-disabled):not(:disabled):hover {
                            border-top-color: var(--dxbl-tabs-bg)
                        }

            .dxbl-tabs.dxbl-tabs-bottom > .dxbl-tabs-tablist > .dxbl-btn {
                margin-top: calc(-1 * var(--dxbl-tabs-separator-border-width))
            }

    .dxbl-tabs.dxbl-tabs-left {
        display: -webkit-box;
        display: -webkit-flex;
        display: -ms-flexbox;
        display: flex;
        --dxbl-tabs-tab-width: 120px;
        height: 100%;
        -webkit-box-orient: horizontal;
        -webkit-box-direction: normal;
        -webkit-flex-direction: row;
        -ms-flex-direction: row;
        flex-direction: row
    }

        .dxbl-tabs.dxbl-tabs-left > .dxbl-tabs-tablist > ul {
            margin-bottom: 0
        }

        .dxbl-tabs.dxbl-tabs-left > .dxbl-tabs-tablist {
            border-right: var(--dxbl-tabs-separator-border-width) var(--dxbl-tabs-separator-border-style) var(--dxbl-tabs-separator-border-color);
            border-top-left-radius: 0;
            border-bottom-left-radius: 0;
            padding-right: 0;
            display: -webkit-box;
            display: -webkit-flex;
            display: -ms-flexbox;
            display: flex;
            -webkit-box-orient: vertical;
            -webkit-box-direction: normal;
            -webkit-flex-direction: column;
            -ms-flex-direction: column;
            flex-direction: column
        }

            .dxbl-tabs.dxbl-tabs-left > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul,
            .dxbl-tabs.dxbl-tabs-left > .dxbl-tabs-tablist > ul {
                -webkit-box-orient: vertical;
                -webkit-box-direction: normal;
                -webkit-flex-direction: column;
                -ms-flex-direction: column;
                flex-direction: column
            }

                .dxbl-tabs.dxbl-tabs-left > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl) > .dxbl-tabs-text-container,
                .dxbl-tabs.dxbl-tabs-left > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl) > .dxbl-tabs-text-container {
                    overflow: hidden;
                    width: calc(100% + var(--dxbl-tabs-separator-border-width))
                }

            .dxbl-tabs.dxbl-tabs-left > .dxbl-tabs-tablist > .dxbl-btn {
                width: 100%
            }

            .dxbl-tabs.dxbl-tabs-left > .dxbl-tabs-tablist > .dxbl-scroll-viewer,
            .dxbl-tabs.dxbl-tabs-left > .dxbl-tabs-tablist > ul {
                margin-right: calc(-1 * var(--dxbl-tabs-separator-border-width))
            }

                .dxbl-tabs.dxbl-tabs-left > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl),
                .dxbl-tabs.dxbl-tabs-left > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl) {
                    border-top-left-radius: 0;
                    border-bottom-left-radius: 0
                }

                    .dxbl-tabs.dxbl-tabs-left > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active,
                    .dxbl-tabs.dxbl-tabs-left > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active {
                        border-right-color: var(--dxbl-tabs-bg)
                    }

                        .dxbl-tabs.dxbl-tabs-left > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active::after,
                        .dxbl-tabs.dxbl-tabs-left > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active::after {
                            width: 3px;
                            top: -1px;
                            bottom: -1px;
                            margin-right: -1px;
                            right: 0
                        }

                        .dxbl-tabs.dxbl-tabs-left > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active:not(.dxbl-disabled):not(:disabled):hover,
                        .dxbl-tabs.dxbl-tabs-left > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active:not(.dxbl-disabled):not(:disabled):hover {
                            border-right-color: var(--dxbl-tabs-bg)
                        }

            .dxbl-tabs.dxbl-tabs-left > .dxbl-tabs-tablist > .dxbl-btn {
                margin-right: calc(-1 * var(--dxbl-tabs-separator-border-width))
            }

    .dxbl-tabs > .dxbl-tabs-tablist:not([data-dx-focus-hidden]) .dxbl-tabs-item:focus-visible {
        outline: var(--dxbl-tabs-tab-focus-outline-width) solid var(--dxbl-tabs-tab-focus-outline-color);
        outline-offset: calc(var(--dxbl-tabs-tab-focus-outline-width) * -1)
    }

    .dxbl-tabs > .dxbl-tabs-tablist[data-dx-focus-hidden] .dxbl-tabs-item:focus-visible {
        -webkit-box-shadow: none;
        box-shadow: none;
        position: relative;
        outline: unset
    }

    .dxbl-tabs > .dxbl-tabs-tablist {
        color: var(--dxbl-tabs-color);
        font-family: var(--dxbl-tabs-font-family);
        font-size: var(--dxbl-tabs-font-size);
        line-height: var(--dxbl-tabs-line-height);
        background-color: var(--dxbl-tabs-bg);
        display: block;
        -webkit-box-align: center;
        -webkit-align-items: center;
        -ms-flex-align: center;
        align-items: center;
        padding: var(--dxbl-tabs-padding-top) var(--dxbl-tabs-padding-right) var(--dxbl-tabs-padding-bottom) var(--dxbl-tabs-padding-left)
    }

        .dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul {
            margin-bottom: 0
        }

        .dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul,
        .dxbl-tabs > .dxbl-tabs-tablist > ul {
            display: -webkit-box;
            display: -webkit-flex;
            display: -ms-flexbox;
            display: flex;
            padding-left: 0;
            white-space: nowrap;
            -webkit-flex-wrap: wrap;
            -ms-flex-wrap: wrap;
            flex-wrap: wrap
        }

            .dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li,
            .dxbl-tabs > .dxbl-tabs-tablist > ul > li {
                list-style: none
            }

                .dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item,
                .dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item {
                    z-index: 0;
                    color: inherit;
                    display: -webkit-box;
                    display: -webkit-flex;
                    display: -ms-flexbox;
                    display: flex
                }

                    .dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item.dxbl-dragging,
                    .dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item.dxbl-dragging {
                        background-color: var(--dxbl-tabs-tab-drag-bg-color);
                        -webkit-box-shadow: var(--dxbl-tabs-tab-drag-shadow);
                        box-shadow: var(--dxbl-tabs-tab-drag-shadow);
                        pointer-events: none;
                        z-index: 20000
                    }

                    .dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item > .dxbl-tabs-text-container,
                    .dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item > .dxbl-tabs-text-container {
                        -webkit-box-flex: 1;
                        -webkit-flex: 1 1 auto;
                        -ms-flex: 1 1 auto;
                        flex: 1 1 auto
                    }

                        .dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item > .dxbl-tabs-text-container > .dxbl-text,
                        .dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item > .dxbl-tabs-text-container > .dxbl-text {
                            --dxbl-text-padding-x: 0;
                            border-left-width: 0;
                            border-right-width: 0;
                            display: -webkit-box;
                            display: -webkit-flex;
                            display: -ms-flexbox;
                            display: flex
                        }

                            .dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item > .dxbl-tabs-text-container > .dxbl-text.dxbl-tabs-space-reservation,
                            .dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item > .dxbl-tabs-text-container > .dxbl-text.dxbl-tabs-space-reservation {
                                display: none
                            }

                            .dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item > .dxbl-tabs-text-container > .dxbl-text .dxbl-tabs-text-overflow,
                            .dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item > .dxbl-tabs-text-container > .dxbl-text .dxbl-tabs-text-overflow {
                                width: 100%;
                                white-space: nowrap;
                                overflow: hidden;
                                text-overflow: ellipsis
                            }

                    .dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item > .dxbl-tabs-close-button,
                    .dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item > .dxbl-tabs-close-button {
                        --dxbl-btn-bg: var(--dxbl-tabs-tab-btn-bg);
                        --dxbl-btn-box-shadow: inset 0 0 0 rgb(0 0 0 / 0%);
                        --dxbl-btn-padding-x: 0;
                        --dxbl-btn-padding-y: 0;
                        --dxbl-btn-border-radius: 50%;
                        --dxbl-btn-border-width: 0;
                        --dxbl-btn-hover-bg: var(--dxbl-tabs-close-btn-hover-bg);
                        --dxbl-btn-hover-image-color: var(--dxbl-tabs-close-btn-hover-color);
                        --dxbl-btn-image-color: var(--dxbl-tabs-close-btn-color);
                        --dxbl-image-width: 1rem;
                        --dxbl-image-height: 1rem;
                        width: var(--dxbl-tabs-close-btn-width);
                        height: var(--dxbl-tabs-close-btn-height);
                        margin-left: var(--dxbl-tabs-button-spacing);
                        -webkit-flex-shrink: 0;
                        -ms-flex-negative: 0;
                        flex-shrink: 0
                    }

                        .dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item > .dxbl-tabs-close-button:not(.dxbl-disabled):not(:disabled).dxbl-focused,
                        .dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item > .dxbl-tabs-close-button:not(.dxbl-disabled):not(:disabled):focus-visible,
                        .dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item > .dxbl-tabs-close-button:not(.dxbl-disabled):not(:disabled).dxbl-focused,
                        .dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item > .dxbl-tabs-close-button:not(.dxbl-disabled):not(:disabled):focus-visible {
                            -webkit-box-shadow: none !important;
                            box-shadow: none !important;
                            position: relative
                        }

                            .dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item > .dxbl-tabs-close-button:not(.dxbl-disabled):not(:disabled).dxbl-focused::after,
                            .dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item > .dxbl-tabs-close-button:not(.dxbl-disabled):not(:disabled):focus-visible::after,
                            .dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item > .dxbl-tabs-close-button:not(.dxbl-disabled):not(:disabled).dxbl-focused::after,
                            .dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item > .dxbl-tabs-close-button:not(.dxbl-disabled):not(:disabled):focus-visible::after {
                                border-radius: var(--dxbl-btn-border-radius);
                                -webkit-box-shadow: 0 0 var(--dxbl-btn-focus-shadow-blur) var(--dxbl-btn-focus-shadow-spread) var(--dxbl-btn-focus-shadow-color);
                                box-shadow: 0 0 var(--dxbl-btn-focus-shadow-blur) var(--dxbl-btn-focus-shadow-spread) var(--dxbl-btn-focus-shadow-color);
                                content: "";
                                position: absolute;
                                left: calc(var(--dxbl-btn-focus-shadow-spread) + var(--dxbl-btn-focus-shadow-blur) + 0px);
                                top: calc(var(--dxbl-btn-focus-shadow-spread) + var(--dxbl-btn-focus-shadow-blur) + 0px);
                                right: calc(var(--dxbl-btn-focus-shadow-spread) + var(--dxbl-btn-focus-shadow-blur) + 0px);
                                bottom: calc(var(--dxbl-btn-focus-shadow-spread) + var(--dxbl-btn-focus-shadow-blur) + 0px);
                                z-index: 1;
                                pointer-events: none
                            }

.dxbl-dragging.dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item > .dxbl-tabs-close-button,
.dxbl-dragging.dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item > .dxbl-tabs-close-button {
    --dxbl-btn-hover-bg: var(--dxbl-btn-bg);
    --dxbl-btn-hover-image-color: var(--dxbl-btn-image-color)
}

.dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-active):not(.dxbl-disabled):not(:disabled),
.dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-active):not(.dxbl-disabled):not(:disabled) {
    cursor: pointer
}

.dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-disabled):not(:disabled).dxbl-active,
.dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-disabled):not(:disabled).dxbl-active {
    cursor: default
}

.dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl),
.dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl) {
    border-style: var(--dxbl-tabs-tab-border-style);
    border-color: transparent;
    border-width: var(--dxbl-tabs-tab-border-width);
    display: -webkit-box;
    display: -webkit-flex;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-align: center;
    -webkit-align-items: center;
    -ms-flex-align: center;
    align-items: center;
    padding: var(--dxbl-tabs-tab-padding-y) var(--dxbl-tabs-tab-padding-x);
    text-decoration: none;
    max-width: var(--dxbl-tabs-tab-width)
}

    .dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):has(.dxbl-tabs-close-button),
    .dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):has(.dxbl-tabs-close-button) {
        padding-right: var(--dxbl-tabs-padding-right-close-btn)
    }

:not(.dxbl-dragging).dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-active):not(.dxbl-disabled):not(:disabled):not(.dxbl-disabled):not(:disabled):hover,
:not(.dxbl-dragging).dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-active):not(.dxbl-disabled):not(:disabled):not(.dxbl-disabled):not(:disabled):hover {
    border-color: var(--dxbl-tabs-tab-border-color);
    position: relative;
    background-color: var(--dxbl-tabs-tab-hover-bg,unset);
    color: var(--dxbl-tabs-tab-hover-color)
}

@media (-webkit-min-device-pixel-ratio:1.25) and (-webkit-max-device-pixel-ratio:1.4895833333333333),(-o-min-device-pixel-ratio:5/4) and (-o-max-device-pixel-ratio:143/96),(min-resolution:120dpi) and (max-resolution:143dpi) {
    :not(.dxbl-dragging).dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-active):not(.dxbl-disabled):not(:disabled):not(.dxbl-disabled):not(:disabled):hover,
    :not(.dxbl-dragging).dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-active):not(.dxbl-disabled):not(:disabled):not(.dxbl-disabled):not(:disabled):hover {
        --dxbl-border-width-with-scaling: calc(var(--dxbl-tabs-tab-border-width, 0px) / 1.25)
    }
}

@media (-webkit-min-device-pixel-ratio:1.5) and (-webkit-max-device-pixel-ratio:1.7395833333333333),(-o-min-device-pixel-ratio:3/2) and (-o-max-device-pixel-ratio:167/96),(min-resolution:144dpi) and (max-resolution:167dpi) {
    :not(.dxbl-dragging).dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-active):not(.dxbl-disabled):not(:disabled):not(.dxbl-disabled):not(:disabled):hover,
    :not(.dxbl-dragging).dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-active):not(.dxbl-disabled):not(:disabled):not(.dxbl-disabled):not(:disabled):hover {
        --dxbl-border-width-with-scaling: calc(var(--dxbl-tabs-tab-border-width, 0px) / 1.5)
    }
}

@media (-webkit-min-device-pixel-ratio:1.75),(-o-min-device-pixel-ratio:7/4),(min-resolution:168dpi) {
    :not(.dxbl-dragging).dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-active):not(.dxbl-disabled):not(:disabled):not(.dxbl-disabled):not(:disabled):hover,
    :not(.dxbl-dragging).dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-active):not(.dxbl-disabled):not(:disabled):not(.dxbl-disabled):not(:disabled):hover {
        --dxbl-border-width-with-scaling: calc(var(--dxbl-tabs-tab-border-width, 0px) / 1.75)
    }
}

:not(.dxbl-dragging).dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-active):not(.dxbl-disabled):not(:disabled):not(.dxbl-disabled):not(:disabled):hover::before,
:not(.dxbl-dragging).dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-active):not(.dxbl-disabled):not(:disabled):not(.dxbl-disabled):not(:disabled):hover::before {
    content: "";
    position: absolute;
    z-index: -1;
    left: calc(0px - var(--dxbl-border-width-with-scaling,var(--dxbl-tabs-tab-border-width,0px)));
    top: calc(0px - var(--dxbl-border-width-with-scaling,var(--dxbl-tabs-tab-border-width,0px)));
    right: calc(0px - var(--dxbl-border-width-with-scaling,var(--dxbl-tabs-tab-border-width,0px)));
    bottom: calc(0px - var(--dxbl-border-width-with-scaling,var(--dxbl-tabs-tab-border-width,0px)));
    border-radius: inherit;
    background-color: #000;
    opacity: 5%
}

.dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active,
.dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active {
    color: var(--dxbl-tabs-tab-selected-color);
    background-color: var(--dxbl-tabs-tab-selected-bg);
    border-color: var(--dxbl-tabs-tab-border-color) var(--dxbl-tabs-tab-border-color) var(--dxbl-tabs-tab-border-color) var(--dxbl-tabs-tab-border-color);
    position: relative
}

    .dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active::after,
    .dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active::after {
        content: "";
        position: absolute;
        z-index: 0;
        /* Le trait de l'onglet SÉLECTIONNÉ — même rôle que
           --dxbl-tabs-tab-selected-color, donc même couleur. */
        background-color: var(--kd-accent)
    }

    .dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active:not(.dxbl-disabled):not(:disabled):hover,
    .dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active:not(.dxbl-disabled):not(:disabled):hover {
        border-color: var(--dxbl-tabs-tab-border-color) var(--dxbl-tabs-tab-border-color) var(--dxbl-tabs-tab-border-color) var(--dxbl-tabs-tab-border-color)
    }

    .dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active.dxbl-dragging,
    .dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl):not(.dxbl-disabled):not(:disabled).dxbl-active.dxbl-dragging {
        background-color: var(--dxbl-tabs-tab-drag-bg-color)
    }

.dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl) > .dxbl-image,
.dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl) > .dxbl-image {
    --dxbl-image-margin-y: 0.4375rem;
    display: inline-block;
    -webkit-align-self: center;
    -ms-flex-item-align: center;
    align-self: center
}

    .dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl) > .dxbl-image:not(:last-child),
    .dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl) > .dxbl-image:not(:last-child) {
        margin-right: var(--dxbl-tabs-image-spacing)
    }

.dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item > svg.dxbl-image,
.dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item > svg.dxbl-image {
    color: var(--dxbl-unset,var(--dxbl-image-color,currentcolor));
    opacity: var(--dxbl-unset,var(--dxbl-image-color,70%))
}

.dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-disabled):not(:disabled).dxbl-active > svg.dxbl-image,
.dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-disabled):not(:disabled).dxbl-active > svg.dxbl-image {
    color: var(--dxbl-unset,var(--dxbl-image-color,currentcolor));
    opacity: var(--dxbl-unset,var(--dxbl-image-color,70%))
}

.dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-disabled):not(:disabled).dxbl-active:not(.dxbl-disabled):not(:disabled):hover > svg.dxbl-image,
.dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-disabled):not(:disabled).dxbl-active:not(.dxbl-disabled):not(:disabled):hover > svg.dxbl-image {
    color: var(--dxbl-unset,var(--dxbl-image-color,currentcolor));
    opacity: var(--dxbl-unset,var(--dxbl-image-color,70%))
}

.dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-disabled):not(:disabled):hover > svg.dxbl-image,
.dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-disabled):not(:disabled):hover > svg.dxbl-image {
    color: var(--dxbl-unset,var(--dxbl-image-color,currentcolor));
    opacity: var(--dxbl-unset,var(--dxbl-image-color,70%))
}

.dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item.dxbl-disabled > .dxbl-image,
.dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:disabled > .dxbl-image,
.dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item.dxbl-disabled > .dxbl-image,
.dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:disabled > .dxbl-image {
    color: var(--dxbl-unset,var(--dxbl-image-disabled-color,currentcolor));
    opacity: var(--dxbl-unset,var(--dxbl-image-disabled-color,40%))
}

.dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item > :not(.dxbl-image),
.dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item > :not(.dxbl-image) {
    opacity: 1
}

.dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-disabled):not(:disabled).dxbl-active > :not(.dxbl-image),
.dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-disabled):not(:disabled).dxbl-active > :not(.dxbl-image) {
    opacity: 1
}

.dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item.dxbl-disabled > :not(.dxbl-image),
.dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:disabled > :not(.dxbl-image),
.dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item.dxbl-disabled > :not(.dxbl-image),
.dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:disabled > :not(.dxbl-image) {
    opacity: 60%
}

.dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item.dxbl-disabled,
.dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:disabled,
.dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item.dxbl-disabled,
.dxbl-tabs > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:disabled {
    cursor: default
}

.dxbl-tabs > .dxbl-tabs-tablist > .dxbl-drop-target-indicator,
.dxbl-tabs > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > .dxbl-drop-target-indicator {
    -webkit-box-shadow: inset 2px 2px 0 var(--dxbl-tabs-tab-drop-indicator-color),inset -2px -2px 0 var(--dxbl-tabs-tab-drop-indicator-color);
    box-shadow: inset 2px 2px 0 var(--dxbl-tabs-tab-drop-indicator-color),inset -2px -2px 0 var(--dxbl-tabs-tab-drop-indicator-color);
    height: 2px;
    pointer-events: none;
    position: fixed;
    -webkit-transition: -webkit-box-shadow .25s;
    transition: -webkit-box-shadow .25s;
    -o-transition: box-shadow .25s;
    transition: box-shadow .25s;
    transition: box-shadow .25s,-webkit-box-shadow .25s;
    width: 2px;
    z-index: 3
}

.dxbl-tabs > .dxbl-tabs-tablist.dxbl-tabs-tablist-scrollable {
    display: -webkit-box;
    display: -webkit-flex;
    display: -ms-flexbox;
    display: flex
}

    .dxbl-tabs > .dxbl-tabs-tablist.dxbl-tabs-tablist-scrollable > .dxbl-btn {
        display: none
    }

    .dxbl-tabs > .dxbl-tabs-tablist.dxbl-tabs-tablist-scrollable.dxbl-tabs-tablist-has-overflow > .dxbl-btn {
        --dxbl-btn-color: var(--dxbl-tabs-btn-color);
        display: -webkit-inline-box;
        display: -webkit-inline-flex;
        display: -ms-inline-flexbox;
        display: inline-flex
    }

        .dxbl-tabs > .dxbl-tabs-tablist.dxbl-tabs-tablist-scrollable.dxbl-tabs-tablist-has-overflow > .dxbl-btn > svg.dxbl-image {
            color: var(--dxbl-tabs-scroll-btn-color,var(--dxbl-image-color,currentcolor));
            opacity: var(--dxbl-tabs-scroll-btn-color,var(--dxbl-image-color,70%))
        }

        .dxbl-tabs > .dxbl-tabs-tablist.dxbl-tabs-tablist-scrollable.dxbl-tabs-tablist-has-overflow > .dxbl-btn:not(.dxbl-disabled):not(:disabled).dxbl-active > svg.dxbl-image {
            color: var(--dxbl-unset,var(--dxbl-image-color,currentcolor));
            opacity: var(--dxbl-unset,var(--dxbl-image-color,70%))
        }

        .dxbl-tabs > .dxbl-tabs-tablist.dxbl-tabs-tablist-scrollable.dxbl-tabs-tablist-has-overflow > .dxbl-btn:not(.dxbl-disabled):not(:disabled).dxbl-active:not(.dxbl-disabled):not(:disabled):hover > svg.dxbl-image {
            color: var(--dxbl-unset,var(--dxbl-image-color,currentcolor));
            opacity: var(--dxbl-unset,var(--dxbl-image-color,70%))
        }

        .dxbl-tabs > .dxbl-tabs-tablist.dxbl-tabs-tablist-scrollable.dxbl-tabs-tablist-has-overflow > .dxbl-btn:not(.dxbl-disabled):not(:disabled):hover > svg.dxbl-image {
            color: var(--dxbl-tabs-scroll-btn-hover-color,var(--dxbl-image-hover-color,currentcolor));
            opacity: var(--dxbl-tabs-scroll-btn-hover-color,var(--dxbl-image-hover-color,95%))
        }

        .dxbl-tabs > .dxbl-tabs-tablist.dxbl-tabs-tablist-scrollable.dxbl-tabs-tablist-has-overflow > .dxbl-btn.dxbl-disabled > .dxbl-image,
        .dxbl-tabs > .dxbl-tabs-tablist.dxbl-tabs-tablist-scrollable.dxbl-tabs-tablist-has-overflow > .dxbl-btn:disabled > .dxbl-image {
            color: var(--dxbl-tabs-scroll-btn-disabled-color,var(--dxbl-image-disabled-color,currentcolor));
            opacity: var(--dxbl-tabs-scroll-btn-disabled-color,var(--dxbl-image-disabled-color,40%))
        }

    .dxbl-tabs > .dxbl-tabs-tablist.dxbl-tabs-tablist-scrollable > .dxbl-scroll-viewer > .dxbl-scroll-viewer-hor-scroll-bar:not(.dxbl-disabled):not(:disabled).dxbl-active,
    .dxbl-tabs > .dxbl-tabs-tablist.dxbl-tabs-tablist-scrollable > .dxbl-scroll-viewer > .dxbl-scroll-viewer-vert-scroll-bar:not(.dxbl-disabled):not(:disabled).dxbl-active {
        display: none
    }

    .dxbl-tabs > .dxbl-tabs-tablist.dxbl-tabs-tablist-scrollable > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul {
        -webkit-flex-wrap: nowrap;
        -ms-flex-wrap: nowrap;
        flex-wrap: nowrap
    }

.dxbl-tabs > .dxbl-tabs-tablist > .dxbl-btn {
    --dxbl-btn-border-width: 0
}

    .dxbl-tabs > .dxbl-tabs-tablist > .dxbl-btn:not(.dxbl-disabled):not(:disabled):hover {
        --dxbl-btn-hover-color: var(--dxbl-btn-color);
        --dxbl-btn-hover-bg: transparent;
        --dxbl-btn-hover-background: none
    }

    .dxbl-tabs > .dxbl-tabs-tablist > .dxbl-btn:not(.dxbl-disabled):not(:disabled).dxbl-focused,
    .dxbl-tabs > .dxbl-tabs-tablist > .dxbl-btn:not(.dxbl-disabled):not(:disabled):focus-visible {
        -webkit-box-shadow: none !important;
        box-shadow: none !important;
        position: relative
    }

        .dxbl-tabs > .dxbl-tabs-tablist > .dxbl-btn:not(.dxbl-disabled):not(:disabled).dxbl-focused::after,
        .dxbl-tabs > .dxbl-tabs-tablist > .dxbl-btn:not(.dxbl-disabled):not(:disabled):focus-visible::after {
            border-radius: var(--dxbl-btn-border-radius);
            -webkit-box-shadow: 0 0 var(--dxbl-btn-focus-shadow-blur) var(--dxbl-btn-focus-shadow-spread) var(--dxbl-btn-focus-shadow-color);
            box-shadow: 0 0 var(--dxbl-btn-focus-shadow-blur) var(--dxbl-btn-focus-shadow-spread) var(--dxbl-btn-focus-shadow-color);
            content: "";
            position: absolute;
            left: calc(var(--dxbl-btn-focus-shadow-spread) + var(--dxbl-btn-focus-shadow-blur) + 0px);
            top: calc(var(--dxbl-btn-focus-shadow-spread) + var(--dxbl-btn-focus-shadow-blur) + 0px);
            right: calc(var(--dxbl-btn-focus-shadow-spread) + var(--dxbl-btn-focus-shadow-blur) + 0px);
            bottom: calc(var(--dxbl-btn-focus-shadow-spread) + var(--dxbl-btn-focus-shadow-blur) + 0px);
            z-index: 1;
            pointer-events: none
        }

.dxbl-tabs .dxbl-tabs-content-panel {
    color: var(--dxbl-tabs-content-color);
    font-size: var(--dxbl-tabs-content-font-size);
    line-height: var(--dxbl-tabs-content-line-height);
    display: -webkit-box;
    display: -webkit-flex;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-flex: 1;
    -webkit-flex-grow: 1;
    -ms-flex-positive: 1;
    flex-grow: 1
}

    .dxbl-tabs .dxbl-tabs-content-panel.dxbl-loaded:not(.dxbl-tabs-render-default) > .dxbl-tabs-content:not([data-dx-tab-loaded]),
    .dxbl-tabs .dxbl-tabs-content-panel:not(.dxbl-tabs-render-default) > .dxbl-tabs-content:not([data-dx-tab-loaded]):not(.dxbl-active) {
        position: absolute;
        visibility: hidden;
        left: -10000px
    }

    .dxbl-tabs .dxbl-tabs-content-panel .dxbl-tabs-content {
        -webkit-box-flex: 1;
        -webkit-flex: 1 0 100%;
        -ms-flex: 1 0 100%;
        flex: 1 0 100%;
        width: 100%;
        padding-top:10px;
    }

.dxbl-tabs.dxbl-sm {
    --dxbl-tabs-image-spacing: 0.25rem;
    --dxbl-tabs-tab-padding-x: 1rem;
    --dxbl-tabs-tab-padding-y: 0.25rem;
    --dxbl-tabs-font-size: 0.75rem;
    --dxbl-tabs-line-height: 1.5;
    --dxbl-tabs-button-spacing: 0.25rem;
    --dxbl-tabs-close-btn-width: 1.25rem;
    --dxbl-tabs-close-btn-height: 1.25rem;
    --dxbl-tabs-content-font-size: 0.75rem;
    --dxbl-tabs-content-line-height: 1.5
}

    .dxbl-tabs.dxbl-sm > .dxbl-tabs-tablist > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl) > .dxbl-image,
    .dxbl-tabs.dxbl-sm > .dxbl-tabs-tablist > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl) > .dxbl-image {
        --dxbl-image-margin-y: 0.3125rem
    }

.dxbl-tabs.dxbl-lg {
    --dxbl-tabs-image-spacing: 0.5rem;
    --dxbl-tabs-tab-padding-x: 1rem;
    --dxbl-tabs-tab-padding-y: 0.25rem;
    --dxbl-tabs-font-size: 1rem;
    --dxbl-tabs-line-height: 1.375;
    --dxbl-tabs-button-spacing: 0.5rem;
    --dxbl-tabs-close-btn-width: 1.75rem;
    --dxbl-tabs-close-btn-height: 1.75rem;
    --dxbl-tabs-content-font-size: 1rem;
    --dxbl-tabs-content-line-height: 1.375
}

    .dxbl-tabs.dxbl-lg > .dxbl-scroll-viewer > .dxbl-scroll-viewer-content > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl) > .dxbl-image,
    .dxbl-tabs.dxbl-lg > ul > li > .dxbl-tabs-item:not(.dxbl-tabs-tmpl) > .dxbl-image {
        --dxbl-image-margin-y: 0.5625rem
    }

.assets_explorer--tabs .dxbl-tabs-content-panel {
    overflow-y: auto;
}



/*------------------------------------------------------------------
[16. DevExpress Modal]
*/

.dxbl-modal > .dxbl-modal-root > .dxbl-popup {
    /*    outline: 0;
    width: var(--dxbl-popup-width);
    margin: var(--dxbl-popup-margin);*/
    border: unset;
    /*    border-radius: var(--dxbl-popup-border-radius);
    background-color: var(--dxbl-popup-bg);
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
    opacity: 1;
    position: relative;
    left: auto;
    top: auto;
    padding: 0;
    -webkit-align-self: center;
    -ms-flex-item-align: center;
    align-self: center;
    justify-self: center;*/
}



/*------------------------------------------------------------------
[17. DevExpress Window]
*/

.dxbl-window .dxbl-window-header {
    background-color: var(--kd-nav-bg) !important;
    color: var(--kd-nav-fg-active) !important;
}

.dxbl-window-title {
    font-size: 13px !important;
}


.dxbl-window-body .dxbl-chart-root .dxbl-widget-container {
    padding-left: 0px;
    padding-right: 0px;
}

/*------------------------------------------------------------------
[18. DevExpress drawer]
*/
/*JT 28/11 ca d�conne*/
/*.dxbl-drawer-content {
    overflow: auto !important;
}*/


/* Le viewer BIM "chaud" (.forge-host) monte en z-index 700, et quand il est ouvert le header d'app est remonte a
   800 (sinon ses dropdowns sont pieges derriere l'overlay - cf. body:has(.forge-host.open) .header). Le DxDrawer
   d'app (LayoutMain, mode overlap) doit passer AU-DESSUS des DEUX quand on ouvre le detail d'un objet clique dans
   la maquette : au-dessus de 700 (sinon "le drawer ne s'ouvre pas") ET au-dessus de 800 (sinon son propre entete
   passe SOUS le header d'app, comme en 2D ou le drawer recouvre le header). On le met a 850 (sous les
   toasts/menus/modales >= 900). Sans impact hors BIM : ailleurs le header est en z-index:1, le drawer le couvre deja. */
.dxbl-drawer.layout-drawer .dxbl-drawer-panel {
    z-index: 850;
}
/* DevExpress ancre le panneau au bas de son conteneur (absolute ; top: 0 ; bottom: 0), lequel occupe toute
   la fenêtre : sans ce raccourcissement, la dernière bande du corps défilant reste inatteignable, le
   défilement s'arrêtant avant. Cf. catégorie 47. Combinateur ENFANT, comme DevExpress lui-même
   (.dxbl-drawer > .dxbl-drawer-panel) : un DxDrawer imbriqué dans le contenu de page est aussi un
   descendant de .layout-drawer, or son panneau n'est pas ancré au viewport et n'a rien à réserver. */
.dxbl-drawer.layout-drawer > .dxbl-drawer-panel {
    bottom: var(--app-dock-bar-height);
}
/* Ombrage de fond (ApplyBackgroundShading, drawer BIM) : sans z-index il reste SOUS le .forge-host (700), qui
   perce alors la bande non couverte par le panneau. On le remonte juste sous le panneau (849) pour qu'il masque
   la maquette 3D derriere le drawer. N'existe que quand l'ombrage est actif (drawer ouvert depuis explorer-bim). */
.dxbl-drawer.layout-drawer .dxbl-drawer-shade {
    z-index: 849;
}

/* ── Drawer de SAISIE (DrawerState.IsForm) ────────────────────────────────────────────────
   Un drawer de lecture laisse la page cliquable : on enchaine les objets, c'est voulu. Un drawer de
   saisie ne le peut pas — un clic derriere recharge le panneau et emporte la saisie sans un mot.
   On neutralise donc NOS conteneurs (.page-container, .page-sidebar) plutot que de parier sur le
   comportement de l'ombrage DevExpress, dont la feuille de styles n'est pas dans le depot : le
   blocage ne depend ainsi d'aucun detail du fournisseur, et survivra au passage a l'offcanvas.
   ⚠︝ pointer-events n'arrete pas le clavier : on peut encore atteindre la page derriere a la
   tabulation. Le remplacement par l'attribut inert est a faire quand le drawer sera reecrit. */
body:has(.dxbl-drawer.layout-drawer-modal) .page-container,
body:has(.dxbl-drawer.layout-drawer-modal) .page-sidebar {
    pointer-events: none;
}

/* Pied collant du panneau (FooterContent) : sans lui, « Enregistrer » disparait des qu'on fait
   defiler un formulaire un peu long — et ce qui disparait ne se clique pas. */
.layout-drawer-withfoot {
    display: flex;
    flex-direction: column;
    height: 100%;
    min-height: 0;
}

.layout-drawer-scroll {
    flex: 1;
    min-height: 0;
    overflow: auto;
}

.layout-drawer-foot {
    flex: none;
    display: flex;
    gap: 8px;
    justify-content: flex-end;
    align-items: center;
    padding: 12px 0 0;
    border-top: 1px solid var(--bs-border-color, #e2e7ee);
}


/*------------------------------------------------------------------
[19. DevExpress graph]
*/

.no-axis-border .dxc-axes-group {
    display: none;
}

.flex-row {
    display: flex;
    flex-direction: row;
}

.flex-column {
    display: flex;
    flex-direction: column;
    width: 100%;
}

.flex-center {
    align-items: center;
}

/*------------------------------------------------------------------
[20. Web Reports]
*/


.page-wrapper-space {
    padding-top: 15px;
    padding-left: 20px;
    padding-right: 20px;
    padding: 16px;
    display: flex;
    flex-direction: column;
}
/* ── OccupancyFilterHeader ───────────────────── */
.occ-subheader {
    position: sticky;
    top: 42px;
    z-index: 100;
    background: var(--bs-gray-100);
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 5px 16px;
    min-height: 40px;
    gap: 8px;
}

.occ-chips-left {
    display: flex;
    gap: 6px;
    align-items: center;
    flex: 1;
    overflow-x: auto;
    flex-wrap: nowrap;
    scrollbar-width: none;
}

.occ-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    background: var(--bs-white);
    border: 1px solid var(--bs-gray-300);
    border-radius: 20px;
    padding: 2px 8px 2px 10px;
    font-size: 12px;
    color: var(--bs-gray-900);
    white-space: nowrap;
    flex-shrink: 0;
}

.occ-chip-x {
    background: none;
    border: none;
    padding: 0 2px;
    cursor: pointer;
    color: var(--bs-secondary);
    font-size: 11px;
    line-height: 1;
}

    .occ-chip-x:hover {
        color: var(--bs-danger);
    }

.occ-clear-btn {
    font-size: 12px;
    color: var(--bs-primary);
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
    white-space: nowrap;
    flex-shrink: 0;
}

.occ-toggle-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 12px;
    border: 1px solid var(--bs-gray-800);
    border-radius: 6px;
    background: var(--bs-gray-800) !important;
    font-size: 13px;
    font-family: 'Inter', sans-serif;
    color: var(--bs-white) !important;
    cursor: pointer;
    transition: all .15s;
}

    .occ-toggle-btn:hover {
        background: var(--bs-gray-900) !important;
        border-color: var(--bs-gray-900) !important;
        color: var(--bs-white) !important;
    }

    .occ-toggle-btn.active {
        background: var(--bs-primary) !important;
        border-color: var(--bs-primary) !important;
        color: var(--bs-white) !important;
    }

.occ-filter-panel {
    position: sticky;
    top: 82px;
    z-index: 99;
    background: var(--bs-white);
    border: 1px solid var(--bs-gray-300);
    border-radius: 12px;
    margin: 8px 16px;
    padding: 10px 16px;
    display: flex;
    flex-direction: column;
    gap: 8px;
    align-items: flex-start;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}

.occ-filter-row {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
    width: 100%;
}

/* ── GenericFilter ─────────────────────────── */
.gf-trigger {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 5px 10px;
    border: 1px solid var(--bs-gray-400);
    border-radius: 6px;
    cursor: pointer;
    background: var(--bs-white);
    font-size: 13px;
    color: var(--bs-gray-900);
    user-select: none;
    white-space: nowrap;
    transition: all .15s;
}

    .gf-trigger.active {
        border-color: var(--bs-primary);
        background: var(--bs-primary-bg-subtle);
    }

    .gf-trigger.disabled {
        cursor: not-allowed;
        opacity: 0.5;
    }

.gf-trigger-label {
    font-weight: 500;
    color: var(--bs-gray-900);
}

    .gf-trigger-label.active {
        color: var(--bs-primary);
    }

.gf-trigger-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: var(--bs-primary);
    color: var(--bs-white);
    font-size: 9px;
    font-weight: 700;
    line-height: 1;
}

.gf-trigger-caret {
    font-size: 15px;
    color: var(--bs-secondary);
    transition: transform .15s;
}

    .gf-trigger-caret.open {
        transform: rotate(180deg);
    }

/* Dropdown */
.gf-dropdown {
    background: var(--bs-white);
    border-radius: 8px;
}

.gf-search-wrapper {
    padding: 8px 10px;
    border-bottom: 1px solid var(--bs-gray-200);
}

.gf-search-input {
    width: 100%;
    padding: 4px 8px;
    border: 1px solid var(--bs-gray-200);
    border-radius: 4px;
    font-size: 12px;
    outline: none;
    color: var(--bs-gray-900);
    background: var(--bs-gray-100);
    box-sizing: border-box;
}

.gf-no-options {
    padding: 10px 12px;
    font-size: 12px;
    color: var(--bs-gray-400);
    text-align: center;
}

.gf-select-all {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 12px;
    cursor: pointer;
    font-size: 12px;
    color: var(--bs-secondary);
    border-bottom: 1px solid var(--bs-gray-200);
}

    .gf-select-all span {
        font-weight: 500;
    }

.gf-checkbox {
    width: 13px;
    height: 13px;
    cursor: pointer;
    accent-color: var(--bs-primary);
}

.gf-items {
    max-height: 220px;
    overflow-y: auto;
}

.gf-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 12px;
    cursor: pointer;
    font-size: 12px;
    color: var(--bs-gray-900);
    transition: background .1s;
}

    .gf-item.selected {
        background: var(--bs-primary-bg-subtle);
        color: var(--bs-primary);
    }

.gf-item-label {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.gf-item-checkbox {
    width: 13px;
    height: 13px;
    cursor: pointer;
    accent-color: var(--bs-primary);
    flex-shrink: 0;
}

.gf-clear-wrapper {
    border-top: 1px solid var(--bs-gray-200);
    padding: 5px 12px;
    text-align: right;
}

.gf-clear-btn {
    font-size: 11px;
    color: var(--bs-secondary);
    background: none;
    border: none;
    cursor: pointer;
    text-decoration: underline;
}
/* ── KpiBar ─────────────────────────────────── */

.kpibar-chart.multiseries .apexcharts-legend-series[rel="1"] {
    display: inline-flex !important;
}

/* ── KpiRadialCard ───────────────────────────── */
.kpi-radial-card {
    border: 1px solid var(--bs-gray-300);
    border-radius: 8px;
    padding: 16px;
    background: var(--bs-white);
    height: 100%;
    box-sizing: border-box;
    opacity: 1;
    transition: opacity 0.2s, border-color 0.2s;
}

    .kpi-radial-card.filtered {
        /* La carte RETENUE par le filtre : un état choisi, pas une action. */
        border-color: var(--kd-accent);
    }

    .kpi-radial-card.dimmed {
        opacity: 0.4;
    }

.kpi-radial-inner {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    height: 100%;
    gap: 12px;
}

.kpi-radial-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 16px;
}

.kpi-radial-info {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
}

.kpi-radial-type {
    font-size: 12px;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--bs-gray-900);
}

.kpi-radial-spaces {
    font-size: 12px;
    color: var(--bs-gray-600);
}

.kpi-radial-hours {
    font-size: 13px;
    font-weight: 500;
    color: var(--bs-gray-800);
    margin-top: 2px;
}

.kpi-radial-gauge {
    width: 110px;
    height: 110px;
    flex-shrink: 0;
}

.kpi-radial-stats {
    display: flex;
    gap: 20px;
    padding-top: 4px;
}

.kpi-stat {
    display: flex;
    flex-direction: column;
    gap: 2px;
    align-items: center;
    flex: 1;
}

.kpi-stat-label {
    font-size: 9px;
    font-weight: 700;
    letter-spacing: .06em;
    color: var(--stat-color);
}

.kpi-stat-row {
    display: flex;
    align-items: center;
    gap: 4px;
}

.kpi-stat-icon {
    font-size: 16px;
    color: var(--stat-color);
}

.kpi-stat-value {
    font-size: 14px;
    font-weight: 700;
    color: var(--stat-color);
    flex: 1;
    text-align: center;
}

/* ── KpiLine ─────────────────────────────────── */
.kpiline-chart .apexcharts-legend-series[rel="1"] {
    display: none !important;
}

/* ── KpiProgressBar ──────────────────────────── */
.kpi-progress-card {
    font-family: 'Inter', sans-serif;
    background: var(--bs-white);
    border-radius: 8px;
    padding: 16px 18px;
    min-width: 260px;
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.kpi-progress-title {
    font-size: 15px;
    font-weight: 500;
    color: var(--bs-gray-900);
}

.kpi-progress-subtitle {
    font-size: 12px;
    color: var(--bs-gray-400);
    margin-top: 2px;
}

.kpi-progress-bar-wrapper {
    width: 100%;
    position: relative;
    padding-top: 24px;
}

.kpi-progress-bubble {
    position: absolute;
    top: 0;
    transform: translateX(-50%);
    background: var(--kpi-color);
    color: var(--bs-white);
    font-size: 11px;
    font-weight: 600;
    padding: 2px 7px;
    border-radius: 10px;
    white-space: nowrap;
    transition: left 0.3s ease;
}

.kpi-progress-track {
    width: 100%;
    height: 8px;
    background: color-mix(in srgb, var(--kpi-color) 20%, transparent);
    border-radius: 4px;
    overflow: hidden;
}

.kpi-progress-fill {
    height: 100%;
    background: var(--kpi-color);
    border-radius: 4px;
    transition: width 0.4s ease;
}

.kpi-progress-stats {
    display: flex;
    gap: 20px;
    padding-top: 4px;
    border-top: 1px solid var(--bs-primary-border-subtle);
    flex-wrap: wrap;
}

/* ── KpiRankCard ─────────────────────────────── */
.kpi-rank-card {
    flex: 1;
    min-width: 220px;
    height: 100%;
}

    .kpi-rank-card .card-title {
        width: 100%;
    }

    /* Top — flop */
    .kpi-rank-card.flop.rank-1 {
        border-color: #f5c2c7;
        background: #fdecea;
    }

    .kpi-rank-card.flop.rank-2 {
        border-color: #fad4d8;
        background: #fff0f1;
    }

    .kpi-rank-card.flop.rank-3 {
        border-color: #fde2e4;
        background: #fff5f6;
    }

    /* Top — top */
    .kpi-rank-card.top.rank-1 {
        border-color: #8fd9a8;
        background: #e6f7ed;
    }

    .kpi-rank-card.top.rank-2 {
        border-color: #b7efc5;
        background: #f0faf3;
    }

    .kpi-rank-card.top.rank-3 {
        border-color: #d6f5e3;
        background: #f7fdf9;
    }

/* Stats */
.kpi-rank-stats {
    display: flex;
    gap: 16px;
    justify-content: space-around;
    flex-wrap: wrap;
}

.kpi-rank-stat {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.kpi-rank-stat-header {
    display: flex;
    align-items: center;
    gap: 4px;
}

.kpi-rank-stat-icon {
    font-size: 14px;
    color: var(--stat-color);
}

.kpi-rank-stat-label {
    font-size: 9px;
    font-weight: 700;
    letter-spacing: .06em;
    color: var(--stat-color);
}

.kpi-rank-stat-value {
    font-size: 15px;
    font-weight: 700;
    color: var(--stat-color);
}

/* ── KpiTable ────────────────────────────────── */
.kpi-dxgrid .dxbl-grid-data-cell,
.kpi-dxgrid .dxbl-grid-header-cell {
    font-family: 'Inter', sans-serif !important;
    font-size: 0.85rem !important;
}

.kpi-dxgrid tr[data-visible-index] td {
    box-shadow: inset 0 -1px 0 var(--bs-border-color);
}

.kpi-table__export-btn button,
.kpi-table__export-btn-inner {
    font-size: 0.75rem;
    padding: 4px 10px;
    border: 1px solid var(--bs-border-color);
    border-radius: 4px;
    background: transparent;
    cursor: pointer;
    color: var(--bs-secondary-color);
}

/* ── TabOccupancyOverview ────────────────────── */
.tab-overview-root {
    padding: 20px;
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.tab-overview-title {
    font-family: 'Inter', sans-serif;
    font-size: 1.5rem;
    font-weight: 400;
}

.tab-overview-kpis {
    display: flex;
    gap: 16px;
    flex-wrap: wrap;
}

.tab-overview-section {
    font-family: 'Inter', sans-serif;
    background-color: var(--bs-white);
    border-radius: 8px;
    padding: 16px;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
}

.tab-overview-section-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 16px;
}

.tab-overview-section-title {
    font-weight: 500;
    color: var(--bs-gray-900);
    font-size: 15px;
}

.tab-overview-cards-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(max(220px, calc(25% - 12px)), 1fr));
    gap: 16px;
}

.tab-overview-timeline {
    border: 1px solid var(--bs-gray-300);
    border-radius: 8px;
    padding: 16px;
    background: var(--bs-white);
}

/* ── TabOccupancy* ───────────────────── */
.tab-occ-root {
    padding: 20px;
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.tab-occ-kpis {
    display: flex;
    gap: 16px;
    flex-wrap: wrap;
}

.tab-occ-bars {
    display: flex;
    gap: 16px;
    flex-wrap: wrap;
}

.tab-occ-table {
    display: flex;
    gap: 16px;
    flex-wrap: wrap;
}

.tab-occ-panel {
    border: 1px solid var(--bs-gray-300);
    border-radius: 8px;
    padding: 16px;
    background: var(--bs-white);
}

.tab-occ-panel-flex {
    flex: 1;
    min-width: 260px;
    border: 1px solid var(--bs-gray-300);
    border-radius: 8px;
    padding: 16px;
    background: var(--bs-white);
}

.tab-occ-rank-cards {
    display: flex;
    gap: 16px;
    flex-wrap: wrap;
}

.tab-occ-rank-chart {
    margin-top: 0;
}

/* ── Occupancy page ──────────────────────────── */
.content.full-height {
    overflow: visible !important;
}

/* ── Onglets : UNE seule famille ─────────────────────────────────────────────
   Il y en avait deux, et ce n'étaient pas deux composants : c'était le MÊME
   DxTabs avec deux classes. `px-0` laissait le rendu natif — soulignement à la
   couleur primaire — et `occ-tabs` le repeignait en pilule verte pleine. Deux
   grammaires pour la même chose, sur des écrans voisins.

   La règle retenue : l'ONGLET (soulignement) change de section d'écran ; la
   PILULE PLEINE est réservée aux filtres posés DANS le contenu — « Regrouper :
   Spatial / Temporel / Source » de la console d'alarmes en est le cas type. Le
   vert plein cesse ainsi de vouloir dire deux choses.

   Ne restent donc ici que les corrections de MISE EN PAGE dont DevExpress a
   besoin dans un conteneur à hauteur calculée. Les couleurs, elles, viennent du
   thème bootstrap-external.bs5, qui lit les mêmes --bs-* que tokens.css : un
   habillage client les suit sans rien redéclarer. */
.kd-tabs {
    overflow: visible !important;
    margin: 0 !important;
    padding: 0 !important;
}

    /* La barre d'onglets reste visible quand le contenu défile sous elle. */
    .kd-tabs .dxbl-tabs-tablist {
        position: sticky;
        top: 0;
        z-index: 101;
        background: var(--kd-surface);
        margin: 0 !important;
        padding: 0 4px;
    }

    .kd-tabs .dxbl-tabs-content-panel {
        overflow: visible !important;
    }

    .kd-tabs .dxbl-tabs-content {
        padding: 0 !important;
        border: none !important;
    }

/* ── Qualité d'air et confort intérieur ──────── */
.metric-stats-bar {
    display: flex;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 12px;
}

.metric-stat-card {
    flex: 1;
    min-width: 120px;
    margin-bottom: 0 !important;
}

    .metric-stat-card .card-body {
        padding: 10px 14px;
    }

    .metric-stat-card .card-header {
        padding: 8px 14px 4px;
        min-height: unset;
    }

    .metric-stat-card .metric-stat-value {
        font-size: 1.4rem;
        font-weight: 600;
        line-height: 1.2;
    }

        .metric-stat-card .metric-stat-value small {
            font-size: 0.75rem;
            font-weight: 400;
            opacity: 0.7;
        }

.heatmap-sentinel .apexcharts-legend-series:has([data\:default-text="___"]) {
    display: none !important;
}


/*------------------------------------------------------------------
[22. Infobulle de marqueur — carte partagée 2D/3D]
*/

/* Carte affichée au survol d'un marqueur, IDENTIQUE dans les deux visionneuses : la 2D en mode Pastille
   (Map2DInterop.js → _buildPopupHtml, dans la coquille de popup de geolysmap) et la 3D (forge-aps-viewer.js
   → _tipHtml, dans sa propre bulle à ergot .kdc-fmtip). Les deux construisent leur DOM en JS, hors scope
   CSS Blazor → styles globaux obligatoires, et définis ICI une seule fois : c'est ce fichier qui garantit
   que les deux écrans se ressemblent. Chaque visionneuse ne garde chez elle que sa COQUILLE (largeur,
   position, ombre, ergot). */
.kdc-mtip-card {
    color: #1f2937;
    font: 13px/1.3 system-ui, -apple-system, sans-serif;
}

.kdc-mtip-head {
    display: flex;
    align-items: center;
    gap: 7px;
    padding-bottom: 6px;
    margin-bottom: 6px;
    border-bottom: 1px solid rgba(0, 0, 0, .08);
}

.kdc-mtip-dot {
    flex: 0 0 auto;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    box-shadow: 0 0 0 1px rgba(0, 0, 0, .1) inset;
}

.kdc-mtip-name {
    font-weight: 700;
    overflow-wrap: anywhere;
}

.kdc-mtip-rows {
    display: flex;
    flex-direction: column;
    gap: 5px;
}

.kdc-mtip-row {
    display: flex;
    align-items: center;
    gap: 7px;
}

/* Largeur réservée même sans icône : les lignes d'une carte mixte (mesures + champs sans picto)
   restent alignées sur la même colonne de libellés. */
.kdc-mtip-ico {
    flex: 0 0 auto;
    width: 1.15em;
    font-size: 15px;
    text-align: center;
    color: #6b7280;
}

.kdc-mtip-lab {
    flex: 1 1 auto;
    color: #6b7280;
}

.kdc-mtip-val {
    font-weight: 700;
    white-space: nowrap;
}

.kdc-mtip-empty {
    color: #9ca3af;
    font-style: italic;
    font-size: 12px;
}

/* Coquille 2D : la boîte blanche, l'ombre et l'ergot viennent de la popup geolysmap — il ne reste
   que la largeur à contraindre. */
.map2d-popup {
    min-width: 160px;
    max-width: 260px;
}

/* L'infobulle passe AU-DESSUS de tout ce qui est posé sur la carte, en particulier des marqueurs de
   valeur permanents du [24] : elle est appelée par un geste délibéré (le survol) et répond à une question
   précise, elle ne peut pas être recouverte par de l'affichage de fond. geolysmap ne lui donne aucun
   z-index — elle ne devait sa position qu'à l'ordre du DOM, ce qui la faisait passer dessous dès qu'un
   overlay était (re)construit après elle. La valeur reste basse : au-dessus des calques de carte, sous
   les menus, modales et toasts de l'application. Cible la classe vendeur car c'est l'élément POSITIONNÉ ;
   .map2d-popup, à l'intérieur, ne pourrait pas soulever son conteneur. */
.mapboxgl-popup {
    z-index: 4;
}

/*------------------------------------------------------------------
[23. Explorers — shell + primitives partagées]
Couche mutualisée consommée par ExplorerRelations (et adoptable par ExplorerPlans/List/BIM).
Génériques : .explorer-* (shell page/viewport) + .exp-* (primitives de contenu des panneaux RailNav).
Comme app.css est global, ces classes atteignent le markup slotté dans RailNav sans ::deep.
*/

/* -- shell de page -- */
/* .explorer-scopebar a disparu : la portée (Campus / Bâtiment / Étage) est rendue dans les outils de la
   barre supérieure sur les quatre explorateurs. La colonne ne porte plus qu'un bandeau optionnel — celui du
   mode diagnostic, qui apporte son propre gabarit — au-dessus du chrome. */
.explorer-page { display: flex; flex-direction: column; height: 100%; width: 100%; }
.explorer-root { flex: 1 1 auto; min-height: 0; background: var(--kd-page-bg); overflow: hidden; }
.explorer-viewport { position: relative; min-width: 320px; height: 100%; display: flex; overflow: hidden; }
.explorer-viewport-overlay { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; width: 100%; color: #9499a1; font-size: 13px; }
    .explorer-viewport-overlay i { font-size: 34px; }
.kdc-graph-canvas { position: absolute; inset: 0; }

/* -- primitives de contenu des panneaux -- */
.exp-tree { padding: 2px 0; user-select: none; }
.exp-row { display: flex; align-items: center; gap: 6px; height: 28px; padding: 0 8px; border-radius: 7px; cursor: pointer; white-space: nowrap; }
    .exp-row:hover { background: rgba(0, 0, 0, .05); }
.exp-row--selected { background: var(--bs-primary-bg-subtle, #e7f1ff); }
    .exp-row--selected .exp-row-label { color: var(--bs-primary, #1178d8); font-weight: 600; }
.exp-caret { width: 16px; flex: 0 0 16px; display: grid; place-items: center; color: #9aa4b2; font-size: 11px; }
.exp-caret--leaf { visibility: hidden; }
.exp-row-ic { width: 18px; flex: 0 0 18px; display: grid; place-items: center; color: #6b7684; }
.exp-row-label { overflow: hidden; text-overflow: ellipsis; }
    .exp-row-label small { margin-left: 6px; font-size: 10.5px; }
.exp-row-meta { margin-left: auto; color: #9aa4b2; font-size: 11px; padding-left: 8px; }
.exp-badge-alarm { margin-left: auto; background: #e5484d; color: #fff; font-size: 10px; font-weight: 700; min-width: 16px; height: 16px; padding: 0 4px; border-radius: 8px; display: inline-grid; place-items: center; }

.exp-empty, .exp-hint { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 26px 14px; color: #9499a1; font-size: 12.5px; text-align: center; }
    .exp-empty i { font-size: 26px; }
.exp-hint { padding: 14px; }
.exp-section { padding: 12px 12px 4px; }
.exp-header { font-size: 11px; letter-spacing: .05em; text-transform: uppercase; color: #6b7684; margin: 0 2px 8px; }

.exp-search { display: flex; align-items: center; gap: 7px; height: 34px; margin: 8px; padding: 0 10px; border-radius: 8px; background: var(--bs-body-bg, #fff); border: 1px solid var(--bs-border-color, #dee2e6); }
    .exp-search i { color: #9aa4b2; }
    .exp-search input { border: 0; background: none; outline: none; width: 100%; font-size: 13px; color: inherit; }

.exp-chip { display: inline-flex; align-items: center; gap: 7px; height: 28px; padding: 0 11px; margin: 0 6px 6px 0; border-radius: 15px; border: 1px solid var(--bs-border-color, #dee2e6); background: var(--bs-body-bg, #fff); color: #6b7684; font-weight: 550; cursor: pointer; }
    .exp-chip .exp-chip-sw { width: 11px; height: 11px; border-radius: 50%; }
    .exp-chip.is-on { color: inherit; border-color: #c4ccd8; }
    .exp-chip.is-off { opacity: .5; }
    .exp-chip.is-disabled { opacity: .4; cursor: not-allowed; font-style: italic; }
.exp-toggle { display: flex; align-items: center; gap: 8px; height: 30px; color: #6b7684; cursor: pointer; }

.exp-detail { padding: 4px 2px; }
.exp-actions { display: grid; gap: 7px; margin-bottom: 8px; }
.exp-act-btn { display: flex; align-items: center; gap: 9px; padding: 9px 11px; border-radius: 9px; border: 1px solid var(--bs-border-color, #dee2e6); background: var(--bs-body-bg, #fff); color: inherit; font-weight: 550; text-align: left; width: 100%; cursor: pointer; }
    .exp-act-btn:hover { background: rgba(0, 0, 0, .04); }
    .exp-act-btn i { color: #6b7684; }
/*------------------------------------------------------------------
[24. Marqueurs de valeur — overlay permanent sur la carte]
*/

/* Marqueurs PERMANENTS de l'onglet Capacités : une carte par capteur, une ligne par mesure affichée
   (KdcValueOverlay, valueoverlay.js). À distinguer de la carte de SURVOL du [22] : celle-ci répond à
   « qu'est-ce que cet objet » (nom, type, toutes les mesures), celle-là à « que vaut la mesure ici » —
   sans identité, volontairement (spec §4.3). Styles GLOBAUX : le DOM est construit en JS, hors scope
   CSS Blazor. Définis ici et non côté Map2D pour servir aussi le portage 3D à venir. */
/* PAS de z-index : l'overlay est inséré tôt dans le conteneur de carte, donc l'ordre du DOM le place
   déjà au-dessus du canvas et EN DESSOUS de tout ce qui est ajouté après — contrôles, et surtout les
   popups de survol. Lui en donner un le faisait passer devant l'infobulle, qui n'en a pas
   (`.mapboxgl-popup` est simplement `position: absolute`) : une valeur explicite bat toujours un `auto`,
   quel que soit l'ordre. La popup, elle, en reçoit un plus haut au [22] pour survivre au cas où l'overlay
   serait reconstruit pendant qu'elle est ouverte (rafraîchissement temps réel sous le curseur). */
.kdc-vov {
    position: absolute;
    inset: 0;
    overflow: hidden;
    pointer-events: none;   /* purement informatif : les clics vont à la carte, pas aux marqueurs */
}

/* Chaque marqueur est positionné par `transform` seul — c'est ce qui permet de suivre un déplacement
   image par image sans déclencher de recalcul de mise en page. */
.kdc-vov-m {
    position: absolute;
    top: 0;
    left: 0;
    will-change: transform;
}

/* Étiquette flottante, reliée par une pointe à la PASTILLE GL posée au même endroit — c'est elle le point
   ancré, et elle porte en plus le survol et le clic. L'overlay n'ajoute que la valeur : quand l'étiquette
   s'efface (dézoom ou switch), la pastille reste, ce qui EST l'état « valeurs masquées » de RG-07. */
/* ⚠️ Chaque pixel de cette étiquette se paie en étiquettes MASQUÉES. Elles sont permanentes et nombreuses
   (100 à 174 pour une seule capacité sur un étage dense), le décluttering en cache d'autant plus qu'elles
   sont grandes : la surface décide de la densité lisible, bien plus que le zoom. D'où un gabarit délibérément
   serré — 10 px et des marges minces — là où l'infobulle de survol du [22], qui s'affiche SEULE, peut rester
   confortable. Ne pas « harmoniser » les deux : elles n'ont pas la même contrainte. */
.kdc-vov-card {
    position: absolute;
    /* Position ET pointe posées par l'ANCRE choisie au décluttering (cf. .kdc-vov-card--* ci-dessous et
       KdcValueOverlay._declutter) : la carte n'a pas de place fixe, elle se range là où il reste de la place. */
    padding: 3px 6px;
    border-radius: 6px;
    background: rgba(255, 255, 255, .97);
    box-shadow: 0 1px 5px rgba(26, 26, 26, .2);
    white-space: nowrap;
    font-size: 10px;
    line-height: 1.4;
    color: #1a1a1a;
    font-variant-numeric: tabular-nums;
}

/* Ancres : la carte se pose sur un côté de la pastille (origine du marqueur .kdc-vov-m). Orthogonales =
   écart 16 px + pointe de rappel ; diagonales = collée au coin (8 px), sans pointe. Chaque règle DOIT rester
   le miroir de KdcValueOverlay._anchorBox (même géométrie), sinon la boîte testée pour les collisions n'est
   pas celle qu'on dessine. */
.kdc-vov-card--top          { bottom: 16px; left: 50%; transform: translateX(-50%); }
.kdc-vov-card--bottom       { top: 16px;    left: 50%; transform: translateX(-50%); }
.kdc-vov-card--right        { left: 16px;   top: 50%;  transform: translateY(-50%); }
.kdc-vov-card--left         { right: 16px;  top: 50%;  transform: translateY(-50%); }
.kdc-vov-card--top-right    { bottom: 8px;  left: 8px; }
.kdc-vov-card--top-left     { bottom: 8px;  right: 8px; }
.kdc-vov-card--bottom-right { top: 8px;     left: 8px; }
.kdc-vov-card--bottom-left  { top: 8px;     right: 8px; }

/* Pointes (ancres orthogonales seulement) : petit triangle sur le bord face à la pastille, teinté du fond
   de la carte. Convention triangle CSS : le côté COLORÉ est opposé à la direction de la pointe. */
.kdc-vov-card--top::after,
.kdc-vov-card--bottom::after,
.kdc-vov-card--right::after,
.kdc-vov-card--left::after {
    content: '';
    position: absolute;
    border: 3px solid transparent;
}
.kdc-vov-card--top::after    { top: 100%;    left: 50%; margin-left: -3px; border-top-color: rgba(255, 255, 255, .97); }
.kdc-vov-card--bottom::after { bottom: 100%; left: 50%; margin-left: -3px; border-bottom-color: rgba(255, 255, 255, .97); }
.kdc-vov-card--right::after  { right: 100%;  top: 50%;  margin-top: -3px;  border-right-color: rgba(255, 255, 255, .97); }
.kdc-vov-card--left::after   { left: 100%;   top: 50%;  margin-top: -3px;  border-left-color: rgba(255, 255, 255, .97); }

/* Nom de l'objet en tête de carte (switch « Afficher le nom de l'objet »). Séparé des mesures par un
   filet, et un cran plus foncé : c'est l'identité, pas une valeur. `max-width` + ellipsis car un nom est
   libre (« Sanitaire collaborateur F cabine ») là où une mesure tient en quelques caractères — sans borne,
   la carte s'étirerait et le décluttering en masquerait d'autant plus (chaque pixel se paie, cf. .kdc-vov-card). */
.kdc-vov-name {
    max-width: 150px;
    overflow: hidden;
    text-overflow: ellipsis;
    font-weight: 700;
    color: #333;
    padding-bottom: 2px;
    margin-bottom: 2px;
    border-bottom: 1px solid rgba(26, 26, 26, .1);
}

.kdc-vov-row {
    display: flex;
    align-items: center;
    gap: 4px;
}

/* L'icône porte la couleur de la valeur (style inline) : c'est ce qui rend une ligne lisible d'un coup
   d'œil, sans revenir à la légende. Elle reste un poil plus grande que le texte — c'est elle qu'on lit
   en premier, et la réduire à la taille du chiffre lui ferait perdre sa fonction de repère. */
.kdc-vov-ico {
    font-size: 12px;
    line-height: 1;
}

.kdc-vov-val {
    font-weight: 600;
}

/* Mode « loin » (dézoom) ou valeurs masquées : l'étiquette s'efface, la pastille reste. Les deux états
   sont volontairement le MÊME rendu — cf. l'en-tête de valueoverlay.js. */
.kdc-vov--dots .kdc-vov-card {
    display: none;
}

/* Masquée par le DÉCLUTTERING : visibility et non display, pour rester MESURABLE — _declutter relit le
   getBoundingClientRect des cartes masquées à la passe précédente ; en display:none elles mesuraient 0×0,
   ressortaient par-dessus leurs voisines et clignotaient à chaque fin de déplacement. La carte étant en
   position absolue, sa boîte invisible n'influe sur aucune autre. */
.kdc-vov-m--hidden .kdc-vov-card {
    visibility: hidden;
}

/*------------------------------------------------------------------
[29. Échelle de tailles de texte — utilitaires .fs-*]
*/

/* Reprise de theme.css, branchée sur les jetons (tokens.css, section
   Typographie). Douze crans EN PIXELS, là où Bootstrap n'en offre que six et
   en rem : Cockpit est une UI dense et distingue fs-11 de fs-12.

   ⚠️ .fs-1 à .fs-6 NE SONT PAS ICI. Elles appartiennent à Bootstrap (tailles
   de titre), theme.css ne les a jamais définies, et 91 usages du markup
   (fs-4, fs-5, fs-6) s'appuient dessus aujourd'hui. Les redéfinir changerait
   leur rendu — c'est le seul piège de cette famille, et il est silencieux.

   Le !important vient du thème d'origine et est CONSERVÉ : ces classes sont
   posées pour trancher, souvent sur un composant DevExpress qui impose sa
   propre taille par un sélecteur plus spécifique. Le retirer rendrait la
   moitié des appels inopérants sans le moindre signal.

   L'interlettrage et l'interligne accompagnent chaque cran depuis le thème :
   ils font partie du réglage optique, pas de la taille seule. */
.fs-10 { font-size: var(--kd-fs-10) !important; letter-spacing: .0180093em; line-height: 15px; }
.fs-11 { font-size: var(--kd-fs-11) !important; letter-spacing: .0128359em; line-height: 16px; }
.fs-12 { font-size: var(--kd-fs-12) !important; letter-spacing: .00849077em; line-height: 18px; }
.fs-13 { font-size: var(--kd-fs-13) !important; letter-spacing: .00484144em; line-height: 19px; }
.fs-14 { font-size: var(--kd-fs-14) !important; letter-spacing: .00177646em; line-height: 22px; }
.fs-15 { font-size: var(--kd-fs-15) !important; letter-spacing: -.000797757em; line-height: 23px; }
.fs-16 { font-size: var(--kd-fs-16) !important; letter-spacing: -.00295978em; line-height: 24px; }
.fs-18 { font-size: var(--kd-fs-18) !important; letter-spacing: -.00295978em; line-height: 24px; }
.fs-20 { font-size: var(--kd-fs-20) !important; letter-spacing: -.00295978em; line-height: 24px; }
.fs-24 { font-size: var(--kd-fs-24) !important; letter-spacing: -.00295978em; line-height: 24px; }
.fs-30 { font-size: var(--kd-fs-30) !important; letter-spacing: -.00295978em; line-height: 24px; }
.fs-40 { font-size: var(--kd-fs-40) !important; letter-spacing: -.00295978em; line-height: 24px; }

/*------------------------------------------------------------------
[30. Page de connexion]
*/

/* Reprise de theme.css § 22. Le seul écran du thème acheté que Cockpit ait gardé :
   il vit hors du shell (pas de menu, pas de barre supérieure), d'où une mise en page
   qui ne ressemble à aucune autre et qui ne se range dans aucune autre catégorie.

   La déclaration `background-color: var(--primary-color)` qui ouvrait le bloc a été
   retirée : le `background` de la ligne suivante est un raccourci, il écrase la couleur
   qui la précède. Elle n'a jamais rien peint. */
.login-wrapper {
    width: 100%;
    height: 100%;
    overflow: hidden;
    background: url(/assets/img/theme/login.webp);
    background-size: cover;
    background-repeat: no-repeat;
    background-position: bottom;
}

.login-wrapper > * {
    height: 100%;
}

.login-wrapper .bg-pic {
    position: absolute;
    width: 100%;
    overflow: hidden;
}

.login-wrapper .bg-pic > img {
    height: 100%;
    opacity: .6;
}

/* Le panneau de saisie, calé à droite de la photo. Il passe pleine largeur dès que
   l'écran ne peut plus tenir les deux côte à côte. */
.login-wrapper .login-container {
    width: 496px;
    display: block;
    position: relative;
    float: right;
}

.login-wrapper .bg-caption {
    width: 550px;
}

@media (max-width: 768px) {
    .login-wrapper .login-container {
        width: 100%;
    }
}

/*------------------------------------------------------------------
[31. Onglets — le composant NavLinks]

Déménagé de theme.css (« Tabs & Accordians »). Consommateurs : le composant
partagé NavLinks (WebComponents/Components/UI/NavLinks/), qui pose
`nav nav-tabs nav-tabs-simple` et ajoute `nav-tabs-left` en mode vertical,
plus trois pages du module CDC qui posent `nav-tabs` seul.

⚠️ Bootstrap 5 attend `.nav-link` sur le <a> ; NavLinks n'en met pas, d'où
des sélecteurs en `> li > a`. Ne pas « corriger » vers .nav-link sans
reprendre le composant : les deux jeux de règles se marcheraient dessus.

Les valeurs sont reprises telles quelles : cette catégorie est un
DÉMÉNAGEMENT, pas une refonte. L'harmonisation des couleurs sur les jetons
appartient au lot des primitives.
*/

.nav-tabs {
    position: relative;
}

.nav-tabs > li {
    padding-left: 0;
    padding-right: 0;
}

.nav-tabs > li > a {
    display: block;
    border-radius: 0;
    padding: 13px 17px;
    margin-right: 0;
    font-family: var(--kd-font-title);
    font-weight: 500;
    letter-spacing: .06em;
    color: #757575;
    font-size: 10.5px;
    min-width: 70px;
    text-transform: uppercase;
    border-color: transparent;
    position: relative;
    line-height: 1.7em;
    cursor: pointer;
}

.nav-tabs > li > a:hover,
.nav-tabs > li > a:focus {
    background: transparent;
    border-color: transparent;
    color: #4b4b4b;
}

.nav-tabs > li > a.active {
    color: #212121;
}

.nav-tabs > li > a.active:hover,
.nav-tabs > li > a.active:focus {
    border-color: #e0e0e0;
    color: #4b4b4b;
    background-color: #e0e0e0;
}

.nav-tabs ~ .tab-content {
    overflow: hidden;
    padding: 15px;
}

/* Onglets en colonne (NavLinks ModeLeft) */
.nav-tabs.nav-tabs-left {
    flex-direction: column;
    flex-shrink: 0;
    float: left;
    padding-right: 0;
}

.nav-tabs.nav-tabs-left:after {
    border-bottom: 0;
}

.nav-tabs.nav-tabs-left > li {
    float: none;
}

/* Le thème écrivait ici `-ms-flex: 1` et `-ms-flex-negative: 0` — la syntaxe
   IE10, qu'aucun navigateur actuel ne lit. Ces deux déclarations n'ont donc
   JAMAIS rien fait. Ne pas les « moderniser » en `flex: 1` : ce serait
   introduire un comportement que la page n'a jamais eu. */
.nav-tabs.nav-tabs-left ~ .tab-content {
    border-left: 1px solid rgba(0, 0, 0, .1);
}

/* Variante « simple » : pas de boîte, un simple trait sous l'onglet actif.
   Le filet de séparation est un ::after sur le <ul>, et le trait actif un
   ::after sur le <a> qu'on fait passer de 0 à 3px — d'où l'animation. */
.nav-tabs-simple {
    border-bottom: 0;
}

.nav-tabs-simple:after {
    content: "";
    border-bottom: 1px solid rgba(0, 0, 0, .1);
    width: 100%;
    position: absolute;
    bottom: -2px;
    z-index: 120;
    left: 0;
}

.nav-tabs-simple > li {
    margin-bottom: 0;
}

.nav-tabs-simple > li > a:after {
    content: "";
    position: absolute;
    display: block;
    width: 100%;
    height: 0;
    left: 0;
    top: 100%;
    z-index: 2;
    background-color: var(--primary-color);
    backface-visibility: hidden;
    transition: all .1s linear 0s;
}

.nav-tabs-simple > li > a.active:after,
.nav-tabs-simple > li > a:hover:after {
    height: 3px;
}

/* Le trait ci-dessus est partagé par l'onglet COURANT et par l'onglet SURVOLÉ :
   la couleur est posée une fois pour toutes sur le `:after` de base. Or les deux
   états ne disent pas la même chose — le survol est une affordance, donc le
   primaire ; l'onglet courant est une SÉLECTION, donc le secondaire (--kd-accent,
   tokens.css). D'où cette règle, plus spécifique (0,2,3 contre 0,1,3), qui ne
   repeint QUE le courant. Elle couvre aussi la variante verticale `.nav-tabs-left`,
   qui ne redéfinit que la géométrie du trait, pas sa couleur.
   Rendu inchangé : --kd-accent vaut var(--kd-primary) par défaut. */
.nav-tabs-simple > li > a.active:after {
    background-color: var(--kd-accent);
}

.nav-tabs-simple > li > a.active:hover,
.nav-tabs-simple > li > a.active:focus {
    background-color: transparent;
    border-color: transparent;
}

/* En colonne, le trait actif bascule à la verticale, contre le bord droit. */
.nav-tabs-simple.nav-tabs-left:after {
    border-bottom: 0;
}

.nav-tabs-simple.nav-tabs-left > li > a:after {
    position: absolute;
    width: 0;
    height: 100%;
    top: 0;
    bottom: 0;
    right: 0;
    left: auto;
}

.nav-tabs-simple.nav-tabs-left > li > a.active:after,
.nav-tabs-simple.nav-tabs-left > li > a:hover:after {
    width: 3px;
}

/*------------------------------------------------------------------
[32. Impression]

Déménagé de theme.css. Le shell n'est pas imprimable tel quel : menu, barre
du haut et voile doivent disparaître, et le contenu reprendre toute la page.

⚠️ Mis à jour au passage. Le thème neutralisait le décalage du contenu par
`padding-left: 0` — c'est ainsi que l'ancien rail décalait. Le shell « boxed »
décale par `margin-left: var(--kd-shell-offset)` (§ 28) ; sans le remettre à
zéro, chaque page sortie à l'imprimante gardait une bande blanche à gauche,
large du menu, alors même que le menu n'est pas imprimé.
*/

@media print {
    /* `.header` a quitté cette liste avec la barre supérieure. Le rail d'outils
       la remplace : c'est du chrome d'application, il n'a rien à faire sur une
       feuille. Idem pour le bouton flottant du mobile. */
    .page-sidebar,
    .kd-tools-frame,
    .kd-tools-fab,
    .overlay {
        display: none;
    }

    .page-container {
        margin-left: 0;
    }

    .page-content-wrapper .content {
        border-top: 0;
    }

    .page-content-wrapper .content .card {
        border: none;
    }

    .page-content-wrapper .content .card .card-body {
        padding: 0;
    }

    .table tbody tr td {
        padding: 10px;
    }
}


/*------------------------------------------------------------------
[33. Listes]

Déménagé de theme.css. La couleur de l'élément actif est passée dans
tokens.css (--bs-list-group-active-bg) : elle y est exprimable.

`.list-inline > li` est la syntaxe Bootstrap 3. Les pages posent aujourd'hui
`.list-inline-item` sur les <li>, que Bootstrap 5 sert déjà — les deux se
superposent. À trancher au lot des primitives, pas ici : la règle du thème
ajoute un padding gauche que .list-inline-item ne met pas.
*/

.list-group-item {
    border: 1px solid var(--page-bg-color);
}

.list-group-item:first-child {
    border-top-left-radius: 2px;
    border-top-right-radius: 2px;
}

.list-group-item:last-child {
    border-bottom-right-radius: 2px;
    border-bottom-left-radius: 2px;
}

.list-inline > li {
    display: inline-block;
    padding-right: 5px;
    padding-left: 5px;
}


/*------------------------------------------------------------------
[34. Barres de progression]

Hauteur, couleurs et rayon sont dans tokens.css (--bs-progress-*) : Bootstrap
les expose tous. Ne restent ici que les deux choses qu'il n'expose pas.

⚠️ CORRECTION d'une affirmation fausse écrite ici en tranche 3 : il était dit
que .progress-bar-indeterminate et .progress-circle-indeterminate n'étaient
« posées nulle part ». C'est INEXACT — cardextended.js les fabrique (:108,
:113, :137). Elles ne sont pas ATTEIGNABLES, ce qui n'est pas la même chose :
`new CardExtended(el)` (cardextended.js:201) ne passe aucune option, donc
`progress` garde sa valeur par défaut 'circle-lg' et seules les branches
`circle`, `bar` et `else` — jamais empruntées — les emploient.

Ces trois branches sont MORTES, et c'est acté : leur CSS est partie en
tranche 3 (df81aa52) et leurs trois images en 412462ad puis dans le commit qui
porte ce commentaire (progress-bar-master, progress-bar-primary,
progress-circle-master). Les rouvrir demanderait de restaurer la CSS *et* les
SVG depuis git — autant dire de les réécrire.

⚠️ Ne pas confondre avec le rouet du § 35 (branche 'circle-lg'), lui bien
vivant : c'est la branche par défaut, empruntée par les 8 widgets d'accueil.
*/

.progress {
    margin-bottom: 20px;
}

/* Pas de variante Bootstrap pour colorer la barre elle-même : --bs-progress-bar-bg
   vaut pour toute la barre de progression, pas par variante. */
.progress-bar-primary {
    background-color: var(--primary-color);
    background-image: none;
}


/*------------------------------------------------------------------
[35. Carte — rouet de rafraîchissement]

RESTAURATION d'une régression introduite au lot 1 (commit 2dca2517).

Ces règles avaient été retirées comme « mortes » parce qu'aucune classe
`card-icon-refresh-lg-*` n'apparaît dans le markup. Elle n'y apparaît en effet
jamais : elle est CONSTRUITE en JavaScript.
cardextended.js:128 pose `card-icon-refresh-lg-${progressColor}-animated`,
et avec les valeurs par défaut (cardextended.js:17-18, `progress: 'circle-lg'`,
`progressColor: 'master'`) la classe réellement insérée dans le DOM est
exactement `card-icon-refresh-lg-master-animated`.

Conséquence mesurée avant correction : au clic sur « rafraîchir » d'un widget
d'accueil, le <i> était bien créé mais calculait 0×0 avec
`background-image: none`, pendant que l'ancienne icône recevait `.fade`
(donc `opacity: 0` via `.fade:not(.show)` de Bootstrap). Résultat : plus rien
à l'écran pendant tout le rafraîchissement. 8 widgets concernés sur l'accueil.

⚠️ LEÇON : une classe absente du markup n'est pas une classe morte. Avant de
couper, chercher aussi les gabarits de chaîne en JS (`${...}`) et en C#
($"..."), pas seulement les attributs class= littéraux.

La variante `white` est conservée : c'est l'autre valeur que le gabarit
accepte, même si aucun appelant ne la passe aujourd'hui.
*/

.card-icon-refresh-lg-master-animated,
.card-icon-refresh-lg-white-animated {
    width: 15px;
    height: 15px;
    display: block;
    background-size: cover;
    opacity: 0;
    transition: opacity .3s ease;
}

.card-icon-refresh-lg-master-animated.active,
.card-icon-refresh-lg-white-animated.active {
    opacity: 1;
}

.card-icon-refresh-lg-master-animated {
    background-image: url("/assets/img/theme/progress/progress-circle-lg-master.svg");
}

.card-icon-refresh-lg-white-animated {
    background-image: url("/assets/img/theme/progress/progress-circle-lg-white.svg");
}


/*------------------------------------------------------------------
[36. Badges]

Taille, graisse, rayon et couleur sont dans tokens.css (--bs-badge-*). Ne
restent ici que ce que Bootstrap 5.3 n'expose par aucune variable, plus le
rembourrage que tokens.css refuse délibérément de porter (il explique
pourquoi : l'uniformiser depuis les jetons resserrait les badges de 3,85 px
à 2,42 px en vertical).

Deux déclarations du thème sont abandonnées comme redondantes : font-family
(le corps de page est déjà en Inter via --bs-body-font-family) et
text-shadow: none (rien n'en pose dans le dépôt).
*/

/* Bootstrap ne donne AUCUN fond par défaut à .badge : il attend une utilitaire
   .text-bg-* ou .bg-*. Cockpit veut un gris neutre quand rien n'est précisé.
   Sans !important : les utilitaires de fond, elles, en ont un et gagnent donc
   bien — vérifié sur .text-bg-danger. */
.badge {
    background-color: #e0e0e0;
}

/* Le bas est asymétrique (4px contre 3,85px en haut) : --bs-badge-padding-y
   est unique pour les deux côtés, la nuance n'est pas exprimable en variable. */
.badge {
    padding-left: 6px;
    padding-right: 6px;
    padding-bottom: 4px;
}

/* Seule variante de couleur que le thème déclarait et que Cockpit pose encore
   à la main. Les autres passent par .text-bg-*. */
.badge-danger {
    background-color: #d83c31;
    color: #fff;
}


/*------------------------------------------------------------------
[37. Helpers de position et de débordement]

⚠️ .scrollable est LOAD-BEARING : BasketTopBar.razor:39 pose
`notification-body scrollable` avec un max-height en ligne, et son
.notification-body n'a PAS d'ancêtre .notification-panel — les règles du thème
qui donnaient le débordement sont donc parties avec la section Notifications.
.scrollable est désormais la seule source de overflow-y sur le panier. La
retirer ferait déborder le panneau en silence.

Tous ces helpers ont un équivalent Bootstrap 5.3 (.overflow-y-auto,
.overflow-auto, .position-absolute + .bottom-0/.start-0/.end-0, .h-100, .w-100).
Les remplacer dans le markup appartient au lot des primitives : ce n'est pas
une équivalence stricte (.w-100 ajoute un !important que .full-width n'a pas,
et app.css § 23 s'appuie sur .content.full-height).
*/

.scrollable {
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
}

.auto-overflow {
    overflow: auto;
}

.bottom-left {
    position: absolute !important;
    bottom: 1px;
    left: 0;
}

.bottom-right {
    position: absolute !important;
    bottom: 0;
    right: 0;
}

.pull-bottom {
    position: absolute !important;
    bottom: 0;
}

.full-height {
    height: 100% !important;
}

.full-width {
    width: 100%;
}


/*------------------------------------------------------------------
[38. Helpers de bordure]

⚠️ NE PAS convertir vers .border-* de Bootstrap sans lire ceci.

Les .b-* du thème ne posent que STYLE + LARGEUR : la couleur retombe donc sur
currentColor. Les .border-* de Bootstrap posent style + largeur + COULEUR, en
!important. Ce n'est pas la même chose, et deux écrans en dépendent :

  AssetsExplorerSidebarTabSearchAsset.razor:13
  AssetsExplorerSidebarTabInteractions.razor:76
      class="… b-b @(sélectionné ? "active text-primary b-primary" : "b-grey")"

`.b-primary` n'existait dans AUCUN CSS du dépôt — modificateur orphelin, sans
effet : il a été RETIRÉ du markup des deux écrans ci-dessus, sans rien changer au
rendu. Le trait coloré sous l'élément sélectionné vient en réalité de ce que
.b-b ne pose pas de couleur : la bordure hérite de currentColor, donc de la
teinte imposée par .text-primary. Ça marche par accident. Passer à
.border-bottom écraserait cette couleur en gris, sans la moindre erreur.

La règle de base est indispensable : c'est elle qui pose border-style: solid.
Les cinq modificateurs ne donnent qu'une largeur ; sans elle, aucun ne dessine.
*/

.b-a,
.b-r,
.b-l,
.b-t,
.b-b {
    border-style: solid;
    border-width: 0;
}

.b-r { border-right-width: 1px; }
.b-l { border-left-width: 1px; }
.b-t { border-top-width: 1px; }
.b-b { border-bottom-width: 1px; }
.b-a { border-width: 1px; }

.b-dashed { border-style: dashed; }

.b-grey { border-color: rgba(33, 33, 33, .14); }
.b-white { border-color: #fff; }


/*------------------------------------------------------------------
[39. Gouttière resserrée]

Porteur : PageWrapper.razor. Bootstrap 5.3 ferait `--bs-gutter-x: 10px`, mais
le périmètre diffère : la règle du thème ne vise que les enfants `col-*`, là
où Bootstrap applique à `.row > *`. Les enfants directs d'un .row SANS classe
col-* garderaient donc 12px aujourd'hui et passeraient à 5px après conversion
(cas concret : AssetDetails.razor:161-166). Conversion à faire au lot des
primitives, avec un diff de rendu.

Les deux sélecteurs d'origine [class^="col-"] et [class*="col-"] ont été
fusionnés : le premier est inclus dans le second.
*/

.sm-gutter .row > [class*="col-"] {
    padding-left: 5px;
    padding-right: 5px;
}

.sm-gutter .row {
    margin-left: -5px;
    margin-right: -5px;
}

.v-align-top {
    vertical-align: top !important;
}


/*------------------------------------------------------------------
[40. Cases à cocher, boutons radio et interrupteurs]

LOT C — LE SKIN DU THÈME EST ABANDONNÉ, ON REPART DU NATIF BOOTSTRAP.

Ce qu'il y avait ici : 161 lignes venues de theme.css qui masquaient l'input
(`opacity: 0; position: absolute; z-index: -1`) et redessinaient la case dans
le `label::before`, le crochet étant un SPRITE PNG animé en 9 étapes dans le
`label::after`. Quinze couleurs en dur, dont un bleu de focus #78c8fe qui
n'appartient à aucune palette de Cockpit. Les interrupteurs, eux, étaient déjà
passés au natif (toutes ces règles étaient cadrées par `:not(.form-switch)`) :
il ne restait à reprendre que la case et le bouton radio.

CE QUE L'UTILISATEUR VOIT CHANGER — c'est un choix graphique, pas du ménage :
  - une case COCHÉE se remplit du PRIMAIRE avec un crochet clair, là où elle
    restait blanche avec un crochet gris : l'état coché se lit enfin de loin ;
  - le crochet apparaît d'un coup, il n'est plus « tracé » en 9 étapes ;
  - un bouton radio SÉLECTIONNÉ devient une pastille primaire à point clair, au
    lieu d'un anneau gris (#757575) ;
  - la bordure d'une case ÉTEINTE passe de #c9c9c9 à --kd-text-muted : 1,7:1
    sur blanc, sous le seuil de 3:1 de WCAG 1.4.11 pour la limite d'un
    contrôle, contre 3,1:1 maintenant. C'est déjà l'arbitrage retenu pour la
    piste des interrupteurs, plus bas ;
  - l'anneau de focus clavier n'est plus le bleu #78c8fe du thème mais l'anneau
    primaire du reste de l'application ;
  - les cases posées HORS d'un `.form-check` (Utilisateurs, Explorateur de
    métriques) étaient restées BLEU BOOTSTRAP — le skin ne les atteignait pas,
    et les reprises de couleur étaient cadrées sur `.form-check.form-switch`.
    Elles deviennent vertes comme les autres.

GÉOMÉTRIE INCHANGÉE, délibérément : case de 16 px calée à 2 px du haut, texte
en retrait de 25 px, 10 px sous chaque ligne. Le natif de Bootstrap donnerait
une case de 1em — 12 px ici — avec un retrait de 1,5em, soit une cible de clic
PLUS PETITE qu'aujourd'hui sur une interface déjà dense. On garde donc les
mesures du thème et on ne reprend que sa peinture.

⚠️ LE POINT TECHNIQUE : UNE IMAGE DE FOND NE LIT PAS UNE VARIABLE.
Bootstrap peint le crochet, le point du radio et le bouton de l'interrupteur
avec des SVG encodés en `background-image: url("data:image/svg+xml,…")`, dont
la couleur est FIGÉE dans le SVG (`stroke='%23fff'`, `fill='%23fff'`,
`fill='%2386b7fe'`). Aucun habillage client ne peut les atteindre, et `var()`
ne s'interpole pas dans un `url()`. Deux techniques les libèrent, employées
chacune selon la FORME à dessiner :

  1. LES RONDS — point du radio, bouton de l'interrupteur — deviennent des
     `radial-gradient`. Un dégradé EST une image de fond, et il accepte `var()`.
     Aucun pseudo-élément, aucune perte de géométrie : les rayons sont
     recalculés à l'identique, le détail est en regard de chaque règle.
  2. LE CROCHET, et la barre de l'état indéterminé, ne sont pas des formes
     exprimables en dégradé : ils passent en MASQUE (`mask-image`) posé sur un
     `::before` de l'input et peint en `var(--kd-on-primary)`. Le masque lit
     l'ALPHA de l'image : le SVG de Bootstrap convient TEL QUEL parce qu'il
     porte `fill='none'` et ne dessine que le trait — un SVG entièrement opaque,
     lui, masquerait tout. Le `stroke` blanc est conservé pour que les moteurs
     qui interprètent un masque en LUMINANCE plutôt qu'en alpha rendent la même
     chose.

  ⚠️ POURQUOI UN `::before`, ET PAS UN MASQUE SUR L'INPUT LUI-MÊME.
  Un masque rogne TOUT ce que l'élément peint : fond, bordure, ombre, contour.
  Masquer l'input à la forme du crochet ferait donc disparaître la case, et il
  ne resterait qu'un crochet flottant. Il faut deux surfaces : l'input porte la
  case, son `::before` porte le crochet. C'est aussi pourquoi les ronds passent
  par un dégradé et non par un masque — le masque y aurait effacé la piste.

  ⚠️ LE FILET DE SÉCURITÉ QUI VA AVEC. Les pseudo-éléments d'un <input> sont
  rendus par Chromium et WebKit, mais la spécification ne les garantit pas sur
  un élément remplacé. L'IMAGE DE FOND NATIVE DE BOOTSTRAP EST DONC CONSERVÉE
  DESSOUS : avec la valeur d'usine (--kd-on-primary = #ffffff) les deux crochets
  se superposent au pixel, et un moteur qui ignorerait le `::before` affiche
  exactement ce qu'on voit aujourd'hui. La différence n'apparaîtrait qu'avec un
  --kd-on-primary personnalisé, et seulement sur ce moteur-là.

Supprimés avec le skin : le sprite PNG du crochet et son `@keyframes
checkbox-check` (aucun autre consommateur, vérifié), le `box-shadow` de focus
bleu, les états `:hover:active` qui grisaient la case, et `label:hover { color }`.
Conservés parce que ce n'est pas de la peinture : le curseur en main sur
l'étiquette et l'absence de sélection de texte au clic répété.
*/

/* Rythme vertical des listes de cases — valeur du thème. Bootstrap donnerait
   .125rem, soit 2 px : les listes de réglages se tasseraient. */
.form-check {
    margin-bottom: 10px;
}

/* Géométrie du thème reconduite sur le contrôle natif. Bootstrap pose
   `padding-left: 1.5em` sur .form-check et `margin-left: -1.5em` sur l'input ;
   on reprend le couple avec les 25 px du thème pour que le texte ne bouge pas
   d'un pixel, et les 16 px de la case pour ne pas rétrécir la cible de clic.
   Cadré sur :not(.form-switch) : l'interrupteur garde ses 2.5em de retrait et
   sa piste de 2em. */
.form-check:not(.form-switch) {
    padding-left: 25px;
}

.form-check:not(.form-switch) .form-check-input {
    width: 16px;
    height: 16px;
    margin-top: 2px;
    margin-left: -25px;
}

/* ⚠️ LA COULEUR NE VIENT PAS TOUTE SEULE, contrairement à ce que laisse croire
   le reste de la couche de jetons : Bootstrap 5.3 écrit ces couleurs EN DUR
   dans .form-check-input, sans variable pour les piloter.
       .form-check-input:checked { background-color: #0d6efd; border-color: #0d6efd }
       .form-check-input:focus   { border-color: #86b7fe }
   --bs-primary vaut bien le vert de Cockpit, mais aucune règle de
   .form-check-input ne le lit — c'est le piège « toutes les variables Bootstrap
   ne sont pas dérivées » (même famille que --bs-secondary-color).
   L'état ÉTEINT doit rester visible, lui aussi : la piste prend par défaut
   --bs-form-check-bg, que Bootstrap fait pointer sur --bs-body-bg, donc chez
   nous le #f4f4f4 du fond de page — 1,10:1 sur une carte blanche, bordure
   comprise à 1,27:1. Fond --kd-surface + bordure --kd-text-muted => 3,1:1. */
.form-check-input {
    --bs-form-check-bg: var(--kd-surface);
    border-color: var(--kd-text-muted);
    cursor: pointer;
    transition: background-color .14s linear, border-color .14s linear;
}

.form-check-input:checked,
.form-check-input[type="checkbox"]:indeterminate {
    background-color: var(--kd-primary);
    border-color: var(--kd-primary);
}

/* Le CROCHET et la BARRE de l'état indéterminé : masque sur le ::before, cf.
   l'en-tête de section. Mêmes SVG que Bootstrap, même cadrage
   (`mask-size: contain`, `mask-position: center`, sur une boîte de la taille de
   la zone de fond), donc même dessin — seule la couleur devient pilotable. */
.form-check-input[type="checkbox"]:checked::before,
.form-check-input[type="checkbox"]:indeterminate::before {
    content: "";
    display: block;
    width: 100%;
    height: 100%;
    background-color: var(--kd-on-primary);
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
    -webkit-mask-position: center;
            mask-position: center;
    -webkit-mask-size: contain;
            mask-size: contain;
}

.form-check-input[type="checkbox"]:checked::before {
    -webkit-mask-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");
            mask-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");
}

.form-check-input[type="checkbox"]:indeterminate::before {
    -webkit-mask-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e");
            mask-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e");
}

/* Un interrupteur est un `input[type="checkbox"]` lui aussi : sans cette
   annulation, il hériterait du crochet. Spécificité identique aux deux règles
   ci-dessus (0-3-1), donc c'est l'ORDRE qui tranche — ne pas la remonter. */
.form-switch .form-check-input[type="checkbox"]::before {
    content: none;
}

/* Le POINT du bouton radio. Le SVG de Bootstrap est un cercle de rayon 2 dans
   un viewBox de 8 : un diamètre qui vaut exactement la MOITIÉ de la boîte, quel
   que soit le diamètre de celle-ci. `background-size: 50% 50%` redonne cette
   proportion, `closest-side` inscrit le cercle dans la tuile, et le
   `background-position: center` posé par Bootstrap le recentre. Le demi-pixel
   entre les deux arrêts évite un bord crénelé — un arrêt franc à 100 % n'est
   pas lissé par tous les moteurs. */
.form-check-input:checked[type="radio"] {
    --bs-form-check-bg-image: radial-gradient(circle closest-side,
        var(--kd-on-primary) calc(100% - .5px), transparent 100%);
    background-size: 50% 50%;
}

/* Le BOUTON de l'interrupteur, même conversion. Le SVG de Bootstrap est un
   cercle de rayon 3 dans un viewBox de 8, redimensionné par
   `background-size: contain` à la HAUTEUR de la piste (1em) : diamètre 0,75em.
   Un dégradé n'a PAS de dimension propre — `contain` lui donnerait toute la
   piste (2em × 1em) et le rond deviendrait une ellipse. D'où la tuile explicite
   de 1em et le rayon de 0,375em, qui redonnent la géométrie au pixel.
   Les trois états sont repris un par un : Bootstrap redéclare
   --bs-form-switch-bg dans `:focus` et `:checked` avec une spécificité
   supérieure à celle de sa règle de base — ne déclarer que la base laisserait
   le SVG revenir dès qu'on focalise ou qu'on coche. */
.form-switch .form-check-input,
.form-switch .form-check-input:focus,
.form-switch .form-check-input:checked {
    --bs-form-switch-bg: radial-gradient(circle .375em at 50% 50%,
        var(--kd-switch-knob) calc(100% - .5px), transparent 100%);
    background-size: 1em 1em;
}

/* Éteint : le gris translucide de Bootstrap, conservé tel quel. Le bouton d'un
   interrupteur au repos n'est pas une couleur de marque, et il doit rester
   lisible quelle que soit la couleur choisie par le client. */
.form-switch .form-check-input {
    --kd-switch-knob: rgba(0, 0, 0, .25);
}

/* ⚠️ CORRECTION DE RENDU. Au clavier, Bootstrap repeignait le bouton d'un
   interrupteur ÉTEINT en #86b7fe — son bleu de focus, et la dernière tache
   bleue des contrôles de formulaire de Cockpit. Il prend le primaire. */
.form-switch .form-check-input:focus {
    --kd-switch-knob: var(--kd-primary);
}

/* Allumé : « texte sur primaire », comme le crochet. Placée APRÈS l'état de
   focus, comme chez Bootstrap : un interrupteur à la fois coché et focalisé
   garde son bouton clair. */
.form-switch .form-check-input:checked {
    --kd-switch-knob: var(--kd-on-primary);
}

/* Focus. Le reset hérité `input[type="checkbox"]:focus { outline: none;
   box-shadow: none }` (§ 44, spécificité 0-2-1) écrase celui de Bootstrap
   (0-2-0) : c'est lui qui éteint l'anneau bleu et l'ombre de 0,25rem, et c'est
   voulu. Il faut donc AU MOINS sa spécificité pour rendre un repère clavier,
   d'où le `input.` en tête — une règle en `.form-check-input:focus-visible`
   (0-2-0) ne passerait pas. Même anneau que le reste de l'application
   (cf. .kd-sidebar-toggle § 28, .kd-tool § 46). */
input.form-check-input:focus {
    border-color: var(--kd-primary);
}

input.form-check-input:focus-visible {
    outline: 2px solid var(--kd-primary);
    outline-offset: 2px;
}

/* Affordance de l'étiquette, reprise du thème : elle est cliquable par son
   `for`, donc elle prend la main, et un clic répété ne doit pas sélectionner
   son texte. L'état désactivé est couvert par Bootstrap, qui pose lui-même
   `cursor: default` et l'opacité sur `.form-check-input:disabled ~ .form-check-label`.
   `.form-check label` en plus de `.form-check-label` : quelques écrans posent un
   <label for> nu, sans la classe de Bootstrap — le retrait du texte leur vient
   du padding du conteneur, mais l'affordance, elle, doit être visée. */
.form-check label,
.form-check-label {
    cursor: pointer;
    -webkit-user-select: none;
            user-select: none;
}


/*------------------------------------------------------------------
[41. Typographie]

Déménagée de theme.css (section « Typography »), moins 13 blocs morts.
C'est la typographie de TOUTE l'application : échelle des titres, corps de
texte, tableaux, et les utilitaires sémantiques. Déménagement à l'identique.

⚠️ LE POINT IMPORTANT, POUR LE LOT DES PRIMITIVES.
Ce bloc redéclare des noms d'UTILITAIRES BOOTSTRAP (.text-danger, .bg-success,
.text-center…) EN !important. Chargé après Bootstrap, c'est lui qui gagne.
Ce ne sont donc pas des doublons inertes : ils peignent réellement l'app.

La raison est structurelle : le thème acheté tenait DEUX valeurs par couleur
sémantique — une pour le FOND, une assombrie pour le TEXTE :

    fond     #19ad79   #ffd945   #d83c31   #3b4752
    texte    #148e63   #927400   #b82d23   #3b4752

Les retirer sèchement ne « rend pas la main à Bootstrap » : cela remplace la
couleur de texte par la couleur de fond. Mesuré sur .text-warning :
#927400 → #ffd945 fait tomber le contraste sur blanc de 4,5:1 à ~1,4:1 —
texte quasi invisible, sur 130 porteurs dans 43 fichiers, dont les icônes
« acquittée » de la console d'alarmes.

✔ RÉSOLU pour les quatre couleurs sémantiques (arbitrage tranché).
La seconde valeur est maintenant tenue par tokens.css, dérivée par règle
(--kd-success-text / -warning- / -danger- / -info-, shade 60 % de Bootstrap)
et câblée sur --bs-*-text-emphasis. Les quatre règles .text-* de ce bloc
consomment les jetons ; elles restent nécessaires parce que Bootstrap fait
servir à .text-success la teinte de FOND. Effets mesurés :
  - les quatre franchissent enfin 4,5:1 sur les trois fonds réels — avant,
    success et warning échouaient partout (détail chiffré dans tokens.css) ;
  - .text-*-emphasis servait la palette d'USINE de Bootstrap (#0a3622 pour
    success, dérivé de son #198754) et sert désormais celle de Cockpit ;
  - ces quatre couleurs deviennent pilotables par un habillage client.

⚠️ RESTE À FAIRE, hors de cet arbitrage — deux points connus :
  - Les .bg-* de ce bloc gardent leurs hex en dur. Attention avant de les
    retirer : leur `color: #fff` n'a PAS d'équivalent chez Bootstrap, et
    `.bg-info` (#3b4752) masque --kd-info (#929aac), donc les retirer change
    aussi la teinte, pas seulement la provenance.
  - Les --bs-*-bg-subtle restent ceux d'usine : un `badge bg-primary-subtle`
    est donc BLEU pâle (#cfe2ff, dérivé du bleu de Bootstrap) alors que le
    primaire de Cockpit est vert. ~20 porteurs. Même correctif que ci-dessus
    (tint 80 % des jetons), mais c'est un changement visible à trancher.
  - Sémantique Bootstrap : .text-success devrait valoir la teinte vive et
    .text-success-emphasis la teinte sombre. Ici les deux valent la sombre,
    pour ne pas toucher 130 porteurs dans 43 fichiers dont 4 modules clients
    qui ne compilent pas par défaut. Migration à coûter séparément.

⚠️ .pg-icon et son @font-face voyagent ensemble. La classe n'a en réalité
qu'UN porteur qui la rende indispensable : le burger mobile (TopBar.razor:17),
dont le texte « menu » est une LIGATURE de la police pages-icon. Sur les ~350
autres occurrences (enveloppes du menu), son font-size:20px est battu par
`.icon-thumbnail > i` et le reste est écrasé par `.ph` : elle n'y produit rien.
Remplacer ce burger par `<i class="ph ph-list">` permettrait de supprimer d'un
coup .pg-icon, son @font-face et pages-icon.woff — mais les glyphes de carte
(§ 27) en dépendent aussi. À traiter d'ensemble.

Retiré comme mort (aucun porteur, vérifié en deux passes) : .logo, .text-color,
le SECOND .text-center (doublon strict du premier, même valeur, même
spécificité), .table > tfoot (un seul <tfoot> dans le dépôt, dans un <table>
sans classe), h3/h4/h5 small (aucun <small> descendant d'un titre), et les
éléments qu'aucune page n'écrit : address ×2, address a, abbr ×2, acronym, dt,
dd, kbd, blockquote p. <acronym> ne peut même pas revenir : HTML5 l'a supprimé.

Doublons internes laissés en place faute d'être morts, mais à noter : la
section a été assemblée par concaténation et déclare deux fois address, abbr
et hr.
*/

html {
  font-size: 100%;
  -ms-text-size-adjust: 100%;
  -webkit-text-size-adjust: 100%;
}

body {
  color: #4b4b4b;
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
  font-size: 12px;
  font-weight: normal;
  letter-spacing: 0.01em;
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;
  -ms-text-size-adjust: 100%;
  -webkit-font-feature-settings: "kern" 1;
  -moz-font-feature-settings: "kern" 1;
}

/* Aligns 
------------------------------------
*/
.text-end {
    text-align: right !important;
}
.text-start {
    text-align: left !important;
}

.text-center {
    text-align: center !important;
}

.vertical-align-middle {
    vertical-align: middle !important;
}

/* Headings 
------------------------------------
*/
h1,
h2,
h3,
h4,
h5,
h6 {
  margin: 10px 0;
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;
  -ms-text-size-adjust: 100%;
  font-weight: 500;
  color: inherit;
}

h1 {
  font-size: 44px;
  line-height: 50px;
  letter-spacing: -0.0141279em;
}

h2 {
  font-size: 30px;
  line-height: 38px;
  letter-spacing: -0.021em;
}

h3 {
  font-size: 27px;
  line-height: 35px;
  letter-spacing: -0.0114923em;
}

h4 {
  font-size: 22px;
  line-height: 32px;
  letter-spacing: -0.00865734em;
}

h5 {
  font-size: 18px;
  line-height: 26px;
  letter-spacing: -0.00630069em;
}

h6 {
    font-size: 14px;
    line-height:20px;
    letter-spacing: -0.00630069em;
}




/* Lins and Others
------------------------------------
*/
a {
    text-shadow: none !important;
    color: var(--primary-color);
    transition: color 0.1s linear 0s, background-color 0.1s linear 0s, opacity 0.2s linear 0s !important;
    font-weight: 500;
}

a:focus,
a:hover,
a:active {
  color: var(--secondary-color);
}

a,
a:focus,
a:hover,
a:active {
  outline: 0 !important;
  text-decoration: none;
}

a[ng-click] {
  cursor: pointer;
}

br {
  line-height: normal;
  clear: both;
}

p {
  display: block;
  color: inherit;
  font-size: 12px;
  font-weight: normal;
  letter-spacing: 0.00177646em;
  line-height: 21px;
  margin: 0px 0px 10px 0px;
  font-style: normal;
  white-space: normal;
}

small,
.small {
  line-height: 18px;
  font-size: 85.714%;
}

ul,
ol {
  margin-bottom: 10px;
}

ul > li,
ol > li {
  padding-left: 3px;
  line-height: 24px;
}





ul.no-style,
ol.no-style {
  list-style: none;
  padding-left: 5px;
}



blockquote {
  padding: 4px 0 0 18px;
  border-left: 0;
}

blockquote:before {
  content: "\e95d";
  font-family: "pages-icon";
  font-size: 20px;
  margin-right: 6px;
  float: left;
  position: relative;
  top: -13px;
}


blockquote small {
  line-height: 29px;
  color: #4b4b4b;
}

blockquote small:before {
  content: "—";
  margin-right: 6px;
}

hr {
  border-color: #e0e0e0;
}

hr.double {
  border-width: 2px;
}







/* Standard HTML Typography tags
------------------------------------
*/
code {
  color: #757575;
  background-color: rgba(33, 33, 33, 0.05);
  font-size: 97%;
  position: relative;
  line-height: inherit;
  border-radius: 3px;
  padding: 5px 7px;
  margin: 0;
}

code:hover {
  color: #0072ec;
}



figcaption {
  font-size: 12px;
  margin-top: 10px;
  font-weight: 400;
  display: block;
  letter-spacing: 0.008em;
  text-align: center;
  color: #757575;
  line-height: 1.46;
}

em {
  font-style: italic !important;
  font-family: inherit;
  font-weight: inherit;
}

ins {
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
  border-bottom: 1px solid #d0d0d0;
  text-decoration: none;
  color: #4b4b4b;
  font-weight: normal;
  font-size: 94%;
}

cite {
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
  font-weight: 300;
  color: #757575;
  text-decoration: none;
  font-style: normal;
  hanging-punctuation: first;
}

sup {
  top: -0.28em;
  font-size: 70%;
}

sub {
  bottom: 0.03em;
}

var {
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
  font-feature-settings: "calt" 1, "tnum" 1, "frac" 1, "case" 1, "ss01" 1, "cv11" 1;
}


q {
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
  font-weight: 500;
  font-size: 20px;
  hanging-punctuation: first;
}

q:before {
  content: "\201C";
  color: rgba(0, 0, 0, 0.44);
  font-size: 35px;
}

q:after {
  content: "\201D";
  font-size: 35px;
  color: rgba(0, 0, 0, 0.44);
}

audio {
  margin-top: 42px;
}

hr {
  clear: both;
  margin-bottom: 42px;
  margin-top: 42px;
  border: 0;
  border-bottom: 1px solid rgba(0, 0, 0, 0.13);
}




pre {
  tab-size: 4;
  font-size: 85.714%;
  overflow-x: auto;
  font-family: monospace, monospace;
  line-height: 1.7;
  counter-reset: line;
  background-color: var(--page-bg-color);
  color: #4b4b4b;
  margin: 0px;
  counter-reset: line;
  padding: 17px 20px;
  border-radius: 3px;
  margin-top: 20px;
  margin-bottom: 20px;
}




/* Types 
------------------------------------
*/


/* Font Sizes 
------------------------------------
*/
.small-text {
  font-size: 12px !important;
  letter-spacing: 0.00849077em;
  line-height: 18px;
}

.small-text a {
  text-decoration: underline;
}





/* Font Weights
------------------------------------
 */
.normal {
  font-weight: normal !important;
}



/* Misc 
------------------------------------
*/

.all-caps {
  text-transform: uppercase;
  letter-spacing: 0.07em !important;
}

.text-uppercase {
  text-transform: uppercase !important;
  letter-spacing: 0.07em !important;
}

.muted {
  color: #757575;
}



.text-ellipsis {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Monochrome Colors
------------------------------------
 */












/* Primary
------------------------------------
*/
/* LOT C — LES UTILITAIRES DE FOND SONT TOKENISÉS, ET ILS RESTENT.
   Le brief demandait de les retirer « s'ils n'existent que pour les .label-* ».
   Mesuré : ce n'est pas le cas. Porteurs hors étiquettes, hors .text-bg-* et
   hors .bg-*-subtle — .bg-primary 4 fichiers, .bg-success 6, .bg-danger 6,
   .bg-warning 5, .bg-white 12, .bg-info 1. Ils sont donc conservés, mais leurs
   hexadécimaux passent aux jetons : sans ça, aucun habillage client ne les
   atteint (ils sont en !important, donc ce sont EUX qui peignent, pas les
   utilitaires de Bootstrap branchés dans tokens.css).
   Le `color` reste littéral quand aucun jeton ne le porte : il n'existe pas de
   --kd-on-success / --kd-on-danger, et en inventer trois relève de la palette,
   pas de ce lot. */
.bg-primary {
  background-color: var(--kd-primary) !important;
  color: var(--kd-on-primary);
}









/* Complete 
------------------------------------
*/










/* Success 
------------------------------------
*/
.bg-success {
  background-color: var(--kd-success) !important;
  color: #fff;
}









/* Info
------------------------------------
*/
/* ⚠️ SEUL CHANGEMENT DE COULEUR VISIBLE DE CE BLOC, ET IL EST ASSUMÉ.
   Le #3b4752 du thème — un ardoise sombre — N'EST PAS --kd-info (#929aac, un
   gris clair) : c'était la dernière couleur « info » du thème acheté, hors de
   portée de tout habillage. Le fond passe donc au jeton, et le texte à son
   pendant sombre, sans quoi le blanc tomberait à 2,8:1 sur le gris clair.
   UN SEUL PORTEUR dans tout le dépôt, et c'est un FOND DE PANNEAU, pas du
   texte : RoutingRuleFormDrawer.razor:289 (module EY, écran « Règles
   d'acheminement »), un encart qui entoure le bouton « Ajouter une cible ».
   Son contenu visible est un DxButton, qui porte ses propres couleurs.
   Contraste mesuré du couple retenu : --kd-info-text sur --kd-info = 3,7:1 —
   suffisant pour une limite de composant (WCAG 1.4.11), sous les 4,5:1 du petit
   texte. Si l'ardoise sombre est un choix graphique à garder, il lui faut son
   propre jeton ; elle ne peut pas rester un hexadécimal orphelin. */
.bg-info {
  background-color: var(--kd-info) !important;
  color: var(--kd-info-text);
}









/* Danger 
------------------------------------
*/
.bg-danger {
  background-color: var(--kd-danger) !important;
  color: #fff;
}



.bg-danger-darker {
  background-color: #900f17;
  color: #fff;
}





/* Warning
------------------------------------
 */
.bg-warning {
  background-color: var(--kd-warning) !important;
  color: var(--kd-text);
}









/* Menu 
------------------------------------
*/






/* Gradients
------------------------------------
*/




/* Other Colors
------------------------------------
*/




.bg-white {
  background-color: var(--kd-surface) !important;
  color: var(--kd-text);
}

.bg-transparent {
  background-color: transparent !important;
}

/* Text Colors */




.text-dark {
  color: #212121 !important;
}


.text-primary {
  color: var(--primary-color) !important;
}

.text-complete {
  color: #0054af !important;
}

/* Ces quatre-là ne sont PAS des doublons inertes de Bootstrap : Bootstrap sert
   ici la teinte de FOND (var(--bs-success-rgb)…), illisible en texte. Elles
   servent la teinte de TEXTE, désormais tenue par tokens.css et donc pilotable
   par un habillage client. Cf. l'avertissement en tête du § 41. */
.text-success {
  color: var(--kd-success-text) !important;
}

.text-info {
  color: var(--kd-info-text) !important;
}

.text-warning {
  color: var(--kd-warning-text) !important;
}

.text-danger {
  color: var(--kd-danger-text) !important;
}

.text-black {
  color: #000 !important;
}

.text-white {
  color: #fff !important;
}

/* Text Aligngments
------------------------------------
*/
.text-right {
  text-align: right !important;
}




/* Étiquettes — vocabulaire BOOTSTRAP 3
------------------------------------
LOT C. `.label` a été remplacé par `.badge` dès Bootstrap 4 ; Cockpit écrit
les deux. Les QUATRE variantes de couleur (.label-success, .label-important,
.label-danger, .label-white) ont été RETIRÉES, leurs cinq porteurs migrés vers
`.badge` + l'utilitaire de couleur maison — l'écriture déjà majoritaire dans
le produit (cf. § 36 : « les autres passent par .text-bg-* ») :

    label label-important  ->  badge text-bg-danger    Fiche de site, alerte
                                                       de capacité (SitesDetails)
    label label-important
      | label-success      ->  badge text-bg-danger
                               | text-bg-success       Pastille de valeur des
                                                       listes (CapabilitiesListItem,
                                                       ListItemVertical)
    label label-danger     ->  badge text-bg-danger    Widget « État des
                                                       connecteurs », pastille ERROR
    label label-danger
      | -success | -white  ->  badge text-bg-danger
                               | text-bg-success
                               | bg-white              Widget « Affluence »,
                                                       compteur par espace

Les couleurs sont IDENTIQUES au pixel : --bs-success-rgb vaut 25,173,121 soit
le #19ad79 de .label-success, --bs-danger-rgb vaut 216,60,49 soit le #d83c31 de
.label-danger, et .bg-white (plus bas) pose exactement le #fff / #4b4b4b de
.label-white. Seul le rembourrage bouge : ~1 px de plus par côté, .badge tenant
son padding-y en em (cf. § 36).

⚠️ LA RÈGLE DE BASE `.label` RESTE, elle a encore DEUX porteurs, et l'un des
deux est hors du périmètre de ce lot :
    HWDefaultWelcome.razor:38                 (widget d'accueil)
    Settings/General/HomeWidgetsSettings.razor:124   (écrans de Réglages)
C'est aujourd'hui un DOUBLON STRICT de `.badge` — mêmes 11 px, même graisse
600, même #e0e0e0, même #4b4b4b, même rayon de 3 px — au rembourrage près.
Migrer ces deux porteurs permettrait de la supprimer d'un trait. */
.label {
  padding: 3px 5px;
  font-size: var(--kd-fs-sm);
  line-height: 1;
  background-color: #e0e0e0;
  font-weight: 600;
  color: var(--kd-text);
  border-radius: var(--kd-radius);
}

/* Font Sizes
------------------------------------
tracking =a+b×e(c × z)

a, b and c are constants
a = -0.0223
b = 0.185
c = -0.1745
z = font size

*/
/* Les douze utilitaires .fs-10 … .fs-40 ont migré vers app.css § 29, branchés sur les
   jetons de tokens.css. Valeurs, interlettrage, interligne et !important repris à l identique. */

/* Line-heights
------------------------------------
*/
.lh-normal {
  line-height: normal;
}















/* Font Faces
------------------------------------
*/


.font-montserrat {
  font-family: "Montserrat", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif !important;
}







/* Wells
------------------------------------
*/
















/* Responsive Handlers : Typo
------------------------------------
*/
@media (max-width: 1400px) {
  body,
  p {
    font-size: 12px;
    line-height: 20px;
  }
  h1 {
    font-size: 33px;
    line-height: 44px;
    letter-spacing: -0.08px;
  }
  h2 {
    font-size: 28px;
    line-height: 40px;
  }
  h3 {
    font-size: 24px;
    line-height: 35.88px;
  }
  h4 {
    font-size: 18px;
    line-height: 33.88px;
  }
  h5 {
    font-size: 16px;
    line-height: 25.88px;
  }
  small,
  .small {
    font-size: 89%;
    line-height: 17px;
  }
}

.alert > p,
.alert > ul {
  margin-bottom: 0;
}

.table > tbody > tr > td,
.table > tbody > tr > th {
  line-height: 1.42857143;
}


.table > thead > tr > td,
.table > thead > tr > th {
  line-height: 1.42857143;
}

/* For Windows : Fixes 
------------------------------------
*/


/*------------------------------------------------------------------
Pages Icons
------------------------------------------------------------------ */
@font-face {
  font-family: "pages-icon";
  /* Seul le woff est livré. Le repli ttf (50 Ko) n'était jamais demandé, et l'URL svg
     d'origine était cassée — une variable Sass jamais interpolée, extension .html :
     url("pages-root-urlfonts/pages-icon/pages-icon.html"). Les deux ont été retirés.
     Reliquat du thème Pages : ~13 glyphes seulement, tous doublonnés par Phosphor. */
  src: url("../fonts/pages-icon/pages-icon.woff") format("woff");
  font-weight: normal;
  font-style: normal;
  font-display: block;
}

.pg-icon {
  /* use !important to prevent issues with browser extensions that change fonts */
  font-family: "pages-icon" !important;
  speak: none;
  font-size: 20px;
  font-style: normal;
  font-weight: normal;
  font-variant: normal;
  text-transform: none;
  line-height: 1;
  direction: ltr;
  word-wrap: normal;
  white-space: nowrap;
  /* Enable Ligatures ================ */
  letter-spacing: 0;
  -webkit-font-feature-settings: "liga";
  -moz-font-feature-settings: "liga=1";
  -moz-font-feature-settings: "liga";
  -ms-font-feature-settings: "liga" 1;
  font-feature-settings: "liga";
  -webkit-font-variant-ligatures: discretionary-ligatures;
  font-variant-ligatures: discretionary-ligatures;
  /* Better Font Rendering =========== */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}


/*------------------------------------------------------------------
[42. Ajustements sous 768 px]

Tout ce qui reste de la section « Demo : Only for demo pursposes » de
theme.css — dont le nom mentait : ce n'était pas de la démo, c'était TOUTE la
couche responsive du thème (7 media queries repositionnant menu, conteneur et
contenu).

Elle a été retirée après vérification par MESURE, pas par lecture : le shell a
été relevé à 450, 700, 900 et 1900 px avant coupe, puis après. Sur les quatre
largeurs, un seul écart est apparu — celui que ces helpers corrigent. Tout le
reste (position et largeur du menu, décalage du conteneur, hauteur d'en-tête,
retraits des entrées de menu, vignettes) est désormais posé par le § 28, qui
est chargé après et gagne.

⚠️ CES NOMS MENTENT AUSSI : `sm-p-l-10` pose une MARGE, pas un padding
(margin-left). Seul `.sm-padding-10` pose vraiment un padding. Ne pas les
« corriger » vers les utilitaires Bootstrap sans lire ce que chacun fait.

⚠️ `.sm-padding-10` est LOAD-BEARING. Il est posé sur le conteneur de contenu
(`container-fluid p-3 sm-padding-10`) et son `!important` bat le `.p-3` de
Bootstrap sous 768 px : sans lui, le rembourrage de page passe de 10 à 16 px
sur mobile. C'est le seul écart qu'a révélé la mesure.

Non repris, car sans porteur : `.sm-p-r-0` et `.sm-p-l-0`.
Non repris non plus, car vides ou inertes : le bloc `@media (min-width:1824px)`
(ses largeurs sont en commentaire, et `.hidden-xlg` est ENTIÈREMENT à
l'intérieur d'un commentaire — la règle n'a jamais existé), et
`@media (min-width:700px) .dd`, crochet du plugin nestable supprimé au lot 1.
*/

@media (max-width: 767px) {
    .sm-pull-bottom { position: relative !important; }
    .sm-p-l-10 { margin-left: 10px !important; }
    .sm-p-r-15 { margin-right: 15px !important; }
    .sm-p-l-15 { margin-left: 15px !important; }
    .sm-p-t-30 { margin-top: 30px !important; }
    .sm-p-b-30 { margin-bottom: 30px !important; }
    .sm-padding-10 { padding: 10px !important; }
}


/*------------------------------------------------------------------
[46. Rail d'outils — la colonne de droite et son panneau]

Elle porte les outils de l'APPLICATION — panier de métriques, alarmes, scan QR,
réglages — c'est-à-dire ceux qui sont identiques sur les ~74 écrans. Les outils
d'un écran restent avec son titre.

⚠️ CE N'EST PAS UNE CARTE. Le rail a d'abord été une quatrième boîte blanche,
comme celle du menu : beaucoup de surface pour trois icônes, et une colonne vide
sur les deux tiers de sa hauteur. Le cadre ne fait donc que POSITIONNER — fond
transparent, aucune ombre, aucun rayon. Ce sont les OUTILS qui portent leur
pastille (ronde, blanche, ombrée), et le PANNEAU sa propre carte quand il s'ouvre,
posée à une gouttière du rail.

DEUX ÉLÉMENTS, PAS UN. .kd-tools ne dessine rien (display:contents) : il ne sert
qu'à porter l'état « feuille ouverte » et à ne PAS créer de bloc conteneur, ce
qui piégerait la modale du lecteur de QR. .kd-tools-frame positionne, et le bouton
flottant du mobile se place indépendamment d'elle.

LE PANNEAU RECOUVRE le contenu — la colonne ne se décale pas, ce qui est déjà le
`Mode=Overlap` du DxDrawer de Drawer.razor.

⚠️ LE SEUL ENDROIT OÙ LE CADRE PORTE UN FOND EST LE MOBILE, où il EST la feuille.
Les outils y redeviennent des lignes : une pastille blanche sur une feuille
blanche ne dirait plus rien.

⚠️ LA GÉOMÉTRIE DU CONTENU NE SE COMPTE PLUS DEPUIS LA FENÊTRE. Le bord droit de
la barre, le rembourrage de .content et .card-maximized consomment désormais
--kd-tools-offset (gouttière + rail + gouttière), miroir exact de
--kd-shell-offset à gauche. Les laisser à --kd-shell-gap ferait passer les trois
SOUS le rail.
------------------------------------------------------------------*/

/* display:contents et non un bloc : voir l'avertissement ci-dessus. */
.kd-tools {
    display: contents;
}

/* ⚠️ LE RAIL N'EST PLUS UNE CARTE, C'EST UNE SURFACE — et la nuance a une histoire.
   Il a d'abord été une quatrième BOÎTE BLANCHE flottante sur le fond, pour trois
   icônes : beaucoup de surface pour peu de contenu, et une colonne vide sur les
   deux tiers de sa hauteur. Elle a été retirée, le cadre réduit à un positionneur
   et les outils rendus à leurs pastilles.
   Depuis que le shell est bord à bord, la question ne se pose plus dans les mêmes
   termes : le rail ne flotte plus, il est adossé au bord droit de la fenêtre du
   haut au bas, comme le menu à gauche. C'est le blanc pleine hauteur du handoff,
   et il porte le même filet de structure que les autres coutures.
   Le CADRE, lui, ne fait toujours que positionner : la surface est sur
   `.kd-tools-rail`, pour que le panneau qui s'ouvre à sa gauche reste un tiroir
   distinct et non un morceau du rail.
   `overflow: visible` en conséquence : `hidden` rognerait l'ombre du tiroir. */
.kd-tools-frame {
    position: absolute;
    /* Bord à bord comme le menu : le rail va du haut au bas et finit au bord droit
       de la fenêtre. Il reste TRANSPARENT — ce sont les pastilles qui portent leur
       surface (voir l'avertissement ci-dessus) — donc « bord à bord » veut dire ici
       que sa colonne d'icônes est centrée sur les 60 px de droite, sans retrait. */
    top: 0;
    right: 0;
    /* Comme .page-sidebar en face (§ 47) : le rail est ancré au bas de .page-container,
       que le bandeau applicatif recouvre. Vaut 0 sans bandeau — géométrie inchangée. */
    bottom: var(--app-dock-bar-height);
    /* Au-dessus du contenu, sous l'overlay du viewer 3D (800) : le rail ne doit
       pas surnager par-dessus un viewer plein écran. */
    z-index: 500;
    display: flex;
    /* Aucune gouttière entre le tiroir et le rail : deux surfaces blanches
       séparées par 10 px de contenu se liraient comme un accident. Elles se
       touchent, et c'est le filet du rail qui fait la couture. */
    gap: 0;
    overflow: visible;
}

.kd-tools-rail {
    flex: 0 0 var(--kd-tools-w);
    width: var(--kd-tools-w);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 12px;
    padding: 12px 0;
    background: var(--kd-tools-bg);
    border-left: 1px solid var(--kd-border);
}

/* ESSAI (11/09/2026, les quatre explorateurs) — voir RailNav.Panel2Overlay. Sur un écran dont le
   panneau passe par-dessus la vue, la vue passe AUSSI sous le rail d'outils et sous son tiroir, et
   les deux prennent le voile du panneau de gauche : même transparence, même flou. Sans vue dessous,
   le flou n'aurait rien à flouter — d'où le rembourrage droit de la zone de contenu rendu à la vue.
   Bureau seulement : sous 576 px, le rail est une feuille et ne recouvre rien.

   ⚠️ « Un écran en surimpression », c'est `.content .railnav--overlay` (2D, Patrimoine, Relations)
   ou `.forge-host.open .railnav--overlay` (3D) — et pas `.railnav--overlay` tout court : l'hôte 3D
   reste monté, panneau compris, sur TOUTES les pages une fois ouvert (il est gardé au chaud). Un
   `body:has(.railnav--overlay)` voilerait le rail partout après une visite du 3D. */
@media (min-width: 576px) {
    /* ⚠️ Le préfixe complet n'est pas décoratif : la règle qu'on défait (§ 28) s'écrit
       `.page-container .page-content-wrapper .content.kd-bleed`, à (0,4,0). Réduite à
       `.content.kd-bleed:has(…)` (0,3,0), elle perdait — mesuré : 60 px de rembourrage restés en
       place alors que le voile du rail, dans le même bloc, s'appliquait. */
    .page-container .page-content-wrapper .content.kd-bleed:has(.railnav--overlay) {
        padding-right: 0;
    }

    /* Le 3D n'est pas dans la zone de contenu : son hôte (.forge-host, fixed, monté hors du layout)
       s'arrêtait au rail d'outils parce qu'il passe AU-DESSUS de lui (z-index 700 contre 500) — à
       right: 0, le rail devenait inatteignable (ExplorerBimViewerHost.razor.css). En surimpression,
       l'hôte va au bord ET le cadre du rail monte à 750 : au-dessus de l'hôte, toujours sous
       l'overlay plein écran du viewer (800). `body` en tête : la règle scopée qu'on défait est à
       (0,2,0), et la feuille scopée est chargée après celle-ci. */
    body .forge-host.open:has(.railnav--overlay) {
        right: 0;
    }

    body:has(.forge-host.open .railnav--overlay) .kd-tools-frame {
        z-index: 750;
    }

    /* Le cube de navigation de la maquette, calé à droite par le viewer APS (`right: -1px`), s'écarte
       du rail d'outils — sans quoi il passerait dessous. */
    .forge-host:has(.railnav--overlay) .viewcubeWrapper {
        right: calc(var(--kd-tools-offset) - 1px);
    }

    /* La barre d'outils du viewer APS s'étire sur toute la largeur de la maquette et centre ses
       groupes : sous les panneaux, son premier groupe passait sous le voile (relevé : barre de 136 à
       1165 pour un panneau jusqu'à 426). On la DÉCALE de la moitié de ce qui recouvre la vue (gauche
       moins droite) : ses groupes se centrent alors sur la partie visible. Variables héritées de la
       racine du rail, qui contient le viewer.
       ⚠️ Écarté après mesure : caler ses bords (`left` / `right`). La barre n'a pas rétréci — bord
       gauche bien à 436, mais largeur restée à 1029 px (ni style en ligne ni règle de largeur
       relevés) : son bord droit est parti à 290 px au-delà de la fenêtre, dernier groupe compris. */
    .forge-host .railnav--overlay #guiviewer3d-toolbar {
        transform: translateX(calc((var(--rn-p2) + var(--rn-p3) - var(--rn-right)) / 2));
    }

    body:has(.content .railnav--overlay, .forge-host.open .railnav--overlay) .kd-tools-rail,
    body:has(.content .railnav--overlay, .forge-host.open .railnav--overlay) .kd-tools-panel {
        background: color-mix(in srgb, var(--kd-tools-bg) var(--kd-veil-alpha), transparent);
        backdrop-filter: var(--kd-veil-filter);
        -webkit-backdrop-filter: var(--kd-veil-filter);
    }

    /* LE DRAWER D'APERÇU (asset / espace) prend le même voile que le panneau de gauche (13/09/2026,
       demande de Jérémy). Il était le seul opaque des surfaces qui flottent sur la vue — le gris
       #f4f4f4 du thème DevExpress, à côté d'un panneau blanc translucide.

       Même portée que le rail d'outils ci-dessus, et pas plus : ailleurs (Explorateur d'équipements,
       Centre de surveillance), ce même drawer recouvre une grille de données, et du texte flouté
       sous du texte se lit moins bien qu'un fond plein. Un drawer de SAISIE (layout-drawer-modal)
       reste opaque partout : on n'y lit pas, on y écrit.

       ⚠️ Le voile est posé sur un ::before, PAS sur le panneau — c'est le montage du panneau de
       gauche (RailNav, .railnav-frame::before), et pour la même raison : un backdrop-filter fait de
       l'élément qui le porte le bloc conteneur de tous ses descendants en position: fixed. Posé sur
       le panneau, il y aurait enfermé les fenêtres et listes déroulantes que son contenu ouvre.
       Le z-index: -1 reste DANS le panneau, qui forme son propre contexte d'empilement (z-index 850,
       § 18) : le voile passe sous le contenu du drawer, jamais sous la vue. */
    body:has(.content .railnav--overlay, .forge-host.open .railnav--overlay) .dxbl-drawer.layout-drawer:not(.layout-drawer-modal) > .dxbl-drawer-panel {
        background: transparent;
    }

    body:has(.content .railnav--overlay, .forge-host.open .railnav--overlay) .dxbl-drawer.layout-drawer:not(.layout-drawer-modal) > .dxbl-drawer-panel::before {
        content: "";
        position: absolute;
        inset: 0;
        z-index: -1;
        background: color-mix(in srgb, var(--kd-surface) var(--kd-veil-alpha), transparent);
        backdrop-filter: var(--kd-veil-filter);
        -webkit-backdrop-filter: var(--kd-veil-filter);
        pointer-events: none;
    }
}

/* Chaque outil porte sa propre pastille : sans le cadre blanc derrière, une icône
   nue flotterait sur le fond de page sans dire qu'elle est cliquable. */
/* ⚠️ UN FILET, PLUS UNE OMBRE. La pastille se détachait par une ombre portée du
   temps où elle était posée sur le fond de page. Sur la surface blanche du rail,
   une ombre ne détache rien — le fond est le même des deux côtés — et c'est le
   contour qui dit où finit le bouton. C'est aussi ce que décrit le handoff :
   fond blanc, filet de contrôle, icône sombre. */
.kd-tool {
    position: relative;
    flex: 0 0 auto;
    width: 40px;
    height: 40px;
    border: 1px solid var(--kd-border);
    border-radius: 50%;
    background: var(--kd-surface);
    box-shadow: none;
    color: var(--kd-text);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    text-decoration: none;
    transition: background-color .14s ease, color .14s ease;
}

/* ⚠️ La taille se pose sur l'ICÔNE, pas sur le bouton. `.kd-tool` portait un font-size de 19 px
   qui ne s'est jamais appliqué : la règle globale `label, input, button, select, textarea`
   (§ 44) l'écrasait sur tout <button> — elle s'écrivait alors `12px !important` — et le panier,
   les alarmes et le QR en sont : icônes relevées à 12 px dans une pastille de 40.
   Le verrou est levé depuis le lot C, mais la règle reste juste et on la garde : viser l'<i>
   est plus sûr que de compter sur l'héritage, et cela laisse `.kd-tool` libre de porter un jour
   un libellé à une autre taille.
   Sa propre taille (--kd-tool-icon, tokens.css) et non plus celle du menu depuis le 13/09/2026 :
   seule dans sa pastille, l'icône y paraissait perdue. */
.kd-tool > i {
    font-size: var(--kd-tool-icon);
}

.kd-tool:hover {
    background: var(--kd-page-bg);
    border-color: var(--kd-text-muted);
    color: var(--kd-text-strong);
}

.kd-tool:focus-visible {
    outline: 2px solid var(--kd-primary);
    outline-offset: 2px;
}

/* `.active` vient de NavLink : le lien Réglages se marque tout seul quand on est
   dans les réglages. `.is-open` est l'équivalent pour un outil à panneau. */
.kd-tool.is-open,
.kd-tool.active {
    background: var(--kd-primary);
    border-color: var(--kd-primary-border);
    color: var(--kd-primary-text);
}

/* Masqué au bureau, réaffiché dans la feuille du mobile : un rail d'icônes ne
   montre jamais ses libellés, une liste tactile en a besoin. Un seul markup. */
.kd-tool-label {
    display: none;
}

.kd-tool-badge {
    position: absolute;
    top: 3px;
    right: 3px;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    border-radius: 8px;
    background: var(--kd-primary);
    color: var(--kd-primary-text);
    font-size: 10px;
    font-weight: 700;
    line-height: 16px;
    text-align: center;
    font-variant-numeric: tabular-nums;
    /* Le liseré détache la pastille de l'icône quand elle la chevauche. */
    border: 2px solid var(--kd-surface);
}

.kd-tool.is-open .kd-tool-badge {
    border-color: var(--kd-primary);
}

/* Une alarme n'est pas une quantité neutre : sa pastille est rouge, pas verte. */
.kd-tool-badge--danger {
    background: var(--kd-danger);
    color: #fff;
}

.kd-tool.is-open .kd-tool-badge--danger {
    border-color: var(--kd-primary);
}

/* --- Une alarme dans le panneau ----------------------------------------- */
.kd-tools-alarm {
    display: block;
    padding: 9px 0;
    border-top: 1px solid var(--kd-border-light);
    color: inherit;
    text-decoration: none;
}

.kd-tools-alarm:first-of-type {
    border-top: 0;
}

.kd-tools-alarm:hover {
    background: var(--kd-hover);
}

.kd-tools-alarm-head {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 3px;
}

.kd-tools-alarm-head time {
    margin-left: auto;
    font-size: 10.5px;
    color: var(--kd-text-muted);
    font-variant-numeric: tabular-nums;
}

.kd-tools-alarm b {
    display: block;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--kd-text-strong);
}

.kd-tools-alarm small {
    display: block;
    font-size: 10.5px;
    color: var(--kd-text-muted);
}


/* --- Le panneau, carte à part ------------------------------------------- */
/* Il portait le fond du cadre ; le cadre n'en ayant plus, il porte le sien. Même
   rayon et même ombre que la boîte du menu — il redevient une surface du shell,
   posée à une gouttière du rail (le `gap` du cadre). */
/* ⚠️ UN TIROIR PLEINE HAUTEUR, PLUS UNE CARTE. Il portait son propre rayon du temps
   où le rail était une carte flottante entre deux gouttières : le panneau y était
   une carte de plus, cohérente avec le reste. Le shell étant passé bord à bord, il
   descend du haut au bas de la fenêtre — un arrondi n'y aurait de coins que contre
   les bords de l'écran, et se lirait comme un défaut de calage. L'ombre reste :
   c'est elle qui le détache du contenu qu'il recouvre. */
.kd-tools-panel {
    width: 300px;
    display: flex;
    flex-direction: column;
    min-width: 0;
    min-height: 0;
    background: var(--kd-tools-bg);
    border-radius: 0;
    box-shadow: var(--kd-shell-shadow);
    overflow: hidden;
}

.kd-tools-panel-head {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 11px 12px;
    border-bottom: 1px solid var(--kd-border-light);
    color: var(--kd-text-strong);
}

.kd-tools-panel-title {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .09em;
    text-transform: uppercase;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.kd-tools-panel-clear {
    flex: 0 0 auto;
    border: 0;
    background: none;
    padding: 0;
    font-size: 12px;
    color: var(--kd-danger);
    cursor: pointer;
}

.kd-tools-panel-close {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 26px;
    height: 26px;
    border: 0;
    border-radius: 7px;
    background: none;
    color: var(--kd-text-muted);
    cursor: pointer;
}

.kd-tools-panel-close:hover {
    background: var(--kd-page-bg);
    color: var(--kd-text-strong);
}

.kd-tools-panel-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 8px 12px;
}

.kd-tools-panel-foot {
    flex: 0 0 auto;
    padding: 10px 12px;
    border-top: 1px solid var(--kd-border-light);
}

.kd-tools-empty {
    padding: 18px 4px;
    text-align: center;
    color: var(--kd-text-muted);
    font-size: 13px;
}

.kd-tools-group {
    padding: 10px 0 4px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--kd-text-muted);
}

.kd-tools-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 0;
    border-top: 1px solid var(--kd-border-light);
}

.kd-tools-group + .kd-tools-item {
    border-top: 0;
}

.kd-tools-item-name {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 12.5px;
    color: var(--kd-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.kd-tools-item-value {
    flex: 0 0 auto;
    font-size: 12.5px;
    font-weight: 700;
    color: var(--kd-text-strong);
    font-variant-numeric: tabular-nums;
}

.kd-tools-item-remove {
    flex: 0 0 auto;
    border: 0;
    background: none;
    padding: 0 2px;
    color: var(--kd-text-muted);
    cursor: pointer;
}

.kd-tools-item-remove:hover {
    color: var(--kd-danger);
}

/* --- Bouton flottant et feuille : le régime sous 576 px ------------------ */
/* Le bouton n'existe qu'en mobile, la feuille aussi : au bureau le rail est
   toujours là et n'a besoin ni de l'un ni de l'autre. */
.kd-tools-fab,
.kd-tools-scrim {
    display: none;
}

@media (max-width: 575px) {
    /* Le rail deviendrait 16 % de la largeur d'un écran de 375 px pour trois
       icônes. Il s'efface donc et la carte se replie en feuille, ouverte par un
       bouton qui, lui, ne coûte aucune largeur. */
    /* Ici le cadre EST la feuille, donc il redevient une surface : c'est le seul
       endroit où il porte un fond. Au bureau il ne fait que positionner. */
    .kd-tools-frame {
        display: none;
        top: auto;
        left: 0;
        right: 0;
        /* La feuille monte au-dessus du bandeau applicatif (§ 47) — 0 sans bandeau. */
        bottom: var(--app-dock-bar-height);
        max-height: 76%;
        gap: 0;
        background: var(--kd-tools-bg);
        border-radius: 18px 18px 0 0;
        box-shadow: 0 -8px 28px rgba(15, 18, 26, .22);
        overflow: hidden;
        z-index: 520;
    }

    .kd-tools--sheet-open .kd-tools-frame {
        display: flex;
    }

    /* Un panneau ouvert prend toute la feuille : empiler la liste des outils
       au-dessus de son propre contenu n'apprendrait rien et coûterait la moitié
       de la hauteur utile. */
    .kd-tools-frame:has(.kd-tools-panel) .kd-tools-rail {
        display: none;
    }

    /* Ici le rail n'est plus une surface adossée à un bord : il EST le corps de la
       feuille, qui porte déjà le fond. D'où le filet et le fond qui tombent. */
    .kd-tools-rail {
        flex: 1 1 auto;
        width: 100%;
        align-items: stretch;
        overflow-y: auto;
        gap: 2px;
        padding: 10px 8px;
        background: none;
        border-left: 0;
    }

    /* Des LIGNES et non des pastilles : la feuille est déjà une surface blanche,
       une pastille blanche cerclée par ligne n'y dirait plus rien. */
    .kd-tool {
        width: 100%;
        height: 46px;
        justify-content: flex-start;
        gap: 12px;
        padding: 0 10px;
        border: 0;
        border-radius: 10px;
        background: none;
        box-shadow: none;
    }

    .kd-tool-label {
        display: block;
        font-size: 14px;
        font-weight: 500;
    }

    /* Dans une ligne, la pastille se range à droite du libellé : plus rien à
       chevaucher, donc plus de liseré. */
    .kd-tool-badge {
        position: static;
        margin-left: auto;
        border: 0;
    }


    /* Le panneau remplit la feuille : c'est elle qui porte le rayon et l'ombre. */
    .kd-tools-panel {
        width: 100%;
        border-radius: 0;
        box-shadow: none;
    }

    .kd-tools-fab {
        display: flex;
        position: absolute;
        right: 14px;
        /* Le retrait se compte au-dessus du bandeau applicatif (§ 47) — 14px sans bandeau. */
        bottom: calc(14px + var(--app-dock-bar-height));
        width: 48px;
        height: 48px;
        align-items: center;
        justify-content: center;
        border: 1px solid var(--kd-primary-border);
        border-radius: 15px;
        background: var(--kd-primary);
        color: var(--kd-primary-text);
        z-index: 530;
        box-shadow: 0 6px 18px rgba(15, 18, 26, .3);
    }

    /* Même piège que .kd-tool (voir plus haut) : le font-size du bouton était écrasé à 12 px par
       la règle globale des <button>. Posé sur l'icône, il s'applique enfin. 22 et non
       --kd-nav-icon : c'est le seul accès aux outils sur mobile, dans un bouton plein de 48 px. */
    .kd-tools-fab > i {
        font-size: 22px;
    }

    /* absolute et non fixed : sous 576 px le tiroir du menu POUSSE
       .page-container par un translate3d. Un élément fixed y serait ancré au
       conteneur transformé de toute façon — mais absolute le dit franchement, et
       le bouton part avec l'écran, ce qui est le geste voulu. */
    .kd-tools-scrim {
        display: block;
        position: absolute;
        inset: 0;
        background: rgba(20, 22, 28, .3);
        z-index: 510;
    }
}

/*------------------------------------------------------------------
[47. Bandeaux applicatifs (AppDock) — réservation de place]
*/

/* Le conteneur des bandeaux (AppDock, hors du Router) est en position fixe en bas de fenêtre et passe
   au-dessus de tout le reste (z-index 1050) : il ne pousse rien et recouvre la dernière bande de toute
   boîte ancrée au bas du viewport. Ces boîtes remontent donc leur bord bas via --app-dock-bar-height ;
   les autres ne changent pas.

   Les deux jetons ci-dessous sont ÉCRITS par le cœur — AppDock les publie en style inline sur <html>
   après mesure (cf. _content/KDCockpit.WebComponents/AppDock.js). Un bandeau n'a rien à publier : il
   marque seulement sa barre permanente d'un attribut data-app-dock-bar. Cf. docs/app-dock.md. */

:root {
    /* Hauteur cumulée des barres PERMANENTES des bandeaux ancrés en bas de fenêtre. Seules les barres
       sont réservées : un panneau déroulé est transitoire et voulu par l'utilisateur — réserver sa
       hauteur ferait sauter toutes les pages à chaque ouverture.
       Repli à 0 : sans bandeau déclaré, la mise en page est strictement inchangée. */
    --app-dock-bar-height: 0px;

    /* Hauteur TOTALE des bandeaux, panneaux déroulés compris. Ne concerne que les rares écrans voulant
       suivre le dépliage, et alors en SURPLUS de la barre déjà réservée ci-dessus — sans quoi celle-ci
       serait comptée deux fois. */
    --app-dock-height: 0px;

    /* Hauteur de fenêtre RÉELLEMENT disponible pour le contenu. Toute boîte dimensionnée sur le
       viewport doit passer par ce jeton plutôt que par 100vh : le bandeau est en position fixe, il ne
       pousse rien, et une boîte en 100vh descend sous lui sans que rien ne permette d'aller la voir. */
    --app-viewport-height: calc(100vh - var(--app-dock-bar-height));
}

/* Widget d'accueil maximisé (.card-maximized, § 43 de ce fichier — ex-theme.css : fixed ;
   bottom: 0 ; z-index 600). `bottom` n'y est PAS en !important (contrairement à `position`), et
   cette règle est plus bas dans la feuille : elle l'emporte par l'ordre des sources. */
.card-maximized {
    bottom: var(--app-dock-bar-height);
}

/*------------------------------------------------------------------
[48. SweetAlert — espacement des boutons]
*/

/* Les popups passent `buttonsStyling: false` pour que les boutons prennent l'habillage Bootstrap
   (cf. window.app.sweetAlert dans assets/js/app.js). L'option retire aussi les marges natives de
   SweetAlert : « Confirmer » et « Annuler » se retrouvaient collés l'un à l'autre.
   .swal2-actions est déjà un conteneur flex, un `gap` suffit — et il ne s'applique qu'aux popups. */
.swal2-actions {
    gap: .5rem;
}
