/* =========================================================================
   BACKEND.CSS — feuille pilote du back-office
   -------------------------------------------------------------------------
   Feuille unique de l'interface : jetons, base, mise en page, surcouche
   Bootstrap, SweetAlert2, utilitaires et compatibilité navigateurs.
   Une seule déclaration par couple sélecteur + propriété : aucune règle
   n'en écrase une autre du même fichier.

   Ordre de chargement
     bootstrap.css   → base du framework
     all.min.css     → police d'icônes (Phosphor duotone)
     backend.css     → CE FICHIER : jetons + interface
     tools.css       → composants maison (tableau, éditeur, calendrier à venir)

   Les jetons définis en section 01 (couleurs dérivées, échelle typographique,
   tailles de bouton, rayons) pilotent aussi tools.css : ne pas les déplacer.

   Sommaire
     01. Jetons                couleurs, typographie, tailles, rayons
     02. Base                  reset léger, typographie de fond, animations
     03. Mise en page          navbar, bandeau, menus, barres latérales
     04. Composants Bootstrap  cartes, modales, formulaires, boutons, onglets
     05. Utilitaires           texte, icônes, compatibilité, impression
   ========================================================================= */

/* =========================================================================
   POLICE — Open Sans, servie par le projet (remplace Lato)
   -------------------------------------------------------------------------
   Auparavant un @import vers fonts.googleapis.com. Placé en tête de feuille,
   un @import BLOQUE le rendu de tout le reste tant que le serveur distant n'a
   pas répondu : sur un poste hors ligne ou derrière un DNS lent, la page
   attendait le délai d'expiration à chaque chargement.

   Seules les graisses employées par le projet sont embarquées — 300, 400, 700
   et 900, plus l'italique 400 pour <em>. Les graisses 500 et 600 que Bootstrap
   demande n'existent pas chez Open Sans : le navigateur les approche depuis
   400 et 700, comme il le faisait déjà avec Lato. Open Sans elle-même ne va
   que jusqu'à 800 (ExtraBold) — la graisse 900 embarquée ici EST ce 800,
   sous cette étiquette, pour ne rien changer au reste de la feuille.

   woff2 seul : tous les navigateurs qui font tourner cet ERP le lisent.
   ========================================================================= */

@font-face {
   font-family: 'Open Sans';
   font-style: normal;
   font-weight: 300;
   font-display: swap;
   src: url('../font/opensans-300-normal.woff2') format('woff2');
}

@font-face {
   font-family: 'Open Sans';
   font-style: normal;
   font-weight: 400;
   font-display: swap;
   src: url('../font/opensans-400-normal.woff2') format('woff2');
}

@font-face {
   font-family: 'Open Sans';
   font-style: italic;
   font-weight: 400;
   font-display: swap;
   src: url('../font/opensans-400-italic.woff2') format('woff2');
}

@font-face {
   font-family: 'Open Sans';
   font-style: normal;
   font-weight: 700;
   font-display: swap;
   src: url('../font/opensans-700-normal.woff2') format('woff2');
}

@font-face {
   font-family: 'Open Sans';
   font-style: normal;
   font-weight: 900;
   font-display: swap;
   src: url('../font/opensans-900-normal.woff2') format('woff2');
}

/* Noto Sans Arabic — texte arabe, tout l'ERP. Même raison que Open Sans
   plus haut : auto-hébergée, jamais un @import distant qui bloquerait le
   rendu sur un poste hors ligne ou derrière un DNS lent. Deux graisses
   seulement : 400 (texte courant) et 700 (accent/titre) — les seules
   employées ici. S'applique automatiquement à tout texte marqué dir="rtl"
   ou lang="ar", sans toucher à Open Sans pour le reste de l'interface. */
@font-face {
   font-family: 'Noto Sans Arabic';
   font-style: normal;
   font-weight: 400;
   font-display: swap;
   src: url('../font/noto-sans-arabic-400-normal.woff2') format('woff2');
}

@font-face {
   font-family: 'Noto Sans Arabic';
   font-style: normal;
   font-weight: 700;
   font-display: swap;
   src: url('../font/noto-sans-arabic-700-normal.woff2') format('woff2');
}

[dir="rtl"], [lang="ar"] {
   font-family: 'Noto Sans Arabic', sans-serif;
   font-size: 1.1rem;
}

/* ========================================================================
   01. JETONS — couleurs, typographie, tailles, rayons
   ======================================================================== */

:root {
   --dominante-color: #065d89;
   --dominante-rgb: 6, 93, 137;

   /* Hauteur de la navbar. Elle était recopiée à quatre endroits — la navbar
      elle-même, le haut de la barre latérale et sa hauteur, le haut de
      l'en-tête de page — auxquels s'ajoutent les notifications, qui doivent se
      poser dessous. La changer imposait de retrouver chaque copie. */
   --navbar-h: 60px;

   /* Couleurs d'état : succès, erreur, avertissement. Seul endroit du projet
      où elles sont écrites ; dialogues, validation et Bootstrap y renvoient. */
   --state-success: #249934;
   --state-error: #d64545;
   --state-warning: #e8a33d;

   /* Teintes claires des mêmes états, pour les fonds d'alerte. Écrites en
      dur : ce sont les seules couleurs du projet qui ne dérivent pas de la
      dominante, leurs déclinaisons non plus. */
   --state-success-bg: #e9f6eb;
   --state-success-line: #bfe3c5;
   --state-error-bg: #fbeaea;
   --state-error-line: #f2c4c4;
   --state-warning-bg: #fdf3e5;
   --state-warning-line: #f6ddb5;
   --dominante-color-hover: #0786c5;
   --body-bg-light: #f7f7f7;
   --menu-bg-light: #f2f2f2;
   --menu-bg-dark: #2c2c2c;
   --text-color: #111;
   --transition-speed: 0.4s;
   --font-ui: "Open Sans", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
   --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
   --fs-2xs: 0.75rem;
   --fs-xs: 0.8125rem;
   --fs-sm: 0.875rem;
   --fs-base: 1rem;
   --fs-md: 1.0625rem;
   --fs-lg: 1.25rem;
   --fs-xl: 1.5rem;
   --fs-2xl: 1.875rem;
   --fs-title: 1.375rem;
   --fs-clock: 1.375rem;
   --lh-tight: 1.15;
   --lh-snug: 1.35;
   --lh-normal: 1.55;
   --lh-loose: 1.7;
   --fw-light: 300;
   --fw-regular: 400;
   --fw-bold: 700;
   --fw-black: 900;
   --ls-caps: .05em;
   --ls-title: -.01em;
   --panel: #ffffff;
   --panel-soft: #eef0f4;
   --line: #e1e4ea;
   --slate: #5b6174;
   --slate-dim: #9198aa;
   --radius-panel: 0;
   --radius-field: 5px;
   --btn-h-sm: 28px;
   --btn-h-md: 34px;
   --btn-h-lg: 42px;
   --btn-px-sm: .6rem;
   --btn-px-md: .8rem;
   --btn-px-lg: 1.15rem;
   --btn-fs-sm: var(--fs-xs);
   --btn-fs-md: var(--fs-sm);
   --btn-fs-lg: var(--fs-base);
   --shadow-panel: 0 1px 3px rgba(20, 23, 31, .08);
   --fs-panel-title: 0.97rem;
   --fs-panel-sub: 0.75rem;
}

[data-theme="dark"] {
   /* LE JETON DE TEXTE BASCULE ICI, et c'est le correctif le plus important
      de ce thème. body pose `color: var(--text-color)`, donc TOUT le texte de
      l'application en hérite. Tant que --text-color valait #111 dans les deux
      thèmes, chaque élément qui ne redéfinissait pas sa couleur — options des
      listes déroulantes, paragraphes d'introduction, contenu des onglets —
      s'affichait en noir sur l'anthracite, donc invisible. Les règles sombres
      écrites jusqu'ici ne faisaient que rattraper des cas particuliers.

      Le basculement est sans danger : les cinq usages du jeton sont tous des
      avant-plans posés sur des surfaces qui basculent elles aussi, et aucun
      fond clair du thème sombre ne compte sur un texte foncé hérité. */
   --text-color: #f3f4f7;

   /* Bootstrap a ses PROPRES jetons de couleur, que le projet ne remappe que
      partiellement. --bs-secondary-color vaut rgba(33,37,41,.75) — un gris
      presque noir — et sert de couleur à .form-text, .text-muted, caption et
      .figure-caption. Les blocs d'aide au-dessus des arbres étaient donc
      illisibles, sans qu'aucune règle du projet soit en cause.

      La valeur reprise est celle du thème sombre de Bootstrap lui-même. On ne
      touche PAS à --bs-body-color : les champs de saisie gardent le fond blanc
      de Bootstrap en mode sombre, et un texte clair y deviendrait invisible. */
   --bs-secondary-color: rgba(222, 226, 230, .75);
   --bs-secondary-color-rgb: 222, 226, 230;

   --panel: #1a1e29;
   --panel-soft: #20242f;
   --line: #2b303d;
   --slate: #8a91a6;
   --slate-dim: #5b6174;
   --shadow-panel: 0 1px 2px rgba(0, 0, 0, .2);
}

@supports (font-size:clamp(1rem,1vw,2rem)) {
   :root {
      --fs-title: clamp(1.375rem, 1.15rem + 1.05vw, 1.875rem);
      --fs-clock: clamp(1.375rem, 1.15rem + 1.05vw, 1.75rem);
   }

}

@keyframes spin {
   0% {
      transform: rotate(0deg);
   }

   100% {
      transform: rotate(360deg);
   }
}

/* ========================================================================
   02. BASE — reset léger, typographie de fond, animations
   ======================================================================== */

hr {
   border: none;
   height: 1px;
   width: 100%;
   margin: 1rem auto;
   background: var(--dominante-color);
}

html {
   font-size: 100%;
   -webkit-text-size-adjust: 100%;
   text-size-adjust: 100%;
}

body {
   background: var(--body-bg-light);
   color: var(--text-color);
   transition: background var(--transition-speed), color var(--transition-speed);
   margin-bottom: 100px;
   font-family: var(--font-ui);
   font-size: var(--fs-base);
   font-weight: var(--fw-regular);
   line-height: var(--lh-normal);
   -webkit-font-smoothing: antialiased;
   -moz-osx-font-smoothing: grayscale;
}

h1,
h2,
h3,
h4,
h5,
h6,
.h1,
.h2,
.h3,
.h4,
.h5,
.h6 {
   margin-top: 0;
   margin-bottom: .5em;
   font-family: var(--font-ui);
   font-weight: var(--fw-bold);
   line-height: var(--lh-tight);
   letter-spacing: var(--ls-title);
}

h1,
.h1 {
   font-size: var(--fs-title);
   font-weight: var(--fw-black);
}

h2,
.h2 {
   font-size: var(--fs-xl);
}

h3,
.h3 {
   font-size: var(--fs-lg);
}

h4,
.h4 {
   font-size: var(--fs-md);
}

h5,
.h5,
h6,
.h6 {
   font-size: var(--fs-base);
   letter-spacing: 0;
}

h6,
.h6 {
   text-transform: uppercase;
   letter-spacing: var(--ls-caps);
   color: var(--dc-50-777777, #3f6a80);
}

p {
   margin-bottom: .75rem;
   line-height: var(--lh-normal);
}

strong,
b {
   font-weight: var(--fw-bold);
}

code,
kbd,
pre,
samp {
   font-family: var(--font-mono);
   font-size: .9375em;
}

@media (max-width:767.98px) {
   body {
      line-height: var(--lh-snug);
   }

}

@media print {
   body {
      font-size: 11pt;
      line-height: 1.4;
      /* Le fond du body (--body-bg-light, gris très clair) n'était
         jamais annulé à l'impression — universel : toute page imprimée
         du projet en souffrait. Blanc pur à l'impression, quelle que
         soit la page. */
      background: #fff;
   }

}

@media (min-width:1400px) {
   body {
      line-height: var(--lh-loose);
   }

}

/* ========================================================================
   03. MISE EN PAGE — navbar, bandeau, menus, barres latérales, chargement
   ======================================================================== */

.grady-light {
   background: linear-gradient(#fff, #ddd) !important;
}

[data-theme="dark"] .grady-light {
   background: linear-gradient(var(--dc-60-333333, #184c67), var(--dc-60-k, #043852)) !important;
}

.page-header {
   position: sticky;
   top: var(--navbar-h);
   z-index: 5;
   border-left: 10px solid var(--dc-50-333333, #1d485e);
   border-right: 10px solid var(--dc-50-333333, #1d485e);
   border-bottom: 2px solid var(--dc-50-w, #83aec4);
   box-shadow: 0 3px 5px rgba(0, 0, 0, 0.185);
   color: var(--dc-80-k, #054a6e);
   display: flex;
   justify-content: space-between;
   align-items: center;
   flex-wrap: wrap;
   gap: .5rem;
   margin-bottom: 1.7rem;
}

[data-theme="dark"] .page-header {
}

[data-theme="dark"] .page-header .page-title,
[data-theme="dark"] .page-header .date-time,
[data-theme="dark"] #current-time {
}

#scrollTopBtn {
   position: fixed;
   bottom: 25px;
   right: 25px;
   width: 43px;
   height: 43px;
   border: none;
   border-radius: 50%;
   background: var(--dominante-color);
   color: #fff;
   font-size: 1.2rem;
   display: none;
   align-items: center;
   justify-content: center;
   box-shadow: 0 4px 8px rgba(0, 0, 0, 0.521);
   transition: transform 0.3s ease;
   z-index: 1055;
   filter: opacity(.7);
}

#scrollTopBtn:hover {
   background: var(--dc-80-k, #054a6e);
   transform: scale(1.1);
   filter: opacity(1);
}

[data-theme="dark"] #scrollTopBtn {
   background: var(--dc-30-w, #b4cedc);
   color: var(--dominante-color);
   filter: opacity(1);
}

[data-theme="dark"] #scrollTopBtn:hover {
   background: var(--dc-80-k, #054a6e);
   color: #fff;
}

.navbar {
   position: sticky;
   top: 0;
   background: var(--dc-95-k, #065882);
   z-index: 1030;
   box-shadow: 0 4px 8px rgba(0, 0, 0, .6);
   transition: background var(--transition-speed);
   height: var(--navbar-h);
}

.btn-square,
.search-group .btn,
#dropdownColor {
   background: rgba(255, 255, 255, .15);
   color: #fff;
}

.btn-square:hover,
.search-group .btn:hover,
#dropdownColor:hover {
   transform: translateY(-2px) scale(1.02);
   color: #222;
   background-color: var(--dc-50-w, #83aec4);
   box-shadow: 0 0 5px 0 #000;
}

.btn-square .ph-duotone,
.darkmode-btn .ph-duotone,
.menu-link .ph-duotone,
.menu-link .triangle {
   transition: color .3s;
}

#selectSkin {
   width: 90px;
   background-color: var(--dc-10-w, #e6eff3);
}

[data-theme="dark"] #selectSkin {
   background: var(--dc-20-3a3a3a, #30414a);
   border-color: #777;
}

.search-group {
   display: flex;
   align-items: center;
   /* Hauteur fixée EXPLICITEMENT : sans elle, un parent flex plus haut (la
      navbar fait 60px, ce groupe 42px comme ses enfants) l'étire par
      défaut (align-items: stretch, jamais annoncé nulle part mais bien la
      valeur initiale de tout conteneur flex) — l'icône et le bouton, eux,
      restent à 42px, laissant un vide visible au-dessus et en-dessous. */
   height: var(--btn-h-lg);
   flex-shrink: 0;
   margin-left: 1rem;
   /* Un contour, sans quoi le champ ne se distingue pas du fond qui le porte :
      blanc sur le panneau clair du menu, l'écart n'est que de 1,12 : 1. Le
      contour, lui, tient au-dessus de 5,9 : 1 sur toutes les dominantes.

      overflow masqué : les extrémités du groupe portent leurs propres arrondis,
      qui déborderaient du contour sans cela. */
   border: 1px solid var(--dc-80-k, #0a2a3c);
   border-radius: var(--radius-field);
   overflow: hidden;
}

/* Le focus du champ est reporté sur le GROUPE. overflow: hidden découpe le halo
   que .form-control:focus dessine autour de lui : il resterait invisible, et le
   champ ne montrerait plus qu'il a la main. Porté par le conteneur, le halo se
   déploie à l'extérieur du découpage. */
.search-group:focus-within {
   border-color: var(--dominante-color);
   box-shadow: 0 0 5px 2px var(--dc-30-t, rgba(6, 93, 137, .3));
}

.search-group .form-control:focus {
   box-shadow: none;
}

/* Affixe d'icône, comme sur les champs de formulaire */
.search-group .input-group-text {
   /* Même carré que le bouton de soumission, à l'autre bout du groupe.
      `flex: 0 0` : sur un écran étroit, un élément flexible se comprime,
      les deux extrémités doivent garder leur taille. */
   flex: 0 0 var(--btn-h-lg);
   width: var(--btn-h-lg);
   height: var(--btn-h-lg);
   padding: 0;
   justify-content: center;
   border: none;
   /* Aucun arrondi sur les enfants : c'est le conteneur .search-group qui
      arrondit et découpe (overflow masqué). Deux rayons pour un même coin ne
      peuvent pas coïncider — le bord intérieur du conteneur est décalé de
      l'épaisseur de sa bordure, et l'écart se voit. */
   border-radius: 0;
   background: var(--dc-25-w, #b6d0dd);
   /* --dc-20-k et non --dc-90-k : ce dernier reste très proche de la dominante
      et ne tenait que 3,58 : 1 sur son propre affixe avec une couleur claire
      (cyan). Celui-ci garde un soupçon de teinte tout en restant lisible —
      12,4 : 1 au pire. */
   color: var(--dc-20-k, #01131b);
}

.search-group .form-control::placeholder {
   color: rgba(17, 17, 17, .65);
}

select.sessionSelect,
#mobile-search-select select.sessionSelect {
   margin: 0 1rem;
   height: 42px;
   min-width: 130px;
   max-width: 200px;
   border-radius: 5px;
   border: none;
   font-size: 1.3rem;
   text-align: center;
   padding: .4rem .9rem;
   background: var(--dc-10-w, #e6eff3);
   color: var(--text-color);
   transition: background var(--transition-speed), color var(--transition-speed);
}

[data-theme="dark"] .sessionSelect,
[data-theme="dark"] #mobile-search-select .sessionSelect {
   background: var(--dc-20-3c3c3c, #31434b);
   color: #fff;
}

[data-theme="dark"] .search-group .form-control {
   background: var(--dc-15-3c3c3c, #344148);
   color: #fff;
}

[data-theme="dark"] .search-group .form-control::placeholder {
   color: rgba(255, 255, 255, .65);
}

#dropdown-menu {
   position: fixed;
   top: var(--navbar-h);
   left: 0;
   width: 280px;
   height: calc(100vh - var(--navbar-h));
   background: var(--menu-bg-light);
   overflow-y: auto;
   transform: translateX(-100%);
   transition: transform .4s ease, opacity .4s ease;
   z-index: 1025;
   display: flex;
   flex-direction: column;
   box-shadow: 3px 0 8px rgba(0, 0, 0, .2);
}

#dropdown-menu.show {
   transform: translateX(0);
}

.menu-items {
   flex: 1;
}

.menu-header {
   display: flex;
   flex-direction: column;
   align-items: center;
   padding: .7rem;
   border-bottom: 1px solid var(--dc-45-w, #8fb6ca);
   box-shadow: 0 2px 6px rgba(0, 0, 0, .1);
}

.menu-header .menu-main {
   display: flex;
   align-items: center;
   width: 100%;
}

.menu-header img.avatar {
   width: 60px;
   height: 60px;
   border-radius: 50%;
   margin-right: 1rem;
   flex-shrink: 0;
   border: 1px solid #c2c2c2;
   padding: 3px;
   background: radial-gradient(#ffffff, #d1cfcf);
}

.menu-header .user-info {
   display: flex;
   flex-direction: column;
   flex: 1;
}

[data-theme="dark"] .menu-header .user-info {
   color: #fff;
}

.menu-header .user-info div {
   margin-bottom: .2rem;
   color: inherit;
}

/* Bouton "Mon profil", positionné en bas à droite de .menu-main
   (nav-menu.php) — lien nu, sans fond de bouton, juste une icône qui
   réagit au survol. */
.mc-profil-lien {
   color: var(--bs-secondary);
   font-size: 2rem;
   line-height: 1;
   transition: color .15s ease, transform .15s ease;
}

.mc-profil-lien:hover {
   color: var(--dominante-color);
   transform: scale(1.1);
}

[data-theme="dark"] #dropdown-menu {
   background: var(--dc-10-2c2c2c, #283135);
}

[data-theme="dark"] .menu-header {
   background: var(--dc-30-1c1c1c, #15303d);
}

[data-theme="dark"] .menu-header .menu-title {
   color: #fff;
}

#mobile-search-select {
   display: none;
   flex-direction: column;
   gap: 0;
   width: 100%;
}

#mobile-search-select select.sessionSelect,
#mobile-search-select .search-group {
   width: 100%;
   margin: 0;
   box-sizing: border-box;
}

#mobile-search-select .search-group {
   display: flex;
}

/* La règle partagée (background: rgba(255,255,255,.15)) se lit foncée sur
   le fond sombre de la navbar, mais claire sur le panneau clair du menu
   mobile — même règle, deux résultats opposés. Un fond opaque assure ici
   le même résultat visuel foncé, indépendamment du fond sous-jacent. */
#mobile-search-select .search-group .btn {
   background: var(--dominante-color);
   color: #fff;
}

#mobile-search-select .search-group .btn:hover {
   background-color: var(--dc-80-k, #054a6e);
   color: #fff;
}

.menu-link::before {
   content: "";
   position: absolute;
   left: 0;
   top: 0;
   bottom: 0;
   width: 0;
   background: none;
   transition: all .35s ease;
   z-index: 0;
}

.menu-link:hover::before {
   width: 9px;
   background: var(--dominante-color);
   clip-path: polygon(0 0, 0 100%, 100% 50%);
}

.menu-link:hover>i {
   transition: all .6s ease;
   transform: rotateX(360deg);
   margin-left: 15px;
}

.menu-link:hover .ph-duotone,
.menu-link:hover .triangle {
   color: var(--dominante-color);
}

.menu-link[aria-expanded="true"] .triangle {
   transform: rotate(180deg);
   color: var(--dc-60-w, #6a9eb8);
}

.sub-menu {
   display: flex;
   flex-direction: column;
   padding-left: 1rem;
   overflow: hidden;
   max-height: 0;
   opacity: 0;
   transition: max-height .6s ease, opacity .6s ease;
}

.sub-menu.open {
   /* 1200px et non 500 (abdAllah, 25/08) : `Profils` porte neuf entrées PLUS
      le sous-menu « Traitements » déplié — 570px au total. À 500, le menu
      était rogné et ses dernières entrées écrasées.
      La valeur ne sert qu'à animer l'ouverture : elle doit dépasser le contenu
      le plus long, sans quoi celui-ci est coupé. */
   max-height: 1200px;
   opacity: 1;
}

[data-theme="dark"] .menu-link[aria-expanded="true"] .triangle {
   color: var(--dc-90-w, #1f6d95);
}

[data-theme="dark"] .menu-link {
   color: #ccc;
   border-left-color: var(--dc-80-w, #387da1);
}

[data-theme="dark"] .menu-link .ph-duotone,
[data-theme="dark"] .menu-link .triangle {
   color: #ccc;
}

[data-theme="dark"] .menu-link:hover {
   background: var(--dc-25-k, #021722);
   color: var(--dc-40-w, #9bbed0);
   border-left-color: var(--dc-30-w, #b4cedc);
}

[data-theme="dark"] .menu-link:hover .ph-duotone,
[data-theme="dark"] .menu-link:hover .triangle {
   color: var(--dc-40-w, #9bbed0);
}

.menu-logo-etablissement {
   padding: 1rem 0;
   text-align: center;
   background: var(--panel, #fff);
}

.menu-logo-etablissement img {
   max-width: 60%;
   max-height: 90px;
}

.menu-footer {
   padding: 1.5rem 0;
   text-align: center;
   background: linear-gradient(var(--dc-10-w, #e6eff3), var(--dc-50-w, #83aec4));
   margin: 5px auto 100px;
   box-shadow: 0 5px 10px #a3a3a3;
}

.menu-footer img {
   height: 55px;
}

[data-theme="dark"] .menu-footer {
   background: linear-gradient(var(--dc-30-5f5f5f, #445e6c), var(--dc-30-1c1c1c, #15303d));
   box-shadow: 0 3px 10px #494949;
}

[data-theme="dark"] .menu-footer img {
   filter: invert(95%);
}

#horizontal-sidebar.show {
   transform: translateY(0);
   opacity: 1;
   pointer-events: auto;
}

#horizontal-sidebar.d-none {
   display: none;
}

.sidebar-link:hover {
   background: var(--dc-70-w, #518eac);
   color: #fff;
   transform: translateY(-2px) scale(1.05);
   border-color: #fff;
}

[data-theme="dark"] #horizontal-sidebar {
   background: var(--dc-30-1e1e1e, #17313e);
}

[data-theme="dark"] .sidebar-link {
   color: #ccc;
   background: var(--dc-10-1e1e1e, #1c2429);
   border-color: var(--dc-70-w, #518eac);
}

[data-theme="dark"] .sidebar-link:hover {
   background: var(--dominante-color);
   color: #fff;
   border-color: #fff;
}

.dropdown-menu {
   border-color: var(--dominante-color) !important;
   color: var(--dominante-color) !important;
   background: var(--dc-7-w, #eef4f7) !important;
   box-shadow: 0 0 5px 2px var(--dc-30-t, rgba(6, 93, 137, 0.3)) !important;
   transition: all 0.25s ease;
}

[data-theme="dark"] .dropdown-menu {
   background-color: #444 !important;
   border-color: var(--dc-40-w, #9bbed0) !important;
   box-shadow: 0 0 0.3rem #aaa !important;
}

#pageLoader {
   position: fixed;
   inset: 0;
   background: var(--bs-body-bg, #fff);
   display: flex;
   justify-content: center;
   align-items: center;
   z-index: 2000;
   transition: opacity 0.4s ease, visibility 0.4s ease;
}

#pageLoader.hidden {
   opacity: 0;
   visibility: hidden;
}

[data-theme="dark"] #pageLoader {
   background: #121212;
}

.loader-content {
   text-align: center;
   display: flex;
   flex-direction: column;
   align-items: center;
}

.spinner-wrapper {
   position: relative;
   width: 80px;
   height: 80px;
   display: flex;
   justify-content: center;
   align-items: center;
}

.loader-img {
   width: 42px;
   position: absolute;
   z-index: 3;
   transition: filter 0.3s ease;
}

[data-theme="dark"] .loader-img {
   filter: brightness(2) saturate(0) contrast(1.2);
}

.spinner-ring {
   position: absolute;
   width: 80px;
   height: 80px;
   border-radius: 50%;
   border: 0.19rem solid var(--dc-20-w, #cddfe7);
   z-index: 1;
}

.spinner-arc {
   position: absolute;
   width: 80px;
   height: 80px;
   border-radius: 50%;
   border: 0.17rem solid transparent;
   border-top-color: var(--dominante-color);
   z-index: 2;
   animation: spin 1s linear infinite;
}

[data-theme="dark"] .loader-text {
   color: var(--dc-30-w, #b4cedc);
}

.page-header,
.navbar,
table.mc-table thead th,
.mc-editor-toolbar {
   position: sticky;
}

#pageLoader,
.mc-loading,
.mc-dialog-backdrop,
.mc-editor.mc-fullscreen {
   top: 0;
   right: 0;
   bottom: 0;
   left: 0;
}

.mc-table-scroll,
.mc-editor-content,
.mc-menu,
#dropdown-menu,
#horizontal-sidebar {
   -webkit-overflow-scrolling: touch;
}

.mc-menu-holder {
   position: relative;
}

.mc-menu-end {
   left: auto;
   right: 0;
}

.mc-menu-item:hover,
.mc-menu-item:focus-visible {
   background: var(--mc-surface-alt);
   color: var(--mc-accent);
   border-left-color: var(--mc-accent);
   border-right-color: var(--mc-accent);
}

/* La case de coche ne réserve sa place que là où il y a quelque chose à
   cocher. Le menu des exports n'a pas d'état — seul celui des colonnes marque
   aria-checked — et son gabarit vide décalait les icônes vers la droite sans
   rien annoncer. */
.mc-menu-item:not([aria-checked]) .mc-check {
   display: none;
}

.mc-menu-item .mc-check {
   flex: 0 0 auto;
   width: 1rem;
   text-align: center;
   color: var(--mc-accent);

   /* En gras : c'est un simple caractère ✓, et à la graisse du libellé il se
      distinguait mal de la ponctuation environnante. */
   font-weight: var(--fw-bold);

   visibility: hidden;
}

.mc-menu-item[aria-checked="true"] .mc-check {
   visibility: visible;
}

.mc-spinner {
   width: 34px;
   height: 34px;
   border: 3px solid var(--mc-border);
   border-top-color: var(--mc-accent);
   border-radius: 50%;
   -webkit-animation: mc-spin .8s linear infinite;
   animation: mc-spin .8s linear infinite;
}

#current-time,
.mc-info,
.mc-page-btn,
.mc-editor-count,
.mc-textarea-counter,
td[data-label="Montant"] {
   font-variant-numeric: tabular-nums;
   -moz-font-feature-settings: "tnum";
   -webkit-font-feature-settings: "tnum";
   font-feature-settings: "tnum";
}

.navbar .btn-square {
   font-size: var(--fs-base);
}

.menu-header .menu-title {
   font-size: var(--fs-lg);
   font-weight: var(--fw-black);
   color: var(--dominante-color);
   margin: 1rem 0 .5rem;
   text-align: center;
   line-height: var(--lh-tight);
   letter-spacing: var(--ls-caps);
   text-transform: uppercase;
}

.menu-main .user-info {
   line-height: var(--lh-snug);
}

#user-firstname,
#user-lastname {
   font-size: var(--fs-base);
}

#user-id {
   font-weight: var(--fw-bold);
}

.menu-link {
   position: relative;
   display: flex;
   align-items: center;
   width: 100%;
   padding: .75rem 1rem;
   /* --dc-80-k et non --dc-60-w : ce dernier ne tenait que 2,2 à 3,0 : 1 sur le
      panneau #f2f2f2 selon la dominante — très en dessous du seuil de 4,5 : 1
      exigé pour du texte, et d'autant plus bas que la couleur est claire.
      La teinte reste celle de la charte, seule sa profondeur change. */
   color: var(--dc-80-k, #0a2a3c);
   text-decoration: none;
   border-bottom: 1px solid rgba(0, 0, 0, .1);
   border-left: 7px solid var(--dc-40-w, #9bbed0);
   transition: background .3s, border-left-color .3s, color .3s, transform .25s;
   overflow: hidden;
   font-size: var(--fs-base);
   line-height: var(--lh-snug);
}

/* Compteur à droite d'un lien de menu (nav-menu.php) — la couleur dominante
   de la charte, pas le gris générique Bootstrap. ms-auto pousse le badge à
   droite dans le .menu-link déjà flex. */
.mc-menu-compteur {
   margin-left: auto;
   min-width: 1.2rem;
   padding: .1rem .4rem;
   border-radius: 999px;
   background: var(--dominante-color, #065d89);
   color: #fff;
   font-size: .7rem;
   font-weight: var(--fw-bold);
   line-height: 1.25;
   text-align: center;
}

.sub-menu .menu-link {
   font-size: var(--fs-sm);
}

.menu-link:hover {
   background: rgba(0, 0, 0, .05);
   color: var(--dominante-color);
   font-weight: var(--fw-bold);
   border-left-color: var(--dominante-color);
}

.menu-link .triangle {
   margin-left: auto;
   transition: transform .3s, color .3s;
   font-size: var(--fs-2xs);
}

.loader-text {
   margin-top: 1rem;
   font-size: var(--fs-md);
   font-weight: var(--fw-bold);
   color: var(--dominante-color);
   letter-spacing: var(--ls-caps);
}

.mc-btn,
.mc-menu-item,
.mc-page-btn,
.mc-select,
.mc-search input {
   font-size: var(--fs-sm);
}

.btn .ph-duotone+span,
.menu-link .ph-duotone,
.mc-menu-item .ph-duotone {
   margin-right: 0;
}

/* Les icônes suivaient la taille du texte (.85rem) : trop petites pour se lire
   d'un coup d'œil dans une liste où elles distinguent les formats. */
.mc-menu-item .ph-duotone {
   font-size: 1.15rem;
   flex: 0 0 auto;

   /* Les icônes suivent la dominante. Phosphor dessine ses duotone en DEUX
      couches — un fond à 20 % d'opacité et un tracé plein — qui héritent
      toutes deux de color : une seule déclaration les tient donc ensemble, et
      le dégradé propre à la charte se fait tout seul. */
   color: var(--dominante-color);
}

/* Icônes de navigation : une seule déclaration pour les deux barres */
.menu-link .ph-duotone,
.sidebar-link .ph-duotone {
   font-size: 1.4rem;
}

.page-header {
   padding: .9rem 1.25rem;
   border: 0;
   border-bottom: 1px solid var(--line);
   background: var(--panel);
   box-shadow: none;
   color: inherit;
   margin-bottom: 1.25rem;
   align-items: flex-end;
}

/* Icône à gauche, titre et sous-titre empilés à droite. */
.page-title-group {
   display: flex;
   align-items: center;
   gap: .75rem;
}

.page-title-icone {
   flex: 0 0 auto;
   font-size: 2.5rem;
   color: var(--dominante-color);
}

.page-header .page-title {
   font-size: 1.4375rem;
   font-weight: var(--fw-bold);
   color: inherit;
   line-height: var(--lh-tight);
   letter-spacing: -.015em;
}

/* Le libellé du dépliant qui contient la page — un onglet virtuel s'il y en a
   un, sinon le module lui-même. Plus discret que le titre : c'est un repère
   de position, pas une information nouvelle. */
.page-header .page-subtitle {
   font-size: .8rem;
   color: var(--slate);
   line-height: var(--lh-tight);
   margin-top: .1rem;
}

.page-header .date-time {
   color: var(--slate);
   font-size: .84rem;
   line-height: 1.3;
   text-transform: capitalize;
}

#current-time {
   font-size: 1.25rem;
   color: inherit;
   margin-top: .1rem;
   font-weight: var(--fw-regular);
   line-height: var(--lh-tight);
   font-family: var(--font-mono);
   letter-spacing: -.03em;
}

/* Icônes de bouton */
.btn .ph-duotone,
.mc-btn .ph-duotone {
   font-size: 1.2rem;
}

/* Les icônes des boutons d'outils — Colonnes, Exporter — suivent la dominante.
   Elles héritaient de --mc-text, la couleur du libellé, et restaient donc
   grises quelle que soit la charte.

   Ce sélecteur ne vise QUE .mc-btn, employé par le seul mc-table.js pour la
   barre d'outils du tableau : ces boutons ont un fond clair. On ne touche pas
   à .btn, dont les variantes pleines portent des icônes sur aplat coloré, où
   la dominante les rendrait illisibles. */
.mc-btn .ph-duotone {
   color: var(--dominante-color);
}

/* Bascule de thème : elle n'est pas un .btn, sa taille se déclare à part */
.darkmode-btn .ph-duotone {
   font-size: 1.3rem;
}

/* box-sizing/min-height/max-height en plus de height, ici et sur le bouton
   ci-dessous : le champ et le bouton se lisaient à des hauteurs visuelle-
   ment différentes malgré la même height:var(--btn-h-lg) déjà posée sur les
   deux — verrouillé explicitement pour ne plus dépendre de la hauteur
   intrinsèque propre à chaque type d'élément (<input> vs <button>). */
.search-group .form-control {
   flex: 1 1 auto;
   min-width: 0;
   box-sizing: border-box;
   height: var(--btn-h-lg);
   min-height: var(--btn-h-lg);
   max-height: var(--btn-h-lg);
   border-radius: 0;
   border: none;
   padding: .4rem .9rem;
   /* Un aplat blanc, non teinté. La version précédente prenait --dc-10-w, qui
      ne se détachait du panneau du menu que de 1,02 : 1 — le champ disparaissait
      purement et simplement. Le blanc tient quelle que soit la dominante, sur le
      panneau clair du menu comme sur la navbar sombre. */
   background: var(--panel, #fff);
   color: var(--text-color);
   transition: background var(--transition-speed), color var(--transition-speed);
}

#horizontal-sidebar {
   position: fixed;
   top: var(--navbar-h);
   left: 0;
   width: 100%;
   background: var(--dc-20-w, #cddfe7);
   overflow: hidden;
   z-index: 1000;
   box-shadow: 0 4px 8px rgba(0, 0, 0, 0.425);
   padding: 0.2rem 0;
   white-space: nowrap;
   border-bottom: 1px solid var(--dc-50-w, #83aec4);
   transform: translateY(-20px);
   opacity: 0;
   pointer-events: none;
   transition: transform 0.4s ease, opacity 0.4s ease;
   max-height: 84px;
}

#horizontal-sidebar .sidebar-items {
   display: flex;
   /* nowrap + overflow-x : le nombre de raccourcis n'est pas plafonné, la
      barre défile. Elle portait overflow-Y, ce qui ne défilait rien sur une
      rangée horizontale et coupait les tuiles au-delà de la largeur. */
   flex-wrap: nowrap;
   gap: 0.25rem;
   padding: .4rem .75rem;
   overflow-x: auto;
   overflow-y: hidden;
   scrollbar-width: thin;
   scrollbar-color: var(--dc-40-w, #9bbed0) transparent;
}

/* Séparateur : un simple trait vertical entre deux groupes de raccourcis.
   flex-shrink: 0 est nécessaire — sans lui, il serait le premier écrasé quand
   la barre se remplit, et disparaîtrait au moment où il sert le plus. */
.sidebar-separateur {
   flex: 0 0 auto;
   align-self: stretch;
   width: 1px;
   margin: .35rem .3rem;
   background: currentColor;
   opacity: .35;
}

/* Le bouton de réglage n'est pas un raccourci : il ne mène pas à un contenu,
   il mène à la page qui compose cette barre. Fond plein de la dominante, là où
   les raccourcis sont des tuiles claires — la différence doit se voir avant
   d'avoir lu le libellé.
   Il reste en tête et ne défile pas : c'est la seule issue quand la barre est
   vide, et une barre neuve EST vide.

   Le sélecteur est préfixé par #horizontal-sidebar À DESSEIN. Sans l'identifiant,
   .sidebar-link-fixe et .sidebar-link ont la même spécificité, et .sidebar-link
   est déclarée plus bas : c'est elle qui l'emportait, le bouton se confondait
   avec les autres et cette règle ne servait à rien. */
#horizontal-sidebar .sidebar-link-fixe {
   position: sticky;
   left: 0;
   z-index: 1;
   border-color: var(--dominante-color, #065d89);
   /* Fond OPAQUE obligatoire : c'est le seul élément sous lequel les autres
      tuiles défilent. Une transparence les laisserait apparaître au travers. */
   background: var(--dominante-color, #065d89);
   color: #fff;
   /* L'ombre à droite donne à voir que quelque chose passe dessous. */
   box-shadow: 6px 0 6px -4px rgba(0, 0, 0, .25);
}

#horizontal-sidebar .sidebar-link-fixe:hover {
   border-color: var(--dc-80-k, #022537);
   background: var(--dc-80-k, #022537);
   color: #fff;
   /* Les tuiles se soulèvent au survol ; un bouton ancré, non — il glisserait
      sous celles qui défilent. */
   transform: none;
}

.sidebar-link {
   display: inline-flex;
   flex-direction: column;
   align-items: center;
   justify-content: center;
   /* Largeur au contenu : un libellé long (« Notifications », « Déconnexion »)
      débordait de la tuile à largeur fixe. */
   min-width: 62px;
   height: 55px;
   text-align: center;
   padding: .3rem .55rem;
   border-radius: 5px;
   border: 1px solid var(--dc-70-w, #518eac);
   white-space: nowrap;
   background: var(--dc-10-w, #e6eff3);
   color: var(--dc-70-k, #044160);
   text-decoration: none;
   transition: all 0.25s ease;
   font-size: var(--fs-2xs);
   box-sizing: border-box;
   flex-shrink: 0;
   line-height: var(--lh-snug);
}

.sidebar-link .ph-duotone {
   margin-bottom: .1rem;
   transition: color 0.25s ease;
}

.menu-link .ph-duotone {
   margin-right: 10px;
   color: var(--dc-60-w, #6a9eb8);
}

.btn-square,
.darkmode-btn,
.search-group .btn,
#dropdownColor {
   width: var(--btn-h-lg);
   height: var(--btn-h-lg);
   border-radius: var(--radius-field);
   border: none;
   display: flex;
   align-items: center;
   justify-content: center;
   transition: all .25s ease;
   padding: 0;
   min-height: var(--btn-h-lg);
}

/* #dropdownColor un peu plus large que les 3 autres boutons carrés avec
   qui il partage la règle ci-dessus — hauteur inchangée, largeur seule. */
#dropdownColor {
   width: calc(var(--btn-h-lg) + 8px);
}
/* Après la règle ci-dessus, de même spécificité : dans le groupe de recherche,
   le bouton ferme le côté droit. Il n'arrondit rien lui-même — voir l'affixe
   plus haut, c'est le conteneur qui s'en charge. */
.search-group .btn {
   flex: 0 0 var(--btn-h-lg);
   border-radius: 0;
   box-sizing: border-box;
   height: 100vh;
}

[data-theme="dark"] .card-header p {
   color: var(--slate);
}

[data-theme="dark"] .page-header {
   background: var(--panel);
   border-bottom: 1px solid var(--line);
   color: #f3f4f7;
}

[data-theme="dark"] .page-header .page-title,
[data-theme="dark"] #current-time {
   color: #f3f4f7;
}

.mc-menu-item {
   display: flex;
   align-items: center;
   gap: .5rem;
   width: 100%;
   padding: .45rem .6rem;

   /* Les bords latéraux sont RÉSERVÉS dès maintenant, en transparent : ajoutés
      seulement au survol, ils élargiraient l'élément de 8px et feraient sauter
      le libellé au passage de la souris. */
   border: 0 solid transparent;
   border-left-width: 4px;
   border-right-width: 4px;
   border-radius: 7px;
   background: none;
   color: var(--mc-text);
   font-size: .85rem;
   text-align: left;
   cursor: pointer;
}

@media (max-width:768px) {
   .page-header {
      /* Le bandeau reste sur une rangée : titre à gauche, date et heure en
         haut à droite. Le titre passe à la ligne si besoin, la date non. */
      flex-direction: row;
      /* Le bandeau autorise le retour à la ligne (`flex-wrap: wrap` en base) :
         sur écran étroit, le titre et la date ne tenaient pas côte à côte et
         la date basculait sous le titre. */
      flex-wrap: nowrap;
      align-items: flex-start;
      justify-content: space-between;
      gap: .75rem;
      text-align: left;
      padding: .8rem 1rem;
   }

   /* C'est CE conteneur, pas le <h1> lui-même, qui est l'enfant flexible
      direct de .page-header depuis que l'icône s'y est ajoutée : le
      min-width:0 doit donc porter ici, sinon le groupe icône+texte refuse de
      rétrécir sous sa largeur de contenu et repousse la date hors du cadre. */
   .page-title-group {
      min-width: 0;
      flex: 1 1 auto;
   }

   .page-header .page-title {
      overflow-wrap: anywhere;
      font-size: 1.3rem;
   }

   .page-header .date-time {
      flex: 0 0 auto;
      text-align: right;
      white-space: nowrap;
      font-size: .9rem;
   }

   #horizontal-sidebar {
      max-height: 100px;
   }

   .sidebar-link {
      min-width: 58px;
      height: 58px;
      font-size: 0.8rem;
      padding: 0.4rem;
   }

   .sidebar-link .ph-duotone {
      font-size: 1.25rem;
      margin-bottom: 0.2rem;
   }

}

@media (max-width:992px) {
   #scrollTopBtn {
      width: 42px;
      height: 42px;
      /* 90px auparavant, pour dégager la barre du bas mobile. Celle-ci
         supprimée, le bouton reprend une marge ordinaire. */
      bottom: 15px;
      right: 10px;
      font-size: 1rem;
   }

}

@media (max-width:480px) {

   .sidebar-link {
      min-width: 52px;
      height: 52px;
      font-size: 0.75rem;
      padding: 0.3rem;
   }

   .sidebar-link .ph-duotone {
      font-size: 1.1rem;
      margin-bottom: 0.15rem;
   }

}

@media (max-width:800px) {
   .navbar .search-group,
   .navbar .sessionSelect {
      display: none;
   }

   #mobile-search-select {
      display: flex;
      align-items: stretch;
      gap: .4rem;
      padding: .6rem .4rem;
      background-color: var(--dc-20-w, #cddfe7);
      border-bottom: 1px solid var(--dc-45-w, #8fb6ca);
   }

   [data-theme="dark"] #mobile-search-select {
      background-color: var(--dc-50-k, #032f45);
   }

   /* Le sélecteur de session et la recherche occupent la même boîte : la
      largeur mini et maxi héritées de .sessionSelect sont neutralisées.
      select.sessionSelect (spécificité élément + classe, voir plus haut)
      exige le même préfixe ici, sans quoi cette règle — moins spécifique —
      ne l'emporterait plus. Bordure identique à .search-group : les deux
      occupent la même boîte, ils doivent se lire comme un seul ensemble
      cohérent, pas deux styles différents côte à côte. */
   #mobile-search-select .search-group,
   #mobile-search-select select.sessionSelect {
      width: 100%;
      min-width: 0;
      max-width: none;
      border: 1px solid var(--dc-80-k, #0a2a3c);
      /* !important : neutralise un éventuel utilitaire mx-* resté dans le
         balisage, les classes d'espacement de Bootstrap étant elles-mêmes
         déclarées en !important. */
      margin: 0 !important;
   }

}

@supports not (gap:1px) {
   .page-header>*,
   #horizontal-sidebar .sidebar-items>*,
   .mc-toolbar>*,
   .mc-toolbar-group>*,
   .mc-pagination>*,
   .mc-editor-toolbar>*,
   .mc-footer>* {
      margin: .25rem;
   }

}

@supports not selector(:focus-visible) {
   .mc-btn:focus,
   .mc-tool:focus,
   .mc-page-btn:focus,
   .mc-menu-item:focus,
   .btn:focus {
      outline: 2px solid var(--dominante-color, #065d89);
      outline-offset: 1px;
   }

}

@media (max-width:767.98px) {
   .page-title-icone {
      font-size: 1.3rem;
   }

   .page-header .page-title {
      font-size: var(--fs-lg);
   }

   .page-header .date-time {
      font-size: var(--fs-xs);
   }

}

@media (prefers-reduced-motion:reduce) {
   .mc-btn,
   .mc-spinner {
      transition: none;
      -webkit-animation: none;
      animation: none;
   }

}

/* ========================================================================
   BOOTSTRAP — NEUTRALISATION DES COULEURS NATIVES
   ========================================================================
   Bootstrap peint ses variantes, ses états de focus et ses composants actifs
   en #0d6efd. On remappe ses variables plutôt que de repeindre chaque règle :
   une seule source de vérité, la dominante, qui suit le sélecteur de couleur.
   ======================================================================== */

:root {
   --bs-blue: var(--dominante-color);
   --bs-primary: var(--dominante-color);
   --bs-primary-rgb: var(--dominante-rgb);
   --bs-primary-text-emphasis: var(--dc-80-k, #054a6e);
   --bs-primary-bg-subtle: var(--dc-12-w, #e0ecf1);
   --bs-primary-border-subtle: var(--dc-30-w, #b6d0dd);
   --bs-link-color: var(--dominante-color);
   --bs-link-color-rgb: var(--dominante-rgb);
   --bs-link-hover-color: var(--dominante-color-hover);
   --bs-focus-ring-color: var(--dc-30-t, rgba(6, 93, 137, .3));
   /* Aucun soulignement dans le projet : le survol se marque par le fond. */
   --bs-link-decoration: none;
   --bs-danger: var(--state-error);
   --bs-success: var(--state-success);
   --bs-warning: var(--state-warning);
   --bs-form-valid-color: var(--state-success);
   --bs-form-valid-border-color: var(--state-success);
   --bs-form-invalid-color: var(--state-error);
   --bs-form-invalid-border-color: var(--state-error);
}

.btn-primary {
   /* color/bg/border-color de base + hover retirés : toujours écrasés plus
      loin dans ce fichier par une 2e règle .btn-primary/.btn-primary:hover
      en couleurs directes (background/border-color/color, pas via ces
      variables Bootstrap) — ces 6 propriétés n'étaient jamais effectives.
      disabled-*/focus-shadow-rgb restent, eux : aucune règle
      .btn-primary:disabled ni :focus ne les retouche, ils sont bien
      consommés par .btn de base (bootstrap.css).
      active-bg/active-border-color/active-color : PAS dans le même cas —
      piège vécu, .btn-primary:active de ce fichier (plus bas) ne suffit
      PAS à les rendre inertes. Bootstrap a sa propre règle pour l'état actif
      (`:not(.btn-check)+.btn:active`, bootstrap.css) dont la spécificité
      CSS (0,3,0) dépasse celle de `.btn-primary:active` (0,2,0) — elle
      l'emporte donc MALGRÉ l'ordre de chargement des fichiers, et
      consommait ces 3 variables restées à leurs valeurs Bootstrap par
      défaut (bleu natif) : le bouton clignotait bleu au clic avant d'être
      surchargé ci-dessous. */
   --bs-btn-disabled-bg: var(--dc-40-w, #9bbed0);
   --bs-btn-disabled-border-color: var(--dc-40-w, #9bbed0);
   --bs-btn-focus-shadow-rgb: var(--dominante-rgb);
   --bs-btn-active-bg: var(--dc-40-w, #9bbed0);
   --bs-btn-active-border-color: var(--dc-60-k, #043852);
   --bs-btn-active-color: var(--dc-40-k, #022537);
}

.btn-secondary {
   --bs-btn-color: var(--dominante-color);
   --bs-btn-bg: var(--dc-12-t, rgba(6, 93, 137, .12));
   --bs-btn-border-color: var(--line);
   --bs-btn-hover-color: var(--dominante-color-hover);
   --bs-btn-hover-bg: var(--dc-25-t, rgba(6, 93, 137, .25));
   --bs-btn-hover-border-color: var(--line);
   --bs-btn-active-color: var(--dominante-color-hover);
   --bs-btn-active-bg: var(--dc-30-t, rgba(6, 93, 137, .3));
   --bs-btn-active-border-color: var(--line);
   --bs-btn-focus-shadow-rgb: var(--dominante-rgb);
}

.btn-outline-primary {
   --bs-btn-color: var(--dominante-color);
   --bs-btn-border-color: var(--dominante-color);
   --bs-btn-hover-color: var(--dominante-contrast);
   --bs-btn-hover-bg: var(--dominante-color);
   --bs-btn-hover-border-color: var(--dominante-color);
   --bs-btn-active-color: var(--dominante-contrast);
   --bs-btn-active-bg: var(--dominante-color);
   --bs-btn-active-border-color: var(--dominante-color);
   --bs-btn-disabled-color: var(--dc-45-w, #8fb6ca);
   --bs-btn-disabled-border-color: var(--dc-45-w, #8fb6ca);
   --bs-btn-focus-shadow-rgb: var(--dominante-rgb);
}

.dropdown-menu {
   --bs-dropdown-link-active-bg: var(--dominante-color);
}

.pagination {
   --bs-pagination-active-bg: var(--dominante-color);
   --bs-pagination-active-border-color: var(--dominante-color);
   --bs-pagination-focus-box-shadow: 0 0 0 .25rem var(--dc-25-t, rgba(6, 93, 137, .25));
   --bs-pagination-hover-color: var(--dominante-color-hover);
   --bs-pagination-color: var(--dominante-color);
}

.accordion {
   --bs-accordion-btn-focus-box-shadow: 0 0 0 .25rem var(--dc-25-t, rgba(6, 93, 137, .25));
   --bs-accordion-active-color: var(--dominante-color);
   --bs-accordion-active-bg: var(--dc-10-w, #e6eff3);
}

.progress {
   --bs-progress-bar-bg: var(--dominante-color);
}

.list-group {
   --bs-list-group-active-bg: var(--dominante-color);
   --bs-list-group-active-border-color: var(--dominante-color);
}

.btn-close {
   --bs-btn-close-focus-shadow: 0 0 0 .25rem var(--dc-25-t, rgba(6, 93, 137, .25));
}

.nav-link:focus-visible {
   box-shadow: 0 0 0 .25rem var(--dc-25-t, rgba(6, 93, 137, .25));
}

/* Aucun lien souligné, nulle part : Bootstrap le pose en dur sur <a>. */
a,
a:hover,
a:focus {
   text-decoration: none;
}

/* Type 1 (liens standards, la grande majorité) : suivent la charte
   dominante (couleur par défaut déjà pilotée par --bs-link-color) et se
   marquent au survol par un rouge d'état (jeton déjà établi, pas une
   couleur inventée). Voir plus bas pour le Type 2 (liens Id de membre),
   qui est une exception délibérée à cette même charte. */
a:hover {
   color: var(--state-error);
}

/* Exception à la charte ci-dessus (Type 1 juste au-dessus), posée à la
   demande d'abdAllah — pas la couleur "dominante" habituelle des liens, un
   bleu/rouge classique de lien web précis, pour les rendre reconnaissables
   comme tels (ex. renduColonneId(), shared/membres.php). Valeurs EN DUR,
   PAS de variable CSS : ce fichier remappe délibérément --bs-blue vers
   var(--dominante-color) plus haut ("NEUTRALISATION DES COULEURS NATIVES"
   — une seule source de vérité, la dominante) — une couleur qui suivrait
   la dominante manquerait justement le but de cette exception.

   Plus de <a> imbriqué (1er essai, .linkUrl posée dessus) — abdAllah a
   demandé plus simple : onclick directement sur le <td> lui-même (toute
   la cellule cliquable, pas seulement le texte), .linkUrl-cellule porte
   donc maintenant AUSSI le style de texte (avant : .linkUrl séparée).

   `!important` sur color, ajouté ensuite : nécessaire quand ce <td> se
   trouve dans un <table> qui porte AUSSI la classe .table de Bootstrap
   (ex. composition-des-classes.php) — sa règle .table> :not(caption)>*>*
   a une spécificité plus élevée (0,1,1, le :not(caption) comptant comme
   un sélecteur de type) que .linkUrl-cellule seule (0,1,0), et l'emporte
   sinon silencieusement. Sans effet sur les pages en mc-datatable (pas
   la classe .table), où cette règle Bootstrap ne s'applique jamais. */
.linkUrl-cellule {
   color: #3674ce !important;
   cursor: pointer;
}

/* Effet "survol de bouton" sur toute la cellule, pas seulement le texte
   de l'id. `!important` nécessaire : le <td> a déjà un background-color
   inline (genre_couleur()), plus spécifique que n'importe quelle règle
   CSS externe sans lui.

   1er essai (fond bleu, dérivé de #3674ce) : abdAllah a demandé du gris à
   la place — #6c757d, gris Bootstrap standard (--bs-gray) en dur, même
   prudence que pour le bleu/rouge plus haut (pas var(--bs-gray), au cas
   où ce jeton serait un jour remappé comme --bs-blue l'est déjà).
   Le texte, lui, reste rouge au survol (#b40303) — jamais bleu, inchangé
   par cette demande. 1er essai du fond (opacité 12% seule) : appliqué
   mais invisible sur le pastel très clair de genre_couleur() — opacité
   nettement relevée + box-shadow inset (contour visible quelle que soit
   la couleur de fond sous-jacente, contrairement à un aplat seul).

   Plus simple encore (abdAllah) que scoper cette règle à `td` : le `<th>`
   de la colonne Id (renduEnteteColonnesMembre() et les 3 autres pages qui
   construisent leur propre en-tête) ne porte tout simplement plus la
   classe `.linkUrl-cellule` du tout — il n'est jamais cliquable, cette
   classe entière (couleur, gras, curseur, survol) n'a donc pas sa place
   dessus. Seul le `<td>` la porte désormais. */
.linkUrl-cellule:hover {
   color: #b40303 !important;
   background-color: rgb(182 185 187 / 53%) !important;
   box-shadow: inset 0 0 0 1px #6c757d;
   transition: background-color .15s ease, box-shadow .15s ease, color .15s ease;
}

/* Alertes : Bootstrap les peint avec ses propres verts et rouges. On remappe
   ses variables sur les jetons d'état — l'information reste lisible (vert =
   réussi, rouge = échec) tout en restant dans la palette du projet. */
.alert-success {
   --bs-alert-color: var(--state-success);
   --bs-alert-bg: var(--state-success-bg);
   --bs-alert-border-color: var(--state-success-line);
}

.alert-danger {
   --bs-alert-color: var(--state-error);
   --bs-alert-bg: var(--state-error-bg);
   --bs-alert-border-color: var(--state-error-line);
}

.alert-warning {
   --bs-alert-color: var(--state-warning);
   --bs-alert-bg: var(--state-warning-bg);
   --bs-alert-border-color: var(--state-warning-line);
}

/* Valeurs écrites en dur dans Bootstrap : aucune variable ne les couvre */
.form-select:focus,
.form-check-input:focus {
   border-color: var(--dominante-color);
   box-shadow: 0 0 0 .25rem var(--dc-25-t, rgba(6, 93, 137, .25));
}

.form-check-input:checked,
.form-check-input[type="checkbox"]:indeterminate {
   border-color: var(--dominante-color);
}

.form-range::-webkit-slider-thumb {
   background-color: var(--dominante-color);
}

.form-range::-moz-range-thumb {
   background-color: var(--dominante-color);
}

.form-range:focus::-webkit-slider-thumb {
   box-shadow: 0 0 0 1px #fff, 0 0 0 .25rem var(--dc-25-t, rgba(6, 93, 137, .25));
}

.form-range:focus::-moz-range-thumb {
   box-shadow: 0 0 0 1px #fff, 0 0 0 .25rem var(--dc-25-t, rgba(6, 93, 137, .25));
}

/* Le chevron de l'accordéon et la pastille de l'interrupteur sont des images
   SVG encodées, couleur comprise : impossible à repeindre par une variable.
   On les traite en masque, l'aplat vient donc de la charte. */
.accordion-button::after {
   background-color: var(--slate);
   background-image: none;
   -webkit-mask: var(--bs-accordion-btn-icon) center / contain no-repeat;
   mask: var(--bs-accordion-btn-icon) center / contain no-repeat;
}

.accordion-button:not(.collapsed)::after {
   background-color: var(--dominante-color);
   background-image: none;
}

.form-check.form-switch .form-check-input:focus {
   --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e");
}

/* ========================================================================
   04. COMPOSANTS BOOTSTRAP — cartes, modales, formulaires, boutons, onglets
   ======================================================================== */

input[type="color"]:focus {
   outline: none;
}

[data-theme="dark"] hr {
   background: var(--dc-50-w, #83aec4);
}

.form-control:focus {
   outline: none;
   border-color: var(--dominante-color);
   color: var(--dominante-color);
   background: var(--dc-7-w, #eef4f7);
   box-shadow: 0 0 5px 2px var(--dc-30-t, rgba(6, 93, 137, 0.3));
   transition: border-color 0.3s ease, box-shadow 0.3s ease;
}

.form-label,
.form-check-label {
   color: var(--dc-90-777777, #116087);
   vertical-align: middle;
}

.form-check-label,
.form-select {
   cursor: pointer;
}

input::placeholder,
textarea::placeholder,
select::placeholder {
   color: #777;
   opacity: 1;
}

[data-theme="dark"] input:focus,
[data-theme="dark"] select:focus,
[data-theme="dark"] textarea:focus,
[data-theme="dark"] button:focus {
   outline: none;
   border-color: var(--dc-20-w, #cddfe7);
   box-shadow: 0 0 5px 2px var(--dc-50-k, #032f45);
   transition: border-color 0.3s ease, box-shadow 0.3s ease;
}

[data-theme="dark"] .form-control,
[data-theme="dark"] .form-select {
   transition: background var(--transition-speed), color var(--transition-speed);
}

[data-theme="dark"] input::placeholder,
[data-theme="dark"] textarea::placeholder,
[data-theme="dark"] select::placeholder {
   color: #bbb;
   opacity: 1;
}

.darkmode-btn {
   background: var(--dominante-color) !important;
   color: #fff !important;
   transition: all .4s ease;
   padding: 4px 32px;
}

.darkmode-btn:hover {
   transform: scale(.95);
   background-color: var(--dc-80-k, #054a6e) !important;
}

#colorPickerBody {
   height: 44px;
}

/* Le sélecteur occupe toute la largeur du menu. Sa largeur de 100 % ne
   suffisait pas : .menu-header est une colonne flex centrée, le <form> qui
   porte le sélecteur s'y réduisait donc à son contenu, et « 100 % » ne valait
   que 100 % de ce contenu. C'est le formulaire qu'il fallait étirer. */
#formStatut,
.statut-unique {
   width: 100%;
}

/* Badge du statut affiché quand un membre n'en a qu'un seul (pas de vrai
   sélecteur dans ce cas, juste ce libellé) — même habillage. */
.statut-unique {
   border-radius: var(--bs-border-radius);
   background-color: var(--bs-white);
   text-align: center;
   padding-top: .25rem;
   padding-bottom: .25rem;
   margin-top: 1rem;
   font-size: .9rem;
}

/* Champ identifiant mis en avant — même format PARTOUT où ce numéro
   apparaît (installation-superviseur.php, mon-profil.php, modal-adulte.php,
   mbr_add-adulte.php, formulaire-enfant.php…) : un seul format, jamais deux
   pages qui divergent sur le même champ. */
.mc-identifiant-accent {
   width: 130px;
   font-size: 1.3rem !important;
   font-weight: 700;
   letter-spacing: 1.5px;
   padding: 0 !important;
}

/* Pastille verte "•" — même légende que mc-requis (astérisque rouge, non
   stylé, hérite du texte), pour les champs facultatifs d'un formulaire
   membre (Photo, mobile, référent…). Couleur alignée sur --state-success,
   jamais une teinte de vert propre à ce composant. */
.mc-optionnel {
   color: var(--state-success);
   font-weight: 700;
}

/* Le sélecteur de statut est un CHAMP COMME LES AUTRES : icône dans un
   input-group-text, liste à côté, exactement comme le champ Pays du gabarit.
   C'est là toute la raison d'être de ce gabarit — une seule manière de
   composer un champ, ici comme dans les formulaires.

   Il portait auparavant son propre habillage : pleine largeur, bordure
   effacée, fond teinté. Autant de règles qui le faisaient diverger, et qui
   contrariaient de surcroît le groupe — .input-group > .mc-combo lui donne
   flex: 1 1 auto, qu'un width: 100 % aurait défait. */
#formStatut .input-group {
   margin: 1rem 0 .5rem;
}

/* La flèche part à droite, la valeur prend ce qui reste. */
.mc-combo[data-select="selectStatut"] .mc-combo-field {
   justify-content: space-between;
}

[data-theme="dark"] .card-header,
.card-body,
.modal-body {
   background: #f9f9f9;
}

[data-theme="dark"] .card-body,
[data-theme="dark"] .modal-body {
}

/* La couleur des options sélectionnées.

   DEUX CAS À NE PAS CONFONDRE.

   Un select SIMPLE ouvre un menu surgissant, dessiné par le système : sous
   Windows et macOS, aucune de ces déclarations ne l'atteint. C'est précisément
   pourquoi MCSelect existe. La règle ne sert donc que Firefox et Chrome sous
   Linux — on la garde à ce titre, mais elle ne garantit rien.

   Une liste MULTIPLE, ou dotée d'un size, est tout autre chose : ce n'est pas
   un menu mais une boîte de liste posée dans la page, et le CSS l'atteint.

   Reste que Chrome écrase background-color sur une option sélectionnée. Un
   DÉGRADÉ passe, lui : c'est une image de fond, traitée par un autre chemin.
   D'où linear-gradient d'une seule couleur — un aplat déguisé, seule façon
   connue d'imposer la dominante à la place du bleu système. */
.form-select option:hover,
.form-select option:checked,
.form-select option:focus {
   background-color: var(--dominante-color) !important;
   color: #fff !important;
}

select[multiple] option:checked,
select[size]:not([size="1"]) option:checked {
   background: linear-gradient(0deg,
      var(--dominante-color) 0%, var(--dominante-color) 100%) !important;
   color: #fff !important;
}

/* Une liste multiple qui n'a plus le focus grise sa sélection dans certains
   navigateurs : la dominante y perdrait sa lisibilité. On la maintient. */
select[multiple]:not(:focus) option:checked,
select[size]:not([size="1"]):not(:focus) option:checked {
   background: linear-gradient(0deg,
      var(--dc-60-w, #4a90b8) 0%, var(--dc-60-w, #4a90b8) 100%) !important;
   color: #fff !important;
}

.btn-primary {
   background: var(--dc-80-w, #387da1);
   border-color: var(--dc-70-555555, #1e5b79);
   color: #fff;
}
.btn-primary:hover {
   background: var(--dc-85-k, #054f74);
   border-color: var(--dc-80-w, #387da1);
}

.btn-primary:active,
.btn-primary:focus {
   background: var(--dc-40-w, #9bbed0);
   border-color: var(--dc-60-k, #043852);
   color: var(--dc-40-k, #022537);
}

/* Même piège que :active/:focus ci-dessus (voir le commentaire du bloc de
   variables plus haut) — les 3 variables --bs-btn-disabled-* réassignées
   ne suffisent pas à elles seules à empêcher le bleu natif Bootstrap le
   temps où le bouton est disabled (spinner de soumission, mc-soumission.js) :
   règle directe nécessaire, même motif exact. */
.btn-primary:disabled {
   background: var(--dc-40-w, #9bbed0);
   border-color: var(--dc-40-w, #9bbed0);
   color: var(--dc-40-k, #022537);
}

[data-theme="dark"] .btn-primary:hover,
[data-theme="dark"] .btn-primary:focus {
   background: var(--dc-85-k, #054f74);
   border-color: #fff;
}

.accordion-button {
   border-radius: 0;
   background: var(--dc-3-w, #f8fafb);
   color: var(--dominante-color);
}

.accordion-button:not(.collapsed) {
   background: var(--dc-7-w, #eef4f7);
   color: var(--dominante-color);
}

[data-theme="dark"] .accordion-button {
   background: var(--dc-50-999999, #507b91);
   color: var(--dc-3-w, #f8fafb);
}

[data-theme="dark"] .accordion-body {
   background: #444;
   color: #fff;
}

.dropdown .btn.dropdown-toggle {
   background-color: var(--dc-80-w, #387da1);
   border-color: var(--dominante-color);
   color: #fff;
   transition: all 0.25s ease;
}

.dropdown .btn.dropdown-toggle:hover,
.dropdown .btn.dropdown-toggle:focus {
   background-color: var(--dominante-color);
   border-color: var(--dominante-color);
   color: #fff;
   box-shadow: 0 0 0 0.15rem var(--dc-40-t, rgba(6, 93, 137, 0.4));
}

.dropdown-item {
   color: var(--dc-90-777777, #116087);
   padding: 0.5rem 1rem;
   transition: background-color 0.2s, color 0.2s;
}

/* Icône d'un dropdown-item : trop petite par défaut (héritée de la taille du
   texte), et collée au libellé sans espace. Convention générale — tout menu
   déroulant Bootstrap du projet en profite, pas seulement celui-ci. */
.dropdown-item .ph-duotone {
   font-size: 1.1rem;
}

.dropdown-item:hover,
.dropdown-item:focus {
   background-color: var(--dc-70-w, #518eac) !important;
   color: #fff;
}

.dropdown-item.active,
.dropdown-item:active {
   background-color: var(--dominante-color) !important;
   color: #fff !important;
}

[data-theme="dark"] .dropdown-item {
   color: #ddd;
}

[data-theme="dark"] .dropdown-item:hover,
[data-theme="dark"] .dropdown-item:focus {
   background-color: var(--dc-20-333333, #2a3b44);
   color: #fff;
}

[data-theme="dark"] .dropdown-item.active,
[data-theme="dark"] .dropdown-item:active {
   background-color: var(--dominante-color) !important;
   color: #fff !important;
}

/* Titre de la carte d'inscription — jamais de retour à la ligne (le
   card-header est en flex avec le dropdown d'actions à droite, un titre
   long le pousserait hors de la carte) ; tronqué avec des points de
   suspension au besoin. min-width:0 nécessaire pour qu'un enfant flex
   accepte de rétrécir sous sa largeur naturelle. */
.mc-inscription-titre {
   min-width: 0;
   overflow: hidden;
   white-space: nowrap;
   text-overflow: ellipsis;
}

/* Dropdown d'actions de la carte d'inscription : bouton "trois
   points" sans fond propre (le fond de la carte suffit) ni caret Bootstrap
   natif (::after, ajouté automatiquement par .dropdown-toggle). Écart
   icône/texte élargi UNIQUEMENT dans ce dropdown précis — les autres
   dropdown-item du projet gardent leur espacement existant. */
.mc-dropdown-inscription-toggle {
   background: none !important;
   border: none !important;
   border-radius: .375rem;
   padding: .25rem .5rem;
   transition: background-color .15s ease;
}

.mc-dropdown-inscription-toggle:hover {
   background-color: rgba(0, 0, 0, .08) !important;
}

.mc-dropdown-inscription-toggle::after {
   display: none;
}

.mc-dropdown-menu-inscription .dropdown-item .ph-duotone {
   margin-right: .6rem;
}

/* Carte "Coordonnées" (informations-classes.php) — même bouton/dropdown
   déjà établi pour les actions d'inscription (.mc-dropdown-inscription-
   toggle, gestion-des-inscriptions.php), contenu différent : identité +
   adresse (fond pâle, bordure gauche colorée selon le genre — voir
   genre_couleur(), posée en style inline côté PHP) puis email/mobile
   (fond blanc normal, section séparée), calqués sur le modèle fourni par
   abdAllah. */
.mc-dropdown-coordonnees {
   width: 320px;
}

.mc-coordonnees-identite {
   padding: .75rem 1rem;
   background: var(--dc-8-t, rgba(6, 93, 137, .08));
   border-radius: var(--radius-field) var(--radius-field) 0 0;
}

.mc-coordonnees-identite .mc-coordonnees-adresse {
   margin-top: .35rem;
   font-size: var(--fs-sm);
   color: var(--mc-muted);
}

.mc-coordonnees-contact {
   padding: .75rem 1rem;
}

.mc-coordonnees-contact div {
   font-size: var(--fs-sm);
}

.mc-coordonnees-contact div + div {
   margin-top: .5rem;
}

.mc-coordonnees-identite .ph-duotone,
.mc-coordonnees-contact .ph-duotone {
   margin-right: .5rem;
   color: var(--dominante-color);
   font-size: 1.2rem;
   vertical-align: middle;
}

/* Libellé discret devant une valeur (ex. "Inscrite le", "Saisie effectuée
   par") — classe générique, pas spécifique aux inscriptions malgré son
   1er usage ici. */
.mc-label-dominante {
   font-size: 9pt;
   color: var(--dominante-color) !important;
   vertical-align: middle;
}

.ribbon {
   --f: .5em;
   font-size: 15px;
   font-weight: bold;
   color: #fff;
   position: absolute;
   top: 0;
   left: 0;
   line-height: 1.9;
   padding-inline: 1.5lh;
   padding-bottom: var(--f);
   border-image: conic-gradient(#0008 0 0) 51% / var(--f);
   clip-path: polygon(100% calc(100% - var(--f)), 100% 100%, calc(100% - var(--f)) calc(100% - var(--f)), var(--f) calc(100% - var(--f)), 0 100%, 0 calc(100% - var(--f)), 999px calc(100% - var(--f) - 999px), calc(100% - 999px) calc(100% - var(--f) - 999px));
   transform: translate(calc((cos(45deg) - 1) * 100%), -100%) rotate(-45deg);
   transform-origin: 100% 100%;
   background-color: #c20000;
}

/* Ruban « Désistée » d'une inscription, espaces membres (abdAllah, 25/08) :
   le bloc est petit, 15px y débordait largement. */
.mc-carte-inscription > .ribbon {
   font-size: 10px;
}

/* Le ruban sort en haut à gauche de son bloc : celui-ci passe devant ses
   voisins, sinon le ruban serait recouvert par le bloc du dessus. */
.mc-carte-inscription:has(> .ribbon) {
   z-index: 1;
}

/* Variante utilisée sur #headerMembre et les cartes cliquables de la
   colonne PROFILS (carte-membre-lien.php) — pas sur le ribbon "Désistée"
   des inscriptions, non concerné par cette demande. */
.ribbon-membre {
   line-height: 1.7;
   font-size: 13px;
}

.accordion-button:focus,
.accordion-button:not(.collapsed) {
   box-shadow: 0 0 3px 0.15rem var(--dc-50-w, #83aec4);
   outline: none;
}

.tab-content {
   background: #fff;
   color: var(--text-color);
   border: 1px solid var(--dc-30-w, #b4cedc);
   border-radius: 0;
   padding: .75rem;
}

[data-theme="dark"] .tab-content {
   background: #414040;
   color: #fff;
}

.nav-tabs .nav-link {
   color: var(--dominante-color);
   background: var(--dc-7-w, #eef4f7) !important;
   border: 1px solid #e6e6e6;
   margin: 2px 2px -1px 0;
   transition: all .3s ease;
}

.nav-tabs .nav-link:hover:not(.active) {
   color: var(--dc-15-k, #010e15) !important;
   background: var(--dc-15-w, #dae7ed) !important;
   border: 1px solid var(--dominante-color);
   transform: translateY(-3px) scale(.97);
   box-shadow: 0 0 2px var(--dc-15-k, #010e15) !important;
}

.nav-tabs .nav-link.active,
.nav-tabs .nav-link:focus {
   border-color: var(--dominante-color) !important;
   color: #fff !important;
   background: var(--dc-50-w, #83aec4) !important;
   box-shadow: 0 0 5px 2px var(--dc-30-t, rgba(6, 93, 137, 0.3)) !important;
}

[data-theme="dark"] .nav-tabs .nav-link {
   background: var(--dc-50-999999, #507b91) !important;
   color: var(--dc-3-w, #f8fafb) !important;
   border-color: var(--dc-50-w, #83aec4) !important;
}

[data-theme="dark"] .nav-tabs .nav-link:hover {
   background: var(--dc-40-w, #9bbed0) !important;
   border-color: var(--dc-50-w, #83aec4) !important;
}

[data-theme="dark"] .nav-tabs .nav-link.active {
   border-color: var(--dominante-color) !important;
   color: #fff !important;
   background: var(--dc-50-w, #83aec4) !important;
   box-shadow: 0 0 3px 1px var(--dc-20-w, #cddfe7) !important;
}

input[type="checkbox"].form-check-input {
   -webkit-appearance: none;
   -moz-appearance: none;
   appearance: none;
   vertical-align: middle;
   width: 22px;
   height: 22px;
   border: 2px solid var(--dominante-color);
   border-radius: 4px;
   background-color: transparent;
   position: relative;
   cursor: pointer;
   transition: all 0.25s ease;
}

[data-theme="dark"] input[type="checkbox"].form-check-input,
[data-theme="dark"] .form-check-input[type="radio"] {
   border: 2px solid var(--dc-70-w, #518eac);
}

input[type="checkbox"].form-check-input:hover {
   box-shadow: 0 0 0 0.15rem var(--dc-20-t, rgba(6, 93, 137, 0.2));
}

input[type="checkbox"].form-check-input::after {
   content: "";
   position: absolute;
   /* Coche contenue dans la case : les dimensions sont celles du trait,
      pas celles de la case. */
   top: 2px;
   left: 6px;
   width: 6px;
   height: 12px;
   border: solid var(--dc-50-777777, #3f6a80);
   border-width: 0 3px 3px 0;
   transform: rotate(40deg);
   opacity: 0;
   pointer-events: none;
   transition: opacity 0.2s ease-in-out;
}

[data-theme="dark"] input[type="checkbox"].form-check-input::after {
   border: solid var(--dc-1-w, #fdfdfe);
   border-width: 0 3px 3px 0;
}

input[type="checkbox"].form-check-input:checked::after {
   opacity: 1;
}

input[type="checkbox"].form-check-input:checked {
   background-color: var(--dc-40-w, #9bbed0);
   border-color: var(--dominante-color);
}

.form-check-input[type="radio"] {
   cursor: pointer;
   width: 22px;
   height: 22px;
   border: 2px solid var(--dominante-color);
   background-color: transparent;
   position: relative;
   -moz-appearance: none;
   transition: all 0.25s ease;
}

.form-check-input[type="radio"]:hover {
   box-shadow: 0 0 0 0.15rem var(--dc-20-t, rgba(6, 93, 137, 0.2));
}

.form-check-input[type="radio"]::after {
   content: "";
   position: absolute;
   top: 50%;
   left: 50%;
   width: 12px;
   height: 12px;
   background-color: var(--dc-50-aaaaaa, #58849a);
   border-radius: 50%;
   transform: translate(-50%, -50%) scale(0);
   transition: transform 0.25s ease;
}

.form-check-input[type="radio"]:checked::after {
   transform: translate(-50%, -50%) scale(1);
}

.form-check.form-switch .form-check-input {
   width: 2.6rem;
   height: 1.4rem;
   border-radius: 2rem;
   background-color: #ddd;
   border: none;
   transition: background-color 0.25s, background-position 0.25s;
}

.form-check.form-switch .form-check-input:checked {
   background-color: var(--dominante-color);
   background-position: right center;
}

[data-theme="dark"] .form-check.form-switch .form-check-input:checked {
   background-color: var(--dc-80-999999, #23698c);
   background-position: right center;
}

.form-check.form-switch .form-check-input::after {
   display: none;
}

.form-check.form-switch .form-check-input:hover {
   filter: brightness(1.1);
}

.form-check-input:focus {
   outline: none;
   box-shadow: 0 0 0 0.2rem var(--dc-30-t, rgba(6, 93, 137, 0.3));
}

/* ÉTAT VALIDE DES CASES, BOUTONS RADIO ET INTERRUPTEURS : LE CONTRÔLE GARDE
   LA DOMINANTE.

   Bootstrap fait verdir le contrôle en même temps que son libellé. Ici, seul le
   TEXTE verdit ; la case, la pastille et l'interrupteur restent à la charte.

   Rien à écrire pour cela : le projet redessine déjà ces contrôles avec des
   sélecteurs plus spécifiques que ceux de Bootstrap —

      input[type="checkbox"].form-check-input   (0,2,1)
      .was-validated .form-check-input:valid    (0,2,0)

   — et l'emporte donc naturellement. C'est cette même mécanique qui pourrait
   passer pour un défaut : le libellé verdit, le contrôle non. C'est voulu.

   La règle de Bootstrap qui colore le LIBELLÉ n'a, elle, pas de concurrente
   dans le projet : elle s'applique, et c'est ce qu'on veut. */

/* Une exception, POUR L'ÉTAT COCHÉ. Bootstrap y ajoute une pseudo-classe, et
   passe alors devant :

      .was-validated .form-check-input:valid:checked    (0,4,0)  ← l'emportait
      input[type="checkbox"].form-check-input:checked   (0,3,1)
      .form-check-input[type="radio"]                   (0,2,0)

   Le fond de la case et du bouton radio repassait donc au vert une fois coché,
   alors même que la bordure gardait la dominante. On rétablit la charte avec le
   poids qu'il faut.

   L'interrupteur, lui, n'a rien demandé : sa règle atteint (0,4,0) et l'emporte
   par l'ordre de chargement, backend.css venant après bootstrap.css. */
.was-validated input[type="checkbox"].form-check-input:valid:checked {
   background-color: var(--dc-40-w, #9bbed0);
}

.was-validated .form-check-input[type="radio"]:valid:checked {
   background-color: transparent;
}

/* La BORDURE du bouton radio, seconde règle de Bootstrap et second duel perdu :

      .was-validated .form-check-input:valid   (0,3,0)  ← l'emportait
      .form-check-input[type="radio"]          (0,2,0)

   Le projet n'a pas de variante :checked pour le radio — contrairement à la
   case —, si bien que l'anneau restait vert dans les DEUX états, et sur les
   deux boutons du groupe dès qu'il devient valide. */
.was-validated .form-check-input[type="radio"]:valid {
   border-color: var(--dominante-color);
}

[data-theme="dark"] .was-validated .form-check-input[type="radio"]:valid {
   border-color: var(--dc-70-w, #518eac);
}


input[type="range"] {
   cursor: pointer;
   background: transparent;
}

input[type="range"]::-webkit-slider-runnable-track {
   height: 6px;
   border-radius: 3px;
   background: var(--dc-30-cccccc, #91abb8);
}

input[type="range"]::-webkit-slider-thumb {
   -webkit-appearance: none;
   width: 18px;
   height: 18px;
   border-radius: 50%;
   background: var(--dominante-color);
   margin-top: -6px;
}

[data-theme="dark"] input[type="range"]::-webkit-slider-thumb {
   border: 1px solid #fff;
   background-color: var(--dc-80-999999, #23698c);
}

input[type="range"]::-moz-range-track {
   height: 6px;
   border-radius: 3px;
   background: var(--dc-30-cccccc, #91abb8);
}

input[type="range"]::-moz-range-thumb {
   width: 18px;
   height: 18px;
   border-radius: 50%;
   background: var(--dominante-color);
}

::-webkit-scrollbar {
   width: 5px;
   height: 5px;
}

::-webkit-scrollbar-track {
   box-shadow: inset 0 0 3px #999;
   background: #f8f8f8;
}

::-webkit-scrollbar-thumb {
   background: var(--dc-80-w, #387da1);
}

::-webkit-scrollbar-thumb:hover {
   background: var(--dc-40-w, #9bbed0);
   cursor: grab;
}

input[type="color"] {
   outline: none;
   width: 30px;
   height: 30px;
   padding: 0;
   margin: 0;
   -moz-appearance: none;
   border: 1px solid rgba(0, 0, 0, .2);
   border-radius: .35rem;
   background: none;
}

input[type="color"]::-webkit-color-swatch-wrapper {
   padding: 2px;
}

input[type="color"]::-webkit-color-swatch {
   border: none;
   border-radius: .2rem;
}

input[type="color"]::-moz-color-swatch {
   border: none;
   border-radius: .2rem;
}

.card-body p,
.accordion-body,
.tab-content p,
.modal-body p {
   max-width: 72ch;
}

.form-check-label {
   font-size: .9rem;
   line-height: var(--lh-snug);
}

.invalid-feedback {
   font-size: 9pt;
   font-weight: 600;
}

.btn {
   font-size: var(--fs-sm);
   font-weight: var(--fw-bold);
   line-height: var(--lh-tight);
   letter-spacing: .01em;
   margin-bottom: 3px;
}

.btn-sm {
   font-size: var(--fs-xs);
}

.btn-xs {
   font-size: 9pt !important;
   padding: 2px 8px !important;
   line-height: 1 !important;
   min-height: 0 !important;
}

.dropdown-item,
.nav-tabs .nav-link,
.accordion-button {
   font-size: var(--fs-sm);
}

.accordion-button,
.nav-tabs .nav-link.active {
   font-weight: var(--fw-bold);
}

.table {
   font-size: var(--fs-sm);
}

.table thead th {
   font-weight: var(--fw-bold);
   letter-spacing: .01em;
}

[data-theme="dark"] body {
   background: #12141c;
}

.card,
.modal-content {
   background: var(--panel);
   border: 1px solid var(--line);
   color: inherit;
   border-radius: 0;
   box-shadow: var(--shadow-panel);
   /* Pas de `overflow: hidden` : il rognait les menus déroulants ouverts
      depuis une carte. Les angles étant vifs, il n'avait plus d'utilité. */
}

/* Une carte qui s'arrête à la largeur de son contenu — un tableau, par
   exemple — plutôt que de s'étirer sur toute la largeur du conteneur, comme
   le fait .card par défaut (comportement de bloc de Bootstrap). Réservée aux
   pages qui le demandent : la plupart des cartes du projet doivent au
   contraire occuper toute la largeur disponible. */
.mc-card-ajustee {
   width: fit-content;
   max-width: 100%;
}

.card-header p {
   margin: .15rem 0 0;
   color: var(--slate-dim);
   font-size: var(--fs-panel-sub);
   font-weight: var(--fw-regular);
   letter-spacing: 0;
}

/* Grosse icône décorative à droite d'un card-header — purement illustrative,
   d'où aria-hidden côté HTML : le titre et le sous-titre suffisent à
   identifier la carte pour un lecteur d'écran. */
.mc-carte-icone {
   font-size: 1.85rem;
   color: var(--dominante-color);
   flex-shrink: 0;
}

/* Convention commune à TOUT tableau ou liste vide du projet — voir
   LE-SOCLE.md, « L'état vide ». */
.mc-etat-vide {
   display: flex;
   flex-direction: column;
   align-items: center;
   gap: .4rem;
   padding: 1.5rem 1rem;
   color: var(--slate);
   text-align: center;
}

.mc-etat-vide .ph-duotone {
   font-size: 2rem;
}

/* Carte d'un événement d'agenda (backend/planifications/agenda.php) — le
   bloc de date en haut, pleine largeur, sans marge ; le corps en dessous ;
   le pied regroupe la progression et les actions, toujours visibles. */
.mc-agenda-carte {
   display: flex;
   flex-direction: row;
   align-items: stretch;
   width: 21rem;
   background: var(--panel-soft, #f4f6f8);
   border: 1px solid var(--line, #dee2e6);
   border-radius: var(--radius-field, .5rem);
   overflow: hidden;
   box-shadow: rgb(0 0 0 / 47%) 0px 2px 3px;
}

/* Le bloc de date colle aux bords de la carte, sans marge ni padding
   autour — juste la hauteur qu'il faut pour ses trois lignes de texte. */
.mc-agenda-bloc-date {
   flex: 0 0 6rem;
   display: flex;
   flex-direction: column;
   align-items: center;
   justify-content: center;
   gap: .1rem;
   padding: .6rem .3rem;
   background: var(--dc-12-t, rgba(6, 93, 137, .12));
   border-right: 3px solid var(--dc-20-t, rgba(6, 93, 137, .2));
   text-align: center;
}

.mc-agenda-jour {
   font-family: 'Open Sans', sans-serif;
   font-size: var(--fs-xs, .8125rem);
   font-weight: 400;
   color: var(--dominante-color, #065d89);
   text-transform: uppercase;
}

.mc-agenda-quantieme {
   font-family: 'Open Sans', sans-serif;
   font-size: 1.3rem;
   font-weight: 700;
   line-height: 1;
   color: var(--dominante-color, #065d89);
}

.mc-agenda-mois {
   font-family: 'Open Sans', sans-serif;
   font-size: var(--fs-xs, .8125rem);
   font-weight: 500;
   color: #b3261e;
   text-transform: uppercase;
}

.mc-agenda-corps {
   flex: 1 1 auto;
   display: flex;
   flex-direction: column;
   padding: .85rem 1rem;
}

.mc-agenda-entete-carte {
   display: flex;
   align-items: baseline;
   justify-content: space-between;
   gap: .4rem;
   cursor: pointer;
   margin-bottom: .3rem;
}

.mc-agenda-titre {
   font-weight: var(--fw-bold);
}

.mc-agenda-chevron {
   flex-shrink: 0;
   color: var(--slate);
   transition: transform .2s ease;
}

.mc-agenda-ouverte .mc-agenda-chevron {
   transform: rotate(180deg);
}

.mc-agenda-pliable {
   max-height: 0;
   overflow: hidden;
   transition: max-height .25s ease;
}

.mc-agenda-ouverte .mc-agenda-pliable {
   max-height: 60rem;
}

.mc-agenda-info {
   font-size: var(--fs-sm);
   color: var(--slate-dim, #5b6174);
   margin: 0 0 .5rem;
}

.mc-agenda-jusquau {
   font-size: var(--fs-xs, .8125rem);
   color: var(--slate);
   margin-bottom: 0;
}

.mc-agenda-destinataires {
   font-size: var(--fs-xs, .8125rem);
   color: var(--dominante-color, #065d89);
   font-style: italic;
   margin-bottom: .5rem;
}

/* Pousse la progression + les actions en bas de carte, même quand une
   carte voisine a un texte plus long — sans ça, le pied de chaque carte
   flotterait à une hauteur différente selon la longueur de l'information. */
.mc-agenda-pied {
   margin-top: auto;
   display: flex;
   align-items: center;
   justify-content: space-between;
   gap: .5rem;
   padding-top: .5rem;
}

.mc-agenda-progression-bloc {
   flex: 1 1 auto;
   min-width: 0;
}

.mc-agenda-progression {
   height: 4px;
   background: var(--line, #dee2e6);
   border-radius: 2px;
   overflow: hidden;
   margin-bottom: .3rem;
}

.mc-agenda-progression > div {
   height: 100%;
   background: var(--dominante-color, #065d89);
}

.mc-agenda-etat {
   font-size: var(--fs-xs, .8125rem);
   color: var(--slate);
}

/* Largeur égale à la hauteur — sinon un bouton icône-seule de btn-sm reste
   plus large que haut (le padding horizontal de Bootstrap dépasse le
   vertical). */
/* LA HAUTEUR seule est réduite (abdAllah, 07/09) : le bouton faisait 36 px là où
   la cellule qui le porte en fait 31, et c'est lui qui décidait de la hauteur des
   lignes. La LARGEUR reste à 2,25 rem — elle ne gênait pas, et la réduire
   serrait les icônes.

   L'ICÔNE suit : le glyphe Phosphor porte sa propre taille, sans quoi le bouton
   garderait la hauteur d'avant. */
.btn-actions {
   width: 2.25rem;
   height: 1.5rem;
   padding-left: 0;
   padding-right: 0;
   display: inline-flex;
   align-items: center;
   justify-content: center;
}

.btn-actions > i {
   font-size: .95rem;
   line-height: 1;
}

/* Bouton "Informations" (classes-de-la-session.php) — transparent par
   défaut (juste l'icône, sans fond ni bordure visibles), un fond léger
   apparaît seulement au survol. */
.mc-info-classe-action {
   background: transparent;
   border: none;
   color: var(--slate);
}

.mc-info-classe-action:hover,
.mc-info-classe-action:focus-visible {
   background: var(--panel-soft);
   color: var(--dominante-color);
}

/* Un événement déjà passé (sa fin, ou son début à défaut de fin, est
   antérieure à aujourd'hui) — le texte se barre et s'estompe, la carte
   reste lisible mais se distingue d'un coup d'œil des événements à venir. */
.mc-agenda-passe {
   opacity: .55;
   filter: grayscale(60%);
}

.mc-agenda-passe .mc-agenda-titre,
.mc-agenda-passe .mc-agenda-info,
.mc-agenda-passe .mc-agenda-jusquau,
.mc-agenda-passe .mc-agenda-destinataires {
   text-decoration: line-through;
}

.card-header {
   font-size: 0.97rem;
   font-weight: var(--fw-bold);
   line-height: 1.3;
   letter-spacing: -.01em;
   padding: .85rem 1.25rem;
   background: var(--dc-12-t, rgba(6, 93, 137, .12));
   border-bottom: 0;
   border-radius: 0;
   color: inherit;
   display: flex;
   flex-wrap: wrap;
   align-items: center;
   justify-content: space-between;
   gap: .6rem;
}

.card-body {
   padding: 1rem 1.25rem 1.25rem;
   background: transparent;
   color: inherit;
}

.card-footer {
   background: var(--dc-12-t, rgba(6, 93, 137, .12));
   border-top: 1px solid var(--line);
   border-radius: 0;
   color: inherit;
   padding: .75rem 1.25rem;
}

/* Le modal se distingue de la carte : un bandeau de tête ET de pied colorés,
   symétriques — la carte, elle, reste transparente (voir
   .card-header/.card-footer juste au-dessus).

   EXACTEMENT la couleur d'.input-group-text (--dc-12-t, 12% d'opacité) —
   pas la couleur dominante pleine, essayée un temps : elle se voit très bien
   en pratique (la capture d'écran le confirme sur les champs du formulaire),
   inutile de forcer plus sombre. Texte et bordure reprennent aussi
   .input-group-text à l'identique — un seul traitement pour « une zone
   teintée par la dominante », qu'elle appartienne à un champ ou à un modal.

   --bs-modal-inner-border-radius à 0 : Bootstrap arrondit par défaut les
   coins de modal-header/footer via cette variable, alors que .modal-content
   est ANGLES VIFS dans tout le projet (voir plus haut : border-radius: 0).
   Sans ce recalage, la couleur du bandeau débordait visiblement des coins
   carrés du modal — invisible tant que le fond était transparent, flagrant
   dès qu'il a pris une couleur. */
.modal-header,
.modal-footer {
   --bs-modal-inner-border-radius: 0;
   background: var(--dc-12-t, rgba(6, 93, 137, .12));
   border-color: var(--line);
   color: var(--dominante-color);
}

/* Le sous-titre du modal-header — même idée que .card-header p (petit,
   grisé), mais une couleur à soi : --slate-dim est pensée pour un fond
   neutre (--panel), le modal-header a le sien, une teinte de la dominante. */
.modal-header p {
   margin: .15rem 0 0;
   color: var(--dominante-color);
   opacity: .75;
   font-size: var(--fs-panel-sub);
   font-weight: var(--fw-regular);
   letter-spacing: 0;
}

.modal-header {
   font-size: 0.97rem;
   font-weight: var(--fw-bold);
   padding: 1rem 1.25rem;
   border-bottom: 1px solid var(--line);
}

.modal-footer {
   border-top: 1px solid var(--line);
   padding: .75rem 1.25rem;
}

.modal-header .btn-close {
   filter: invert(0);
}

/* Icône du titre de modale — même principe que .form-label .ph-duotone
   (margin-right, jamais margin sur le span qui suit) mais un cran plus
   grosse : ici elle porte seule l'identité de la modale, une icône de
   16px dans un titre lui-même petit passait presque inaperçue. */
.modal-title .ph-duotone {
   font-size: 1.4rem;
   margin-right: .6rem;
   vertical-align: -.2em;
}

.modal-body {
   font-size: var(--fs-base);
   line-height: var(--lh-normal);
   padding: 1.25rem;
   background: transparent;
   color: inherit;
}

.chip {
   display: inline-block;
   padding: 3px 9px;
   border-radius: 20px;
   font-size: .6875rem;
   font-weight: var(--fw-bold);
   letter-spacing: 0;
}

.chip-teal {
   color: #3fa796;
   background: rgba(63, 167, 150, .14);
}

.form-control,
.form-select,
.input-group-text {
   min-height: var(--btn-h-md);
   padding: .3rem .6rem;
   font-size: var(--fs-sm);
   line-height: 1.4;
   border-radius: var(--radius-field);
   border-color: var(--line);
}

.input-group-text {
   background: var(--dc-12-t, rgba(6, 93, 137, .12));
   color: var(--dominante-color);
   font-size: .95rem;
   min-width: 2.5rem;
   text-align: center;
   border-color: var(--line);
}

.form-control-color {
   min-height: var(--btn-h-md);
   padding: 2px;
}

.form-label {
   margin-bottom: .2rem;
   font-size: .8125rem;
   font-weight: var(--fw-bold);
   line-height: var(--lh-snug);
   color: var(--slate);
}

/* Case et libellé centrés l'un sur l'autre. Bootstrap fait flotter la case
   et la recale avec `margin-top`, un décalage calculé pour une case de 1 em ;
   les nôtres font 22 px, elles retombaient trop bas. */
.form-check {
   display: flex;
   align-items: center;
   flex-wrap: wrap;
   gap: .5rem;
   min-height: 0;
   margin-bottom: 0;
   padding-left: 0;
}

.form-check-inline {
   display: inline-flex;
   margin-right: 0;
}

.form-check .form-check-input {
   flex: 0 0 auto;
   float: none;
   margin: 0;
}

.form-check-label {
   margin: 0;
}

/* Le message de validation prend toute la largeur sous la case */
.form-check .invalid-feedback {
   flex: 0 0 100%;
   margin-top: .15rem;
}

.form-range {
   height: 1.1rem;
}

[data-theme="dark"] .card,
[data-theme="dark"] .modal-content {
   background: var(--panel);
   border-color: var(--line);
   color: var(--white, #f3f4f7);
}

[data-theme="dark"] .card-body,
[data-theme="dark"] .modal-body {
   background: transparent;
   color: #f3f4f7;
}

/* card-header/card-footer gardent leur fond de charte (--dc-12-t en clair,
   --dc-20-t en sombre) dans les deux thèmes, comme input-group-text et
   modal-header/footer — ce jeton est recalculé par mc-theme.js à chaque
   thème, aucune règle séparée n'est nécessaire pour le fond lui-même. */
[data-theme="dark"] .card-header,
[data-theme="dark"] .card-footer {
   background: var(--dc-20-t, rgba(6, 93, 137, .2));
   color: var(--dc-60-w, #6b9fba);
}

/* modal-header/footer gardent leur fond de charte (--dc-12-t) en sombre
   comme en clair : ce jeton est recalculé par mc-theme.js à chaque thème,
   aucune règle séparée n'est nécessaire ici. */
[data-theme="dark"] .modal-header,
[data-theme="dark"] .modal-footer {
   background: var(--dc-20-t, rgba(6, 93, 137, .2));
   color: var(--dc-60-w, #6b9fba);
}

[data-theme="dark"] .modal-header p {
   color: var(--dc-60-w, #6b9fba);
}

[data-theme="dark"] .card-header,
[data-theme="dark"] .modal-header {
   border-bottom: 1px solid var(--dc-50-w, #83aec4);
   border-bottom-color: var(--line);
}

[data-theme="dark"] .card-footer,
[data-theme="dark"] .modal-footer {
   border-top-color: var(--line);
}

[data-theme="dark"] .form-control,
[data-theme="dark"] .form-select,
[data-theme="dark"] textarea {
   background: var(--panel-soft);
   border-color: var(--line);
   color: #f3f4f7;
}

[data-theme="dark"] .form-control:focus,
[data-theme="dark"] .form-select:focus {
   background: var(--panel-soft);
   color: #f3f4f7;
   transition: background var(--transition-speed), color var(--transition-speed);
   border-color: var(--dominante-color);
}

/* Fond bleu clair imposé par Chrome au remplissage automatique — sa feuille
   de style écrit background-color en !important, aucune déclaration
   d'auteur ne peut la reprendre directement (même constat déjà posé pour
   la coche de validité, voir tools.css § Formulaires). box-shadow échappe
   à ce verrouillage : un inset couvrant tout le champ masque visuellement
   le fond imposé sans jamais le déclarer lui-même. var(--panel) plutôt
   qu'un blanc fixe : déjà blanc en thème clair, s'adapte de lui-même en
   thème sombre. */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
select:-webkit-autofill,
select:-webkit-autofill:hover,
select:-webkit-autofill:focus {
   -webkit-box-shadow: 0 0 0 1000px var(--panel) inset;
   box-shadow: 0 0 0 1000px var(--panel) inset;
   -webkit-text-fill-color: var(--text-color);
}

[data-theme="dark"] .input-group-text {
   background: var(--dc-20-t, rgba(6, 93, 137, .2));
   border-color: var(--line);
   color: var(--dc-60-w, #6b9fba);
}

.input-group-text .ph-duotone {
   font-size: 1.2rem;
}

.form-label .ph-duotone,
.form-check-label .ph-duotone {
   margin-right: .35em;
   font-size: 1rem;
   color: var(--dominante-color);
   vertical-align: -0.15em;
}

.input-group-textarea .input-group-text {
   align-items: flex-start;
   padding-top: .45rem;
}

.input-group-range .form-range {
   flex: 1 1 auto;
   width: 1%;
   min-width: 0;
   height: var(--btn-h-md);
   padding: 0 .7rem;
   border: 1px solid var(--line);
   border-left: 0;
   border-radius: 0 var(--radius-field) var(--radius-field) 0;
   background: var(--panel);
}

[data-theme="dark"] .input-group-range .form-range {
   background: var(--panel-soft);
}

.input-group .form-control-color {
   min-width: 3rem;
}

.form-control[type="file"]::-webkit-file-upload-button,
.form-control[type="file"]::file-selector-button {
   margin: -.3rem .6rem -.3rem -.6rem;
   padding: .3rem .6rem;
   border: 0;
   border-right: 1px solid var(--line);
   background: var(--panel-soft);
   color: var(--slate);
   font-size: var(--fs-sm);
}

.input-group .invalid-feedback {
   flex: 0 0 100%;
   order: 10;
   margin-top: .2rem;
}

.form-control-choices {
   display: flex;
   flex-wrap: wrap;
   align-items: center;
   gap: .25rem 1rem;
   cursor: default;
}

.form-control-choices .form-check {
   margin: 0;
}

.form-control-choices .form-check-label {
   cursor: pointer;
}

.form-control-choices .form-check-input {
   cursor: pointer;
}

.form-control-choices:focus,
.form-control-choices:focus-within {
   box-shadow: none;
}

.form-control-choices:focus-within {
   border-color: var(--dominante-color);
}

@media (max-width:767.98px) {
   /* 16px minimum — sous ce seuil, Safari iOS (et dans une moindre mesure
      Chrome Android) zoome AUTOMATIQUEMENT toute la page dès qu'on touche
      un de ces champs, pour rendre le texte plus lisible. Ce n'est pas une
      règle CSS qui changerait la taille d'un élément précis : c'est le
      navigateur qui zoome la page ENTIÈRE, ce qui donne l'impression que
      "tout" grossit d'un coup. input-group-text n'est lui-même jamais
      focalisable, mais reste à la même taille que le champ qu'il
      accompagne — sinon l'icône/le texte à côté resterait visiblement plus
      petit une fois le champ remonté. */
   .form-control,
   .form-select,
   .input-group-text,
   input[type="text"],
   input[type="tel"],
   input[type="email"],
   input[type="date"],
   input[type="search"],
   textarea,
   select {
      font-size: 16px;
      min-height: 38px;
   }

   .card-header {
      font-size: var(--fs-base);
   }

   .modal-body,
   .card-body {
      font-size: var(--fs-sm);
   }

}

/* ========================================================================
   05. UTILITAIRES, ICÔNES, COMPATIBILITÉ, IMPRESSION
   ======================================================================== */

.ph-duotone,
[class^="ph-"],
[class*=" ph-"] {
   display: inline-block;
   letter-spacing: 0;
   word-spacing: 0;
   vertical-align: -0.125em;
}

[data-theme="dark"] .ph-duotone:before {
   opacity: .35;
}

/* ==========================================================================
   PAGE D'ACCÈS — carte retournable connexion / inscription
   --------------------------------------------------------------------------
   La rotation est pilotée par une case à cocher masquée (#basculeAcces) :
   aucune ligne de JavaScript n'est nécessaire pour retourner la carte.

   Les deux faces occupent la MÊME cellule de grille : le conteneur prend
   ainsi la hauteur de la plus haute des deux, sans hauteur fixe à maintenir.
   ========================================================================== */

.page-acces {
   min-height: 100vh;
   min-height: 100dvh;
   background: var(--body-bg-light);

   /* LA CAUSE DU DÉFILEMENT PERMANENT DE CES PAGES, et elle n'était pas dans
      leur contenu : le `body` du projet porte `margin-bottom: 100px` (voir
      tout en haut de ce fichier), une respiration utile sous une longue page
      de gestion. Ici elle s'ajoute à un `.acces-scene` déjà haut de 100vh :
      le document mesure donc TOUJOURS 100px de plus que la fenêtre, et la
      barre de défilement apparaît même quand le contenu tient largement.
      Aucun resserrement du contenu ne pouvait y changer quoi que ce soit.

      Neutralisée pour les seules pages pleine hauteur — connexion,
      installation, pages de message, accueil public : elles se terminent sur
      le bas de l'écran, il n'y a rien à faire respirer en dessous. Les pages
      de gestion gardent leurs 100px.

      dvh après vh, comme partout ailleurs : 100vh compte la barre d'adresse
      mobile, donc une hauteur que l'écran n'offre pas. La ligne vh reste en
      premier, comme repli pour les navigateurs qui ignorent dvh. */
   margin-bottom: 0;
}

.acces-scene {
   display: flex;
   flex-direction: column;
   align-items: center;
   justify-content: center;
   gap: 1.5rem;
   min-height: 100vh;
   min-height: 100dvh;
   padding: 1.5rem 0.5rem;
}

/* connexion.php tient toujours dans un écran : centrer verticalement (ci-
   dessus) y fonctionne bien. Une page de message (404, session-requise…)
   peut porter bien plus — logo, flash, icône, titre, texte, un formulaire
   embarqué — et centrer un contenu plus haut que l'écran pousse tout vers
   le bas au lieu de partir du haut, comme si la page démarrait au milieu
   d'elle-même. Un point de départ fixe, pas un centrage qui dépend d'une
   hauteur qu'on ne maîtrise pas. */
.page-message .acces-scene {
   justify-content: flex-start;
   padding-top: 4rem;
}

/* Décalage léger vers le haut demandé par abdAllah, PC uniquement — même
   breakpoint que le seuil mobile déjà établi ailleurs dans ce fichier
   (767.98px), inversé. transform plutôt que justify-content/padding : ne
   touche pas au centrage lui-même (préférence déjà tranchée plus haut dans
   ce fichier, voir le commentaire sur .acces-carte) — un translateY ne
   modifie pas le flux de mise en page, juste la position visuelle. */
@media (min-width:768px) {
   .page-connexion .acces-scene {
      transform: translateY(-2.5rem);
   }
}

.acces-logo {
   width: 100%;
   max-width: 8rem;
}

/* La case pilote tout : ni visible, ni atteignable au clavier — ce sont les
   <label> qui la commandent qui portent le focus. */
.acces-bascule {
   position: absolute;
   width: 1px;
   height: 1px;
   opacity: 0;
   pointer-events: none;
}

.acces-carte {
   display: flex;
   flex-direction: column;
   width: 100%;
   max-width: 26rem;
   position: relative;
}

.acces-face {
   /* Colonne : le formulaire reste en haut, la rangée de liens suit les
      boutons. */
   display: flex;
   flex-direction: column;
   padding: 0 2rem;
}

/* Chorégraphie séquentielle demandée par abdAllah (remplace l'ancien
   dépliement où les deux faces animaient SIMULTANÉMENT) : au clic, le
   formulaire actif se replie D'ABORD entièrement (5px), PUIS seulement le
   nouveau s'ouvre. height plutôt que max-height : les deux formulaires
   partent du MÊME état de repos (5px), orchestré pas à pas par le script
   en bas de page — CSS pose juste la transition, JS décide quand chaque
   étape se déclenche. linear plutôt que ease-in/cubic-bezier : demande
   explicite d'une animation "non accélérée". */
.page-connexion #formConnexion,
.page-connexion #formInscription {
   overflow: hidden;
   height: 5px;
   padding: 10px;
   transition: height 1s linear;
}

/* "border-bottom noire un peu plus basse du form login" — le rideau
   (.acces-carte-en-mouvement, plus bas) suit le bord inférieur du contenu
   RÉELLEMENT visible ; cette légère marge pousse ce bord un peu plus bas
   spécifiquement pendant que #formConnexion est seul visible, sans toucher
   au formulaire d'inscription. */
.page-connexion #formConnexion {
   margin-bottom: 1.3rem;
}

/* <hr> entre Étudiant et Externe (voir HTML plus bas, bloc Situation) —
   surcharge le style générique de hr (margin:1rem auto, partagé par tout
   le projet) pour cette page seule, sans y toucher ailleurs. */
.page-connexion hr {
   margin: .7rem auto;
}

/* Formulaire d'inscription centré verticalement dans sa hauteur fixe
   (800px), PC uniquement — sur mobile le contenu
   remplit déjà toute cette hauteur (plus de retours à la ligne sur une
   largeur étroite), le centrage n'y a pas d'utilité. */
@media (min-width:768px) {
   .page-connexion #formInscription {
      display: flex;
      flex-direction: column;
      justify-content: center;
   }
}

/* Rideau — posée par le script UNIQUEMENT le temps de la chorégraphie
   complète (étape 1 à 6), retirée une fois le nouveau formulaire stabilisé.
   Sur .acces-carte (pas .acces-face) : une seule ligne commune aux deux
   formulaires plutôt qu'une par face.
   Un ::after plutôt qu'un border-bottom classique : une bordure ne peut pas
   balayer horizontalement depuis un point central, seule sa LARGEUR
   change. left:50% + translateX(-50%) garde le pseudo-élément centré peu
   importe sa largeur — l'animer de 0 à 100% donne l'effet demandé
   (« part d'un point au centre et élargit sur les côtés jusqu'aux bords »).
   Au retrait de la classe, la transition rejoue automatiquement en sens
   inverse (100%→0, toujours centrée) — effet fermeture symétrique sans
   code séparé. */
.acces-carte::after {
   content: '';
   position: absolute;
   left: 50%;
   bottom: 0;
   width: 0;
   height: 7px;
   background: #606060;
   transform: translateX(-50%);
   transition: width .5s ease;
}

.acces-carte.acces-carte-en-mouvement::after {
   width: 100%;
}

/* Les deux faces sont posées à même la page : ni cadre, ni fond, ni ombre. */
.acces-recto,
.acces-verso {
   background: none;
   border: none;
   box-shadow: none;
}

/* Bascule et soumission côte à côte, largeurs égales : la même paire, dans
   le même ordre, sur les deux faces — seuls les libellés s'échangent. */
.acces-actions {
   display: flex;
   gap: .75rem;
}

.acces-actions > .btn {
   flex: 1 1 0;
   min-width: 0;
}

/* Le <label> déguisé en bouton doit s'aligner comme un vrai bouton. */
.acces-actions > .acces-bascule-lien {
   display: inline-flex;
   align-items: center;
   justify-content: center;
   gap: .4rem;
   margin: 0;
}

/* Deux colonnes fixes, identiques sur les deux faces : gauche « Mot de passe
   oublié », centre la bascule, droite « Retour au site ». Le verso n'ayant pas
   de lien à gauche, sa colonne reste vide — mais « Se connecter » tombe au
   même endroit que « S'inscrire », au pixel près, avant comme après la
   rotation. */
.acces-liens {
   display: grid;
   grid-template-columns: 1fr 1fr;
   align-items: center;
   gap: .75rem;
   /* Les liens suivent les boutons de près. La marge automatique qui les
      poussait en bas n'a plus lieu d'être : c'est la paire de boutons, la
      même sur les deux faces, qui sert désormais de repère au regard. */
   margin-top: .85rem;
   font-size: var(--fs-sm);
   white-space: nowrap;
}

.acces-lien-oubli {
   grid-column: 1;
}

/* Les liens vivent maintenant dans le formulaire : la rangée garde son
   écartement vis-à-vis des boutons. */
.acces-actions + .acces-liens {
   margin-top: .85rem;
}

/* Écarts resserrés sur cette page seule (abdAllah : "les ecarts entre les
   btn est trop grand -> 0.3rem max idem pour mobile") — pas de media query,
   la même valeur vaut sur PC et mobile. Ni flex:1 1 0 (.acces-actions >
   .btn) ni grid-template-columns:1fr 1fr (.acces-liens) ne dépendent du
   gap : l'égalité de largeur des 4 boutons reste intacte. */
.page-connexion .acces-actions,
.page-connexion .acces-liens {
   gap: .3rem;
}

.page-connexion .acces-actions + .acces-liens {
   margin-top: .3rem;
}

.acces-lien-retour {
   grid-column: 2;
}

/* Seul de sa rangée — la face inscription n'a pas de « Mot de passe oublié » —
   il occupe toute la largeur et se centre. `:only-child` évite de viser la
   face : la règle vaut partout où le lien se retrouve seul. */
.acces-liens .acces-lien-retour:only-child {
   grid-column: 1 / -1;
   justify-self: center;
}

/* Style « épuré » de front/connexion.php — validé par abdAllah sur mockup
   (coin carré, champs à trait inférieur seul, sans icône affixe) avant tout
   code. Porté par .page-connexion (posée sur ce seul <body>) pour ne
   toucher ni index.php, ni init-bdd.php, ni page-message.php : les trois
   autres pages partagent .page-acces mais gardent leur style .acces-*
   d'origine (input-group + icône, coins arrondis) inchangé. */
.page-connexion .acces-face .form-control,
.page-connexion .acces-face .form-select {
   border-radius: 0;
   border: none;
   border-bottom: 1.5px solid var(--line);
   background: none;
   padding: 0 .7rem;
}

.page-connexion .acces-face .form-control:focus,
.page-connexion .acces-face .form-select:focus {
   background: var(--panel);
}

.page-connexion .acces-face .form-label {
   text-transform: none;
   font-size: .8rem;
   color: var(--slate-dim);
}

.page-connexion .acces-face .btn-primary {
   border-radius: 0;
}

.page-connexion .acces-face .btn-secondary {
   border-radius: 0;
}

/* Champ mot de passe SANS input-group (retiré avec l'icône affixe) : l'œil
   est repositionné en absolu par-dessus le trait inférieur du champ, même
   principe que le mockup validé. */
.page-connexion .acces-champ-mdp {
   position: relative;
}

.page-connexion .acces-champ-mdp .form-control {
   padding-right: 1.6rem;
}

.page-connexion .acces-oeil {
   position: absolute;
   right: 0;
   top: 0;
   margin-top: .5rem;
   margin-right: .5rem;
   background: none;
   border: none;
   padding: 0;
   color: var(--slate-dim);
   font-size: 1.1rem;
   line-height: 1;
}

.page-connexion .acces-oeil:hover,
.page-connexion .acces-oeil:focus-visible {
   color: var(--dominante-color);
}

/* Autoremplissage du navigateur (voir le correctif GLOBAL plus haut dans ce
   fichier, § Formulaires — même mécanisme, box-shadow inset) : sur cette
   page seule, fond de PAGE plutôt que blanc plein, pour rester transparent
   à l'oeil sur le style épuré à trait inférieur — pas de variable dédiée
   déjà adaptée aux 2 thèmes pour ce fond précis (contrairement à --panel),
   donc les 2 valeurs réelles du body sont reprises telles quelles ici. */
.page-connexion input:-webkit-autofill,
.page-connexion input:-webkit-autofill:hover {
   -webkit-box-shadow: 0 0 0 1000px var(--body-bg-light) inset;
   box-shadow: 0 0 0 1000px var(--body-bg-light) inset;
}

[data-theme="dark"] .page-connexion input:-webkit-autofill,
[data-theme="dark"] .page-connexion input:-webkit-autofill:hover {
   -webkit-box-shadow: 0 0 0 1000px #12141c inset;
   box-shadow: 0 0 0 1000px #12141c inset;
}

/* Champ à la fois autorempli ET focus : même fond blanc que le focus normal
   (voir .form-control:focus plus haut), pas le fond transparent ci-dessus —
   la personne est activement en train de le modifier. */
.page-connexion input:-webkit-autofill:focus {
   -webkit-box-shadow: 0 0 0 1000px var(--panel) inset;
   box-shadow: 0 0 0 1000px var(--panel) inset;
}

/* Les deux extrémités du champ mot de passe partagent le fond de l'affixe :
   le bouton œil est un .btn, il ne reprenait donc rien de .input-group-text.

   AUCUNE PORTÉE DE PAGE, et c'est le fond du sujet. Ce raccord a été accroché
   d'abord à .acces-face — la carte retournable de la connexion — puis à
   .acces-scene, son <main>. Les deux fois il a fallu revenir l'élargir, parce
   que le bouton œil n'appartient à aucune page en particulier : il vit dans
   un input-group, partout où un mot de passe se saisit. Quatre pages en
   portent un aujourd'hui (connexion, installation, Paramètres › Base de
   données, Paramètres › Configurations) et la prochaine en héritera sans
   qu'on ait à y penser.

   Le sélecteur décrit donc la SITUATION, pas l'emplacement : un .btn[data-voir]
   dernier enfant d'un .input-group s'aligne sur l'affixe d'en face. Rien à
   ajouter le jour où un cinquième formulaire en aura besoin. */
.input-group > .btn[data-voir] {
   background: var(--dc-12-t, rgba(6, 93, 137, .12));
   border-color: var(--line);
   color: var(--dominante-color);

   /* LA GÉOMÉTRIE AUTANT QUE LA COULEUR. Un raccord de teinte seul laissait
      deux blocs de tailles différentes de part et d'autre du champ : .btn
      n'hérite RIEN de .input-group-text, ni sa hauteur, ni sa largeur
      minimale, ni son rembourrage — et porte en plus une marge basse de 3px
      (voir .btn plus haut, posée pour les boutons en ligne) qui le raccourcit
      de 3px dans un input-group, où les enfants s'étirent sur la hauteur.
      D'où un œil visiblement plus grand, décalé, et sans le carré de l'affixe
      d'en face. Les six valeurs ci-dessous sont EXACTEMENT celles de
      .input-group-text : les recopier ici est le seul moyen de garantir la
      symétrie, une classe utilitaire de plus ne ferait qu'empiler. */
   min-height: var(--btn-h-md);
   min-width: 2.5rem;
   padding: .3rem .6rem;
   margin-bottom: 0;
   font-size: .95rem;
   line-height: 1.4;

   /* Seulement les coins DROITS : Bootstrap met déjà les gauches à zéro pour
      le dernier enfant d'un input-group, les arrondir ici rouvrirait la
      jointure avec le champ. */
   border-top-right-radius: var(--radius-field);
   border-bottom-right-radius: var(--radius-field);
}

.input-group > .btn[data-voir]:hover,
.input-group > .btn[data-voir]:focus-visible {
   background: var(--dc-25-t, rgba(6, 93, 137, .25));
   border-color: var(--line);
   color: var(--dominante-color);
   transform: none;
}

/* Thème sombre : la dominante en avant-plan y devient illisible. Cette règle
   vivait dans le groupe des sélecteurs .acces-* plus bas ; elle en sort pour
   suivre la portée générale ci-dessus — sinon le bouton s'alignerait partout
   en clair, mais garderait sa couleur d'origine en sombre hors des pages
   d'accès. Même jeton que le reste du groupe, --dc-60-w. */
[data-theme="dark"] .input-group > .btn[data-voir],
[data-theme="dark"] .input-group > .btn[data-voir]:hover,
[data-theme="dark"] .input-group > .btn[data-voir]:focus-visible {
   color: var(--dc-60-w, #6b9fba);
}

/* .lien-plat : un <button> qui doit se lire comme un lien. Le formulaire de
   connexion s'en sert pour « Mot de passe oublié », qui déclenche une autre
   action du même formulaire plutôt qu'une navigation.
   :not(.acces-lien-retour) — « Retour au site » (les 2 faces) doit rester
   visuellement IDENTIQUE à « S'inscrire » (couleur/fond/survol) : sans
   cette exclusion, ce sélecteur générique (plus spécifique que .btn-
   secondary seul) écrasait sa couleur et son survol natifs, alors que
   « S'inscrire », posé dans .acces-actions et non .acces-liens, n'est
   jamais concerné par cette règle. */
.acces-liens a:not(.acces-lien-retour),
.acces-liens .lien-plat {
   padding: .4rem .8rem;
   border-radius: var(--radius-field);
   color: var(--dominante-color);
   cursor: pointer;
   transition: background-color .18s ease, color .18s ease;
}

.acces-liens .lien-plat {
   background: none;
   border: none;
   font: inherit;
   text-align: inherit;
}

.acces-liens a:not(.acces-lien-retour):hover,
.acces-liens a:not(.acces-lien-retour):focus-visible,
.acces-liens .lien-plat:hover,
.acces-liens .lien-plat:focus-visible {
   background: var(--dc-12-t, rgba(6, 93, 137, .12));
   color: var(--dominante-color-hover);
}

/* Une rotation peut gêner : on la remplace alors par un simple changement. */
@media (prefers-reduced-motion: reduce) {

   .acces-carte,
   .acces-bascule:checked ~ .acces-carte,
   .acces-verso {
      transition: none;
      transform: none;
   }
}

/* --------------------------------------------------------------------------
   Pages de message (404, compte désactivé, accès refusé)
   Même mise en scène que la page d'accès : logo, carte centrée, paire de
   boutons. Seul le contenu de la carte change.
   -------------------------------------------------------------------------- */

/* Sans la classe .card, la section n'a ni fond ni bordure : c'est le cas des
   pages 404 et accès refusé, qui posent leur message à même la page. */
.acces-message {
   width: 100%;
   max-width: 30rem;
   padding: 2rem 1.5rem;
   text-align: center;
}

/* Même palette que .alert-danger (mêmes jetons Bootstrap) — un message qui
   annonce un blocage réel doit se voir comme tel, pas se fondre dans le
   même fond neutre qu'une page 404 ou un compte désactivé. */
.acces-message-danger {
   background: var(--bs-danger-bg-subtle);
   border: 1px solid var(--bs-danger-border-subtle);
   color: var(--bs-danger-text-emphasis);
}

.acces-message-danger .acces-message-icone {
   color: var(--bs-danger-text-emphasis);
}

/* Icône en tête d'un titre d'accès — l'écran d'installation la place à gauche
   de « Base de données ». Dimensionnée en `em` et non en `rem` : elle suit le
   titre qu'elle accompagne, y compris si --fs-xl change un jour, au lieu de
   se figer à une taille absolue qui finirait par jurer.

   vertical-align négatif plutôt qu'un flex sur le h1 : le titre reste un bloc
   de texte centré comme les autres de la page, sans qu'il faille reprendre
   son alignement. */
.acces-titre-icone {
   margin-right: .35em;
   font-size: 1.7em;
   color: var(--dominante-color);
   vertical-align: -.22em;
}

.acces-message-code {
   font-size: 3.5rem;
   font-weight: var(--fw-bold);
   line-height: 1;
   color: var(--dc-25-w, #b6d0dd);
}

.acces-message-icone {
   display: block;
   margin: .5rem 0 1rem;
   font-size: 3rem;
   color: var(--dominante-color);
}

.acces-message-titre {
   margin: 0 0 .75rem;
   font-size: var(--fs-xl);
   font-weight: var(--fw-bold);
   color: var(--slate);
}

.acces-message-texte {
   margin: 0 auto 1.5rem;
   max-width: 26rem;
   font-size: var(--fs-sm);
   line-height: 1.6;
   color: var(--slate-dim);
}

/* Sur ces pages, la paire de boutons ne remplit pas toute la largeur. */
.acces-message .acces-actions {
   justify-content: center;
}

.acces-message .acces-actions > .btn {
   flex: 0 1 12rem;
}

/* Conditions du mot de passe — la puce passe au vert dès la règle satisfaite.
   Les puces sont dessinées en ::before : une liste sans marqueur natif, dont
   l'icône change d'état sans toucher au balisage. */
.mdp-regles {
   margin: .25rem 0 1rem;
   padding: 0;
   list-style: none;
   font-size: var(--fs-xs);
   line-height: 1.7;
   color: var(--slate-dim);
}

.mdp-regles li {
   display: flex;
   align-items: center;
   gap: .4rem;
}

.mdp-regles li::before {
   content: "";
   flex: 0 0 auto;
   width: .55rem;
   height: .55rem;
   border-radius: 50%;
   border: 2px solid var(--dc-30-w, #b6d0dd);
   transition: background-color .15s ease, border-color .15s ease;
}

.mdp-regles li.regle-ok {
   color: var(--state-success);
}

.mdp-regles li.regle-ok::before {
   background: var(--state-success);
   border-color: var(--state-success);
}

/* ==========================================================================
   BANDEAU DES MESSAGES (flash)
   --------------------------------------------------------------------------
   Posé en haut du contenu, sous le bandeau de titre. Les messages sont
   consommés à l'affichage : ils ne reviennent pas au chargement suivant.
   ========================================================================== */

.flash-zone {
   padding: .75rem 1.5rem 0;
}

/* Entrée par le haut, sortie vers le haut : le mouvement dit d'où vient le
   message et où il repart.

   Les animations ne sont pas posées sur .flash-message directement : le
   balisage arrive avec la page, l'entrée se jouerait pendant le chargement,
   souvent avant que l'écran n'affiche quoi que ce soit. C'est le script qui
   ajoute .flash-entre une fois la page prête. */
@keyframes flash-glisse {
   from {
      opacity: 0;
      transform: translateY(-1.25rem);
   }

   to {
      opacity: 1;
      transform: translateY(0);
   }
}

@keyframes flash-glisse-sortie {
   from {
      opacity: 1;
      transform: translateY(0);
      max-height: 12rem;
   }

   to {
      opacity: 0;
      transform: translateY(-1.25rem);
      max-height: 0;
      margin-bottom: 0;
      padding-top: 0;
      padding-bottom: 0;
   }
}

.flash-message {
   /* Invisible tant que le script n'a pas lancé l'entrée. Le <noscript> du
      bandeau rétablit l'affichage si le script ne s'exécute pas. */
   opacity: 0;
   display: flex;
   align-items: flex-start;
   gap: .7rem;
   margin-bottom: .6rem;
   padding: .8rem 3rem .8rem 1rem;
   border-radius: var(--radius-field);
   font-size: var(--fs-sm);
   line-height: var(--lh-snug);
   /* Le message entier se ferme au clic : le curseur doit le dire. */
   cursor: pointer;
}

.flash-icone {
   flex: 0 0 auto;
   font-size: 1.4rem;
   line-height: 1;
}

.flash-corps {
   min-width: 0;
}

.flash-titre {
   margin: 0;
   font-size: var(--fs-sm);
   font-weight: var(--fw-bold);
   line-height: var(--lh-snug);
}

/* Le texte suit le titre ; sans titre, il occupe seul la ligne. */
.flash-texte {
   margin: .15rem 0 0;
}

.flash-titre:only-child,
.flash-texte:only-child {
   margin-top: 0;
}

/* Le bouton de fermeture de Bootstrap est positionné en absolu : le
   rembourrage droit du message lui réserve sa place. */
.flash-message .btn-close {
   padding: 1rem;
}

@media (max-width: 768px) {
   .flash-zone {
      padding: .6rem 1rem 0;
   }
}

.flash-message.flash-entre {
   animation: flash-glisse .35s cubic-bezier(.22, .68, .36, 1) both;
}

.flash-message.flash-sortie {
   overflow: hidden;
   animation: flash-glisse-sortie .3s cubic-bezier(.55, 0, .68, .3) both;
}

/* Certains préfèrent — ou doivent — se passer d'animations. Le mouvement
   disparaît, le fondu reste : le message ne surgit pas brutalement et rien
   ne glisse à l'écran. */
/* Mouvement réduit : plus de glissement, le fondu suffit à signaler l'arrivée
   et le départ. */
@media (prefers-reduced-motion: reduce) {

   .flash-message.flash-entre,
   .flash-message.flash-sortie {
      animation-duration: .2s;
      animation-timing-function: linear;
   }

   .flash-message.flash-entre {
      animation-name: flash-fondu;
   }

   .flash-message.flash-sortie {
      animation-name: flash-fondu-sortie;
   }

   @keyframes flash-fondu {
      from {
         opacity: 0;
      }

      to {
         opacity: 1;
      }
   }

   @keyframes flash-fondu-sortie {
      from {
         opacity: 1;
      }

      to {
         opacity: 0;
      }
   }
}

/* Sur les pages d'accès, le bandeau s'aligne sur la largeur de la carte :
   une alerte pleine largeur au-dessus d'un formulaire étroit flotte. */
.page-acces .flash-zone {
   width: 100%;
   max-width: 32rem;
   padding: 0 0 .25rem;
}

/* Tableau des permissions : une colonne par statut, une case par croisement.
   Les en-têtes de statut restent lisibles sur écran étroit — le tableau
   défile horizontalement dans son .table-responsive plutôt que de comprimer
   les libellés. */
.table-permissions th {
   font-size: var(--fs-sm);
   font-weight: var(--fw-bold);
   white-space: nowrap;
}

.table-permissions tbody th {
   font-weight: var(--fw-regular);
}

.table-permissions tbody th .ph-duotone {
   margin-right: .4rem;
   color: var(--dominante-color);
}

/* La colonne du superviseur est verrouillée : sa case grisée doit se lire
   comme « acquis », pas comme « indisponible ». */
.table-permissions .form-check-input:disabled {
   opacity: 1;
   background-color: var(--dominante-color);
   border-color: var(--dominante-color);
}

/* ---- Chenillard et mention, écran d'installation ------------------------
   Le scanner de KITT est une rangée de segments FIXES dont la luminosité
   varie : la lumière ne se déplace pas, elle se propage. Et elle fait un
   VA-ET-VIENT — elle revient sur ses pas au lieu de resurgir du même côté.

   POURQUOI PAS UN animation-delay PAR SEGMENT. C'était ma première idée, et
   elle est fausse : décaler 12 segments d'un même pas produit un
   chenillard, pas un rebond. La crête traverse, sort par la gauche, et la
   suivante entre par la droite — la simulation le montre sans ambiguïté. Le
   décalage est monotone, il ne peut pas s'inverser à mi-parcours.

   D'OÙ CE MONTAGE, en trois couches superposées :
     1. le fond du ruban  — les segments « éteints », une teinte faible ;
     2. la lueur          — UN seul élément, un dégradé aux bords fondus, qui
                            fait l'aller-retour par translateX + `alternate` ;
                            un seul élément qui rebondit, donc un vrai rebond,
                            garanti par le moteur et non par une arithmétique
                            de décalages ;
     3. la grille (::after) — des séparateurs de la couleur de la CARTE, posés
                            par-dessus. Ils découpent la lueur continue en
                            12 segments distincts : c'est ce qui donne les
                            carrés dont l'opacité varie, sans qu'aucun carré
                            n'ait à être animé individuellement.

   Le tout suit --dominante-color, et --panel pour les séparateurs : ces deux
   jetons basculent seuls en thème sombre, la grille reste donc invisible sur
   les deux fonds sans une seule règle de plus.

   Seul `transform` est animé — composé par le GPU, sans recalcul de mise en
   page. Et tout est en CSS : une animation qui dépendrait d'un script
   s'arrêterait net si celui-ci échouait. */
.acces-chenillard {
   position: relative;

   /* width: 100% EST INDISPENSABLE, ce n'est pas une redondance avec
      max-width : .acces-message porte aussi .card, dont Bootstrap fait un
      conteneur flex en colonne. Sans largeur explicite, cet enfant retombe
      sur la taille de son contenu — nulle, la lueur étant en position
      absolue — et le ruban reste invisible. flex: 0 0 auto le protège de la
      compression sur l'axe principal. */
   flex: 0 0 auto;
   width: 100%;
   max-width: 18rem;
   height: .4rem;
   margin: 15px auto 30px;
   overflow: hidden;
   background: var(--dc-12-t, rgba(6, 93, 137, .12));
}

/* La lueur. Les bords fondus du dégradé font la traînée : un bloc à bords
   nets donnerait un carré qui glisse, pas une lumière qui se propage. */
.acces-chenillard > span {
   position: absolute;
   top: 0;
   bottom: 0;
   left: 0;
   width: 42%;
   /* --dc-60-w et non --dominante-color : la dominante pure est une teinte
      PROFONDE, et une lueur doit éclairer, pas assombrir. Ce jeton en est une
      version éclaircie — même couleur de charte, mais lumineuse.

      SENS DE L'ÉCHELLE, à ne pas inverser : le nombre monte VERS la
      dominante. --dc-40-w (#9bbed0) est donc plus clair que --dc-60-w
      (#6b9fba), lui-même plus clair que la dominante (#065d89). Monter le
      nombre assombrit. */
   background: linear-gradient(90deg,
         transparent 0%,
         var(--dc-60-w, #6b9fba) 42%,
         var(--dc-60-w, #6b9fba) 58%,
         transparent 100%);

   /* 2.6s : un aller simple. Le cycle complet, aller ET retour, en dure donc
      le double — c'est ce que `alternate` implique, et c'est la durée que
      l'œil perçoit. */
   animation: acces-chenillard 2.6s ease-in-out infinite alternate;
}

/* De −50% à 188.1%, et non de 0 à « collé à droite ». C'est le CENTRE de la
   lueur qui doit atteindre chaque extrémité, pas son bord : sinon la crête
   ne balaie que les segments du milieu et les deux bouts ne s'allument
   jamais vraiment — chez KITT, la lumière touche les deux extrémités. La
   lueur déborde donc de part et d'autre, ce que `overflow: hidden` recadre
   proprement. −50% place son centre sur le bord gauche ; 188.1%, soit
   (100 / 42) × 100 − 50, le place sur le bord droit.

   `alternate` fait le retour, et `ease-in-out` marque le ralentissement à
   chaque extrémité — c'est ce temps d'arrêt qui signe le mouvement de KITT. */
@keyframes acces-chenillard {
   from { transform: translateX(-50%); }
   to   { transform: translateX(188.1%); }
}

/* La grille. Des séparateurs de la couleur de la carte, PAR-DESSUS la lueur :
   c'est en masquant qu'ils dessinent les segments, aucun n'existe en propre.
   top/right/bottom/left séparés plutôt que le raccourci `inset`, plus récent :
   son repli serait une grille sans position, donc un aplat qui couvrirait
   tout le ruban. */
.acces-chenillard::after {
   content: "";
   position: absolute;
   top: 0;
   right: 0;
   bottom: 0;
   left: 0;
   background: repeating-linear-gradient(90deg,
         transparent 0,
         transparent calc(100% / 12 - 2px),
         var(--panel, #fff) calc(100% / 12 - 2px),
         var(--panel, #fff) calc(100% / 12));
}

/* Thème sombre : la lueur doit être PLUS CLAIRE que sur fond blanc, donc un
   nombre PLUS BAS — 40 et non 60. La règle précédente disait « un cran plus
   clair » mais montait le nombre : elle assombrissait la lueur sur le fond le
   plus sombre, exactement l'inverse du besoin. Le projet applique déjà cette
   logique ailleurs (voir .input-group-text, qui passe de la dominante pleine
   à --dc-60-w en sombre). Les séparateurs, eux, suivent --panel et n'ont
   besoin de rien. */
[data-theme="dark"] .acces-chenillard > span {
   background: linear-gradient(90deg,
         transparent 0%,
         var(--dc-40-w, #9bbed0) 42%,
         var(--dc-40-w, #9bbed0) 58%,
         transparent 100%);
}

/* Ce réglage système vise exactement les animations perpétuelles comme
   celle-ci. La lueur reste, immobile au centre, plutôt que de disparaître. */
@media (prefers-reduced-motion: reduce) {
   .acces-chenillard > span {
      animation: none;
      transform: translateX(69.0%);
   }
}

/* Survol des deux boutons de l'écran d'installation. Le fond change déjà, via
   les jetons --bs-btn-hover-bg de .btn-primary et .btn-secondary : ce bloc
   n'y touche pas et AJOUTE le relief, un léger soulèvement et une ombre
   portée. Deux effets qui se lisent ensemble — le fond dit « ce bouton
   réagit », le relief dit « il est cliquable ».

   PORTÉE .page-init-bdd .btn, PLUS .acces-actions. Les deux boutons ont quitté
   cette barre : « Se connecter » est monté dans la rangée du mot de passe,
   « Fichier SQL » est descendu seul sous la note. Accrochée à .acces-actions,
   la règle ne touchait plus rien — le relief avait disparu en silence, sans
   qu'aucune vérification ne s'en plaigne. Elle vise donc désormais les boutons
   de la page, où qu'ils soient posés.

   :not([data-voir]) écarte le seul autre .btn de la page, l'œil du champ mot
   de passe : lui appartient à son input-group et ne doit pas se soulever
   au-dessus du champ auquel il est collé.

   Le vocabulaire est celui déjà employé par .search-group .btn:hover plus
   haut dans ce fichier : soulèvement en translateY + ombre. Reprendre un
   geste existant plutôt qu'en inventer un troisième garde l'interface d'une
   seule main.

   .btn ne porte AUCUNE transition dans ce projet : sans la ligne ci-dessous,
   le soulèvement serait instantané, un sursaut plutôt qu'un mouvement. Elle
   ne nomme que transform et box-shadow — surtout pas `all`, qui animerait
   aussi le fond géré par Bootstrap et le rendrait mou. */
.page-init-bdd .btn:not([data-voir]) {
   transition: transform .18s ease-out, box-shadow .18s ease-out;
}

/* Un bouton d'envoi doit faire la MÊME HAUTEUR que le champ posé à sa
   gauche : sans cela il retombe sur sa hauteur naturelle, environ 31px
   (0.875rem × 1.15 + 2 × 0.375rem + les bordures) contre les 34px que
   .form-control tient de --btn-h-md. Trois pixels d'écart suffisent à faire
   voir deux blocs mal appariés là où on attend une seule ligne.

   Le calage VERTICAL, lui, se fait dans le balisage — une étiquette vide en
   vis-à-vis de « Mot de passe » — et non ici : une marge chiffrée en CSS
   serait à corriger le jour où la taille des étiquettes change. */
/* AUCUNE PORTÉE DE PAGE, pour la même raison que le bouton œil plus haut :
   cette classe décrit une SITUATION — un bouton d'envoi posé à côté d'un
   champ, dans une rangée où les deux doivent se lire comme une seule ligne —
   et non un emplacement. Deux pages l'emploient déjà (init-bdd.php et
   Paramètres › Base de données) ; laissée sous .page-init-bdd, elle n'aurait
   servi que la première, et la seconde aurait retrouvé les 3 px de décalage
   sans que rien ne le signale. */
.acces-envoi {
   min-height: var(--btn-h-md);

   /* .btn porte `margin-bottom: 3px` pour tout le projet — une respiration
      utile aux boutons posés en ligne dans une barre d'outils. Ici elle nuit :
      le bouton est le seul contenu de sa colonne, en vis-à-vis d'un champ qui
      n'a pas cette marge, et ces 3px le décalent vers le haut de la ligne.
      Le champ mot de passe d'en face garde, lui, l'espacement de son
      conteneur .mb-2 : c'est ce dernier qui doit régler l'écart, pas une
      marge de bouton. */
   margin-bottom: 0;
}

/* Même piège, même solution : dernier enfant d'un input-group, #btnTesterSmtp
   hérite les 3px de marge basse de .btn (voir plus haut) qui le raccourcit
   face au champ dont il partage la ligne. */
#btnTesterSmtp {
   min-height: var(--btn-h-md);
   margin-bottom: 0;
}

.page-init-bdd .btn:not([data-voir]):hover,
.page-init-bdd .btn:not([data-voir]):focus-visible {
   transform: translateY(-2px);
   box-shadow: 0 4px 10px rgba(20, 23, 31, .18);
}

/* Le clic REPOSE le bouton : sans cet état, il reste soulevé pendant qu'on
   appuie dessus, et le geste ne rend rien. */
.page-init-bdd .btn:not([data-voir]):active {
   transform: translateY(0);
   box-shadow: none;
}

/* Le fond, lui, continue de répondre : c'est le mouvement qui est retiré,
   pas la réaction au survol. */
@media (prefers-reduced-motion: reduce) {
   .page-init-bdd .btn:not([data-voir]),
   .page-init-bdd .btn:not([data-voir]):hover,
   .page-init-bdd .btn:not([data-voir]):focus-visible,
   .page-init-bdd .btn:not([data-voir]):active {
      transition: none;
      transform: none;
   }
}

/* Mention sous la carte, hors de celle-ci : elle appartient à la page, pas au
   formulaire. */
.acces-copyright {
   margin: 0;
   font-size: var(--fs-xs);
   color: var(--slate);
   text-align: center;
}

/* Aucun soulignement, règle du projet : c'est le fond qui marque le survol. */
.acces-copyright a {
   padding: .1rem .35rem;
   border-radius: var(--radius-field);
   color: var(--dominante-color);
   text-decoration: none;
   transition: background .25s;
}

.acces-copyright a:hover,
.acces-copyright a:focus-visible {
   background: var(--dc-12-t, rgba(6, 93, 137, .12));
   color: var(--dominante-color);
   text-decoration: none;
}

[data-theme="dark"] .acces-copyright a {
   color: var(--dc-60-w, #6b9fba);
}

/* ---- Écran d'installation : s'adapter à la fenêtre, quelle qu'elle soit ---
   init-bdd.php porte plus que la connexion — un titre, un texte, CINQ champs,
   deux boutons et une note. Aux mesures par défaut de la scène d'accès
   l'ensemble dépasse la hauteur utile d'un portable courant, et la page
   défile alors qu'elle doit se lire d'un coup d'œil.

   TOUT EST EN clamp(min, vh, max), JAMAIS EN VALEUR FIXE. Une valeur fixe
   choisie sur un écran donné est juste sur celui-là et fausse partout
   ailleurs : trop serrée sur un grand moniteur, encore trop large sur un
   portable. Ici chaque espacement suit la hauteur de la FENÊTRE — la page
   retrouve toute sa respiration au-delà de ~800 px, se resserre d'elle-même
   en dessous, et le `min` empêche l'écrasement illisible sur un mobile en
   paysage. Aucun seuil à maintenir, aucune machine de référence.

   dvh AVANT vh, et non l'inverse : sur mobile, 100vh compte la barre
   d'adresse qui se rétracte au défilement, donc une hauteur que l'écran
   n'offre pas — c'est précisément ce qui fait apparaître un défilement
   résiduel sur téléphone. Les navigateurs qui ignorent dvh gardent la ligne
   vh au-dessus, écrite en premier comme repli.

   L'icône du titre ne varie pas : c'est un choix d'affichage assumé, pas de
   l'espace à récupérer. Et rien n'est masqué ni tronqué — sous ~480 px de
   hauteur utile, la page défile, ce qui reste le comportement correct : un
   overflow caché escamoterait le bouton d'envoi.

   Porté par .page-init-bdd : la connexion et les pages de message gardent
   exactement leurs mesures. */
.page-init-bdd .acces-scene {
   gap: clamp(.5rem, 2vh, 1.5rem);
   padding: clamp(.5rem, 2vh, 1.5rem) .5rem;
}

.page-init-bdd .acces-logo {
   max-width: clamp(4.5rem, 12vh, 8rem);
}

.page-init-bdd .acces-message {
   padding: clamp(1rem, 3vh, 2rem) 1.5rem;
}

.page-init-bdd .acces-message-titre {
   margin-bottom: clamp(.25rem, 1vh, .75rem);
}

.page-init-bdd .acces-message-texte {
   margin-bottom: clamp(.5rem, 2vh, 1.5rem);
}

/* Ces deux marges venaient d'utilitaires mt-* dans le balisage : une valeur
   fixe de plus, que le CSS n'aurait pu reprendre qu'à coups de !important.
   Retirées du HTML, elles suivent la fenêtre comme le reste.

   La première visait .acces-actions, la barre des deux boutons ; celle-ci
   ayant disparu du balisage, la marge revient à ce qui occupe sa place, le
   bouton « Fichier SQL » désormais seul sous la note. */
.page-init-bdd .init-bdd-sql {
   margin-top: clamp(.5rem, 2vh, 1.5rem);
}

.page-init-bdd .init-bdd-note {
   margin-top: clamp(.5rem, 2vh, 1.5rem);
}

/* ---- Page de connexion en thème sombre --------------------------------- */

/* backend/index.php charge mc-theme : la page d'accès peut donc s'afficher
   en sombre, et sa dominante en avant-plan y devenait illisible. */

[data-theme="dark"] .acces-liens .lien-plat,
[data-theme="dark"] .acces-liens .lien-plat:focus-visible,
[data-theme="dark"] .acces-message-icone,
[data-theme="dark"] .acces-titre-icone,
[data-theme="dark"] .form-check-label .ph-duotone {
   color: var(--dc-60-w, #6b9fba);
}

[data-theme="dark"] .acces-message-texte,
[data-theme="dark"] .mdp-regles {
   color: var(--slate, #8a91a6);
}

/* Même largeur qu'une modal-dialog SANS taille (modal-adulte.php,
   modal-enfant.php) — pour une page autonome qui affiche le même
   formulaire hors modale (add-adulte.php, add-enfant.php) et doit lui
   rester visuellement identique. var(), pas 500px en dur : si la largeur
   de modale par défaut change un jour, celle-ci suit sans y repenser. */
.mc-largeur-modale {
   max-width: var(--bs-modal-width, 500px);
}

/* Timeline d'installation — voir inc/timeline-installation.php. Un fond de
   charte (même dominante que card-header/input-group-text), pour se
   distinguer d'un panneau ordinaire sans pour autant crier « attention » —
   ce n'est pas une erreur, juste une étape qui reste à franchir. */
.mc-timeline-install {
   background: var(--dc-12-t, rgba(6, 93, 137, .12));
   border-radius: var(--radius-field);
   padding: .85rem 1.25rem;
   margin: 0 0 1rem;
   box-shadow: rgb(0 0 0 / 42%) 0px 2px 6px;
}

.mc-timeline-install-titre {
   font-size: var(--fs-xs);
   font-weight: var(--fw-bold);
   color: var(--dominante-color);
   margin: 0 0 .6rem;
}

.mc-timeline-install-etapes {
   display: flex;
   align-items: flex-start;
   list-style: none;
   margin: 0;
   padding: 0;
}

/* Le POURQUOI de l'étape en cours — un seul texte à la fois, jamais les
   quatre à la suite : la timeline montre déjà où on en est, ce cadre se
   contente d'expliquer l'étape qu'il reste à franchir. */
.mc-timeline-install-instructions {
   display: flex;
   align-items: flex-start;
   gap: .6rem;
   margin-top: .9rem;
   padding-top: .85rem;
   border-top: 1px solid var(--dc-20-t, rgba(6, 93, 137, .2));
}

.mc-timeline-install-instructions .ph-duotone {
   color: var(--dominante-color);
   font-size: 1.15rem;
   flex-shrink: 0;
   margin-top: .1rem;
}

.mc-timeline-install-instructions p {
   font-size: var(--fs-sm);
   color: var(--text-color);
   margin: 0;
   line-height: 1.45;
}

/* La ligne reliant deux puces se pose sur l'étape SUIVANTE (border-left),
   pas sur la précédente : la toute première étape n'a alors rien à gauche
   dont elle devrait se distinguer, sans condition ':first-child' séparée. */
.mc-timeline-install-etape {
   display: flex;
   flex-direction: column;
   align-items: center;
   gap: .35rem;
   flex: 1 1 0;
   min-width: 0;
   position: relative;
   padding-top: .55rem;
}

.mc-timeline-install-etape:not(:first-child)::before {
   content: '';
   position: absolute;
   top: calc(.55rem + 1.375rem);
   left: -50%;
   width: 100%;
   height: 2px;
   background: var(--dc-25-k, #d0d7de);
   z-index: 0;
}

.mc-timeline-install-faite:not(:first-child)::before {
   background: var(--dominante-color);
}

/* L'étape déjà validée se revisite : ce lien enveloppe puce + libellé,
   reprenant la même mise en page en colonne que .mc-timeline-install-etape
   elle-même — sans quoi les deux enfants perdraient leur centrage en
   passant sous cet intermédiaire. */
.mc-timeline-install-lien {
   display: flex;
   flex-direction: column;
   align-items: center;
   gap: .35rem;
   text-decoration: none;
   color: inherit;
}

.mc-timeline-install-lien:hover .mc-timeline-install-puce {
   transform: scale(1.08);
   box-shadow: 0 0 0 3px var(--dc-20-t, rgba(6, 93, 137, .2));
}

.mc-timeline-install-lien:hover .mc-timeline-install-libelle {
   color: var(--dominante-color);
}

.mc-timeline-install-puce {
   display: flex;
   align-items: center;
   justify-content: center;
   width: 2.75rem;
   height: 2.75rem;
   border-radius: 50%;
   background: #fff;
   border: 2px solid var(--dc-25-k, #d0d7de);
   color: var(--slate-dim);
   font-size: 1.35rem;
   position: relative;
   z-index: 1;
   flex-shrink: 0;
   transition: transform .15s ease, box-shadow .15s ease;
}

.mc-timeline-install-faite .mc-timeline-install-puce {
   background: var(--dominante-color);
   border-color: var(--dominante-color);
   color: #fff;
}

.mc-timeline-install-active .mc-timeline-install-puce {
   border-color: var(--dominante-color);
   color: var(--dominante-color);
}

.mc-timeline-install-libelle {
   font-size: var(--fs-2xs);
   color: var(--slate-dim);
   text-align: center;
   white-space: nowrap;
   transition: color .15s ease;
}

.mc-timeline-install-active .mc-timeline-install-libelle {
   color: var(--dominante-color);
   font-weight: var(--fw-bold);
}

.mc-timeline-install-faite .mc-timeline-install-libelle {
   color: var(--text-color);
}

/* Mobile : les libellés des étapes NON actives disparaissent — sur un
   écran étroit, quatre libellés côte à côte se chevaucheraient ou
   forceraient un défilement horizontal disgracieux. Seule l'étape en
   cours garde son texte, les puces (avec leur icône) suffisent à situer
   les autres d'un coup d'œil. */
@media (max-width: 480px) {
   .mc-timeline-install-etape:not(.mc-timeline-install-active) .mc-timeline-install-libelle {
      display: none;
   }

   .mc-timeline-install-puce {
      width: 2.1rem;
      height: 2.1rem;
      font-size: 1.05rem;
   }

   .mc-timeline-install-etape:not(:first-child)::before {
      top: calc(.55rem + 1.05rem);
   }
}

[data-theme="dark"] .mc-timeline-install-puce {
   background: var(--panel);
}

[data-theme="dark"] .mc-timeline-install-etape:not(:first-child)::before {
   background: var(--dc-25-w, #3a4250);
}

[data-theme="dark"] .mc-timeline-install-faite:not(:first-child)::before {
   background: var(--dominante-color);
}

/* Réglages des couleurs de fond — voir parametres/configurations.php. */
/* Une ligne de réglage — label à gauche, champ (ou interrupteur) compact à
   droite. Voir parametres/configurations.php, carte Réglages. */
.mc-reglage-ligne {
   display: flex;
   align-items: center;
   justify-content: flex-end;
   gap: .5rem;
   margin-bottom: .25rem;
}

.mc-reglage-champ {
   max-width: 170px;
   font-size: 10pt;
   flex-shrink: 0;
}

.mc-couleur-groupe-titre {
   font-size: var(--fs-2xs);
   font-weight: var(--fw-bold);
   color: var(--slate-dim);
   text-transform: uppercase;
   letter-spacing: .03em;
}

/* Le bouton « Origine » (à droite de chaque titre de groupe) — l'icône
   Phosphor hérite par défaut d'une taille bien plus grande que le texte
   9pt du bouton, ce qui le rendait plus haut que large. Ramenée à la
   taille du texte qui l'entoure. */
.mc-couleur-reinitialiser .ph-duotone {
   font-size: 1em;
}

/* Le TEXTE reste toujours lisible quelle que soit la teinte de fond
   choisie — un contour (text-shadow, pas de couleur unique qui pourrait
   elle-même se fondre dans un fond clair ou sombre) plutôt qu'une seule
   couleur de texte fixe, qui aurait fini par disparaître sur certaines
   teintes. */
.mc-couleur-swatch {
   display: flex;
   align-items: center;
   justify-content: center;
   height: 1.7rem;
   border-radius: 0;
   border: 1px solid var(--line);
   font-size: 8pt;
   font-weight: var(--fw-bold);
   cursor: pointer;
   text-align: center;
   padding: .25rem .4rem;
   transition: transform .1s ease;
   overflow: hidden;
   white-space: nowrap;
   text-overflow: ellipsis;
}

.mc-couleur-swatch:hover {
   transform: scale(1.04);
}

.mc-couleur-swatch:focus-visible {
   outline: 2px solid var(--dominante-color);
   outline-offset: 2px;
}

/* Retire tout arrondi DANS cette carte précisément — ni le cadre lui-même,
   ni le bouton Effacer, ni la boîte « Module non intégré », qui utilisent
   par ailleurs des classes Bootstrap génériques (.rounded, .btn) partagées
   avec le reste du site : cibler .mc-card-couleurs directement évite de
   toucher au style de ces classes ailleurs. */
.mc-card-couleurs,
.mc-card-couleurs * {
   border-radius: 0 !important;
}

/* Jamais affichée elle-même — seul son sélecteur natif (déclenché par
   .click() en JS) doit apparaître, positionné au clic sur une pastille. */
.mc-couleur-flottante {
   position: fixed;
   width: 1px;
   height: 1px;
   padding: 0;
   border: 0;
   opacity: 0;
   pointer-events: none;
}

/* ========================================================================
   CALENDRIER (backend/planifications/calendrier.php) — lecture seule,
   inspiré de FullCalendar.io, quatre vues (année/mois/semaine/jour).
   ======================================================================== */

.mc-calendrier-titre {
   font-size: 1.6rem;
   font-weight: var(--fw-bold);
}

.mc-calendrier-legende {
   display: flex;
   align-items: center;
   flex-wrap: wrap;
   gap: .4rem 1.2rem;
   font-size: var(--fs-sm);
   color: var(--slate);
   margin-bottom: .75rem;
}

.mc-calendrier-puce {
   display: inline-block;
   width: .9rem;
   height: .9rem;
   border-radius: 3px;
   margin-right: -.8rem;
   vertical-align: middle;
}

/* Icônes du GROUPE de navigation (Précédent/Suivant) — sans cette
   réduction, elles héritent de .btn .ph-duotone (1.2rem), bien plus
   grandes que le texte d'« Aujourd'hui » entre les deux, ce qui rendait
   ces boutons plus hauts que le reste de la ligne.
   La restriction à .btn-group est délibérée (abdAllah, 20/08) : le bouton
   « Imprimer » porte la même classe mais n'appartient pas au groupe, il
   est en taille normale et accompagné d'un libellé — son icône reprend
   donc les 1.2rem de tous les autres boutons du projet. */
.btn-group.mc-calendrier-btn-nav .ph-duotone {
   font-size: 1em;
}

/* --- Vue mois : grille --------------------------------------------- */

.mc-calendrier-mois {
   table-layout: fixed;
}

.mc-calendrier-mois th {
   text-align: center;
   font-size: var(--fs-sm);
   background: var(--dc-12-t, rgba(6, 93, 137, .12));
}

.mc-calendrier-mois td {
   height: 6rem;
   vertical-align: top;
   padding: .5rem;
}

.mc-calendrier-quantieme {
   display: inline-block;
   font-weight: var(--fw-bold);
   color: inherit;
   text-decoration: none;
   margin-bottom: .3rem;
}

.mc-calendrier-quantieme:hover {
   text-decoration: underline;
}

.mc-calendrier-hors-mois {
   background: var(--panel-soft, #f4f6f8);
   color: var(--slate);
}

.mc-calendrier-aujourdhui {
   background: var(--dc-12-t, rgba(6, 93, 137, .12));
}

.mc-calendrier-pastille {
   display: block;
   font-size: var(--fs-xs, .8125rem);
   border-radius: 4px;
   padding: .15rem .4rem;
   margin-bottom: .25rem;
   overflow: hidden;
   text-overflow: ellipsis;
   white-space: nowrap;
   text-decoration: none;
   transition: transform .1s ease, box-shadow .1s ease;
}

.mc-calendrier-pastille-priorite {
   display: flex;
   align-items: center;
   justify-content: center;
   flex-shrink: 0;
   width: 1.3rem;
   border-radius: 4px 0 0 4px;
   font-size: .65rem;
   line-height: 1;
   margin-bottom: .25rem;
}

.mc-calendrier-pastille-priorite + .mc-calendrier-pastille {
   border-radius: 0 4px 4px 0;
}

.mc-calendrier-pastille:hover {
   transform: scale(1.03);
   box-shadow: 0 1px 4px rgba(0, 0, 0, .35);
   text-decoration: none;
}

.mc-calendrier-plus {
   display: block;
   font-size: var(--fs-xs, .8125rem);
   color: var(--dominante-color, #065d89);
   text-decoration: none;
}

.mc-calendrier-plus:hover {
   text-decoration: underline;
}

/* --- Vue mois, en dessous de md : liste plutôt que grille ----------- */

.mc-calendrier-liste {
   display: flex;
   flex-direction: column;
   gap: .6rem;
}

.mc-calendrier-jour-liste {
   background: var(--panel-soft, #f4f6f8);
   border: 1px solid var(--line, #dee2e6);
   border-radius: var(--radius-field, .5rem);
   padding: .6rem .8rem;
}

.mc-calendrier-jour-liste-date {
   font-weight: var(--fw-bold);
   font-size: var(--fs-sm);
   margin-bottom: .3rem;
   text-transform: capitalize;
}

/* --- Vue semaine ----------------------------------------------------- */

.mc-calendrier-semaine {
   display: grid;
   grid-template-columns: repeat(7, minmax(9rem, 1fr));
   gap: .6rem;
   overflow-x: auto;
}

.mc-calendrier-semaine-jour {
   background: var(--panel-soft, #f4f6f8);
   border: 1px solid var(--line, #dee2e6);
   border-radius: var(--radius-field, .5rem);
   padding: .6rem;
   min-height: 8rem;
}

.mc-calendrier-semaine-entete {
   display: block;
   font-weight: var(--fw-bold);
   font-size: var(--fs-sm);
   color: inherit;
   text-decoration: none;
   text-transform: capitalize;
   margin-bottom: .4rem;
}

.mc-calendrier-semaine-entete:hover {
   text-decoration: underline;
}

.mc-calendrier-rien {
   font-size: var(--fs-xs, .8125rem);
   color: var(--slate);
   margin: 0;
}

/* --- Vue jour ---------------------------------------------------------- */

.mc-calendrier-jour {
   display: flex;
   flex-direction: column;
   gap: .6rem;
}

.mc-calendrier-jour-carte {
   position: relative;
   border-left: 4px solid;
   background: var(--panel-soft, #f4f6f8);
   border-radius: var(--radius-field, .5rem);
   padding: .7rem 1rem;
}

.mc-calendrier-jour-type {
   font-size: var(--fs-xs, .8125rem);
   text-transform: uppercase;
   color: var(--slate);
   margin-bottom: .15rem;
}

.mc-calendrier-jour-libelle {
   font-weight: var(--fw-bold);
}

.mc-calendrier-jour-periode {
   font-size: var(--fs-sm);
   color: var(--slate-dim, #5b6174);
   margin-top: .2rem;
}

/* --- Vue année : douze mini-mois --------------------------------------- */

.mc-calendrier-annee {
   display: grid;
   grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
   gap: 1rem;
}

.mc-calendrier-mini {
   background: var(--panel-soft, #f4f6f8);
   border: 1px solid var(--line, #dee2e6);
   border-radius: var(--radius-field, .5rem);
   padding: .6rem;
}

.mc-calendrier-mini-titre {
   display: block;
   text-align: center;
   font-weight: var(--fw-bold);
   color: inherit;
   text-decoration: none;
   text-transform: capitalize;
   margin-bottom: .4rem;
}

.mc-calendrier-mini-titre:hover {
   text-decoration: underline;
}

.mc-calendrier-mini-table {
   width: 100%;
   border-collapse: collapse;
   font-size: var(--fs-xs, .8125rem);
   text-align: center;
}

.mc-calendrier-mini-table th {
   color: var(--slate);
   font-weight: var(--fw-regular);
   padding-bottom: .2rem;
}

.mc-calendrier-mini-table td {
   padding: .1rem 0;
}

.mc-calendrier-mini-hors-mois {
   color: var(--line, #dee2e6);
}

.mc-calendrier-mini-table a {
   display: inline-flex;
   align-items: center;
   justify-content: center;
   width: 1.5rem;
   height: 1.5rem;
   border-radius: 50%;
   color: inherit;
   text-decoration: none;
}

.mc-calendrier-mini-marque {
   background: var(--dc-20-t, rgba(6, 93, 137, .2));
   font-weight: var(--fw-bold);
}

.mc-calendrier-mini-aujourdhui {
   background: var(--dominante-color, #065d89);
   color: #fff;
}

/* --- Impression : la barre d'outils s'efface, le calendrier occupe toute
   la feuille, la grille du mois garde ses proportions plutôt que de
   basculer en liste (le média print n'a pas de largeur d'écran mobile).
   Tout le CHROME de la page (navbar, menu déroulant, bandeau de titre,
   messages) s'efface aussi — sans quoi la feuille imprimée reprendrait
   l'intégralité de la page à l'écran, pas seulement le calendrier. */
@media print {
   .mc-calendrier .d-none.d-md-block {
      display: block !important;
   }
   .mc-calendrier .d-md-none {
      display: none !important;
   }

   .navbar,
   #dropdown-menu,
   #horizontal-sidebar,
   .page-header,
   .flash-zone {
      display: none !important;
   }

   .mc-calendrier {
      border: 0;
      box-shadow: none;
      margin: 0;
   }
}

/* Imprimer UNIQUEMENT une section précise de la page (ex. un tableau),
   pas le reste (filtres, boutons, autres cartes) — motif réutilisable,
   projet-wide. Technique CSS classique (visibility, pas display) : garde
   la mise en page, masque juste le rendu, puis fait remonter la section
   ciblée en position absolue au-dessus de tout le reste.

   Usage : poser la classe .section-a-imprimer sur l'élément à imprimer
   (ex. <table class="mc-datatable section-a-imprimer">), et sur le
   bouton : onclick="document.body.classList.add('mc-impression-section'); window.print();"
   .mc-impression-section n'a aucun effet hors de @media print — inutile
   de la retirer après coup. */
@media print {
   body.mc-impression-section * {
      visibility: hidden;
   }
   body.mc-impression-section .section-a-imprimer,
   body.mc-impression-section .section-a-imprimer * {
      visibility: visible;
   }
   body.mc-impression-section .section-a-imprimer {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
   }
}

/* ========================================================================
   TÂCHES (backend/planifications/taches.php) — post-it colorés par
   priorité, pastel dérivé de couleurEclaircie().
   ======================================================================== */

.mc-tache-alerte {
   display: inline-flex;
   align-items: center;
   gap: .3rem;
   font-size: var(--fs-sm);
   font-weight: var(--fw-bold);
}

.mc-tache-postit {
   position: relative;
   display: flex;
   flex-direction: column;
   width: 16.5rem;
   border-radius: 2px;
   padding: .85rem .7rem .5rem;
   box-shadow: rgba(0, 0, 0, .22) 2px 4px 8px;
   transform: rotate(-1.4deg);
   transition: transform .15s ease, box-shadow .15s ease;
}

/* Une inclinaison alternée, jamais toutes du même côté — un vrai post-it
   collé à la main ne s'aligne jamais parfaitement, et encore moins tous
   dans le même sens. */
.mc-tache-postit:nth-child(even) {
   transform: rotate(1.2deg);
}

.mc-tache-postit:nth-child(3n) {
   transform: rotate(-.6deg);
}

.mc-tache-postit:hover {
   transform: rotate(0deg) scale(1.02);
   box-shadow: rgba(0, 0, 0, .3) 3px 6px 14px;
   z-index: 1;
}

.mc-tache-epingle {
   position: absolute;
   top: -.4rem;
   left: 50%;
   transform: translateX(-50%);
   width: 1rem;
   height: 1rem;
   border-radius: 50%;
   box-shadow: 1px 2px 3px 0px rgb(0 0 0 / 65%);
}

.mc-tache-corps {
   flex-grow: 1;
}

.mc-tache-titre {
   font-weight: var(--fw-bold);
   margin-bottom: .3rem;
}

.mc-tache-type {
   font-size: var(--fs-sm);
   color: var(--slate-dim, #5b6174);
   margin-bottom: .2rem;
}

.mc-tache-date {
   font-size: var(--fs-xs, .8125rem);
   color: var(--slate);
}

.mc-tache-retard {
   color: #b3261e;
   font-weight: var(--fw-bold);
}

.mc-tache-note {
   font-size: var(--fs-xs, .8125rem);
   background: rgba(255, 255, 255, .55);
   border-radius: 4px;
   padding: .35rem .5rem;
   margin-top: .4rem;
}

.mc-tache-actions {
   display: flex;
   justify-content: flex-end;
   gap: .3rem;
   margin-top: .6rem;
}

/* Tâche faite : le texte se barre et s'estompe, le cercle se remplit —
   même logique visuelle que .mc-agenda-passe pour un événement terminé. */
.mc-tache-faite {
   opacity: .6;
}

.mc-tache-faite .mc-tache-titre,
.mc-tache-faite .mc-tache-type {
   text-decoration: line-through;
}

/* ========================================================================
   RAPPELS (backend/inc/rappels-widget.php) — sur chaque page d'accueil.
   ======================================================================== */

.mc-rappel-carte {
   position: relative;
   display: flex;
   flex-direction: column;
   width: 15.5rem;
   border-radius: var(--radius-field, .5rem);
   background: var(--panel-soft, #f4f6f8);
   border-left: 3px solid var(--dominante-color, #065d89);
   overflow: hidden;
}

.mc-rappel-corps {
   display: flex;
   flex-direction: column;
   flex-grow: 1;
   gap: .3rem;
   padding: .7rem .8rem;
   color: inherit;
   cursor: pointer;
   transition: background .1s ease;
}

.mc-rappel-corps:hover {
   background: var(--dc-12-t, rgba(6, 93, 137, .12));
   color: inherit;
}

.mc-rappel-corps:focus-visible {
   outline: 2px solid var(--dominante-color);
   outline-offset: 2px;
}

.mc-rappel-pied {
   display: flex;
   align-items: center;
   justify-content: center;
   gap: .35rem;
   padding: .45rem .8rem;
   background: var(--dc-12-t, rgba(6, 93, 137, .12));
   color: var(--dominante-color, #065d89);
   font-size: var(--fs-sm);
   font-weight: var(--fw-bold);
   text-decoration: none;
   cursor: pointer;
   transition: background .1s ease;
}

.mc-rappel-pied:hover {
   background: var(--dc-20-t, rgba(6, 93, 137, .2));
   color: var(--dominante-color, #065d89);
}

.mc-rappel-pied:focus-visible {
   outline: 2px solid var(--dominante-color);
   outline-offset: -2px;
}

.mc-rappel-icone {
   flex-shrink: 0;
   font-size: 1.3rem;
   color: var(--dominante-color, #065d89);
}

.mc-rappel-pastille {
   position: absolute;
   top: .4rem;
   right: .4rem;
   width: 1.4rem;
   height: 1.4rem;
   border-radius: 50%;
   display: flex;
   align-items: center;
   justify-content: center;
   line-height: 1;
   z-index: 1;
}

.mc-rappel-pastille .ph-duotone {
   font-size: .8rem;
   margin-right: 0;
}

.mc-rappel-libelle {
   font-weight: var(--fw-bold);
   min-width: 0;
   overflow: hidden;
   text-overflow: ellipsis;
   white-space: nowrap;
}

.mc-rappel-echeance {
   font-size: var(--fs-xs, .8125rem);
   color: var(--slate);
}

/* Icône « rappel défini » — agenda (à gauche du titre, dans son groupe
   propre pour ne pas perturber le space-between titre/chevron) et post-it
   (en haut à droite, symétrique à l'épingle centrée en haut). */
.mc-agenda-rappel {
   color: var(--dominante-color, #065d89);
   font-size: 1.15em;
   animation: mc-rappel-clignote 1.4s ease-in-out infinite;
}

.mc-tache-rappel {
   position: absolute;
   top: .3rem;
   right: .4rem;
   color: rgba(0, 0, 0, .55);
   font-size: 1.3rem;
   animation: mc-rappel-clignote 1.4s ease-in-out infinite;
}

@keyframes mc-rappel-clignote {
   0%, 100% { opacity: 1; }
   50%      { opacity: .35; }
}

/* ========================================================================
   VACANCES — carte mobile (backend/planifications/vacances-et-conges.php),
   à la place du tableau en dessous de md.
   ======================================================================== */

.mc-vacance-carte {
   border: 1px solid var(--line, #dee2e6);
   border-radius: var(--radius-field, .5rem);
   padding: .7rem .8rem;
   background: var(--panel-soft, #f4f6f8);
}

.mc-vacance-libelle {
   font-weight: var(--fw-bold);
}

.mc-vacance-dates {
   font-size: var(--fs-sm);
   color: var(--slate);
}

.mc-vacance-actions {
   display: flex;
   justify-content: flex-end;
   gap: .35rem;
   margin-top: .6rem;
}

/* MCEditor dans #modalFormation (backend/inc/modal-formation.php) : réduit
   par rapport au défaut de tools.css (max-height: 70vh, pensé pour l'éditeur
   plein document de reglement.php). Ici, quatre éditeurs se partagent des
   onglets DANS une modale déjà bornée par modal-dialog-scrollable — un
   éditeur à 70vh y aurait rarement tenu sans un double défilement gênant.
   Sélecteur scopé plutôt que de baisser la valeur par défaut globale, qui
   servirait mal l'éditeur plein document ailleurs dans le projet. */
#modalFormation .mc-editor-content {
   max-height: 25vh;
}

/* Colonne Actions (toujours la dernière) : padding distinct demandé — les
   boutons Modifier/Supprimer ont besoin de moins d'air que le texte des
   autres colonnes. #tableClasses cible le WRAPPER que MCTable construit à la
   place du <table> d'origine — son id est désormais reporté dessus (voir
   mc-table.js, _readSchema()), sans quoi ce sélecteur ne retrouverait plus
   rien une fois le tableau transformé. */
#tableClasses td:last-child {
   padding: 2px 0 0;
}

/* .nav-tabs : défilement horizontal plutôt que de tasser/wrapper le texte
   des onglets — comportement générique pour TOUS les nav-tabs du projet, pas
   un cas particulier d'une seule page. Sans contenu qui déborde, ces règles
   n'ont visuellement aucun effet sur desktop.
   overflow-x:auto SEUL pose AUSSI overflow-y en implicite (un axe non-visible
   force l'autre à sortir de "visible" — même piège déjà documenté ailleurs
   dans ce fichier et dans tools.css) : d'où overflow-y:hidden explicite,
   sans quoi une barre de défilement verticale apparaît sans raison. */
.nav-tabs {
   flex-wrap: nowrap;
   overflow-x: auto;
   overflow-y: hidden;
   padding-top: 5px;
}

/* Bandeau d'alerte module frauduleux — même famille visuelle que
   .flash-message (icône/corps/titre/texte réutilisés tels quels), mais
   sans son mécanisme d'entrée animée : .flash-message est invisible
   (opacity:0) tant qu'un script ne lui ajoute pas .flash-entre, ce qui
   n'a jamais lieu ici puisque ce bandeau n'a pas ce script — d'où
   « présent mais invisible ». Persistant, pas transitoire : pas besoin
   d'entrer depuis le haut, il est déjà là au chargement de la page. */
.alerte-module-frauduleux {
   display: flex;
   align-items: flex-start;
   gap: .7rem;
   margin-bottom: .6rem;
   padding: .8rem 1rem;
   border-radius: var(--radius-field);
   font-size: var(--fs-sm);
   line-height: var(--lh-snug);
}

/* Badges "Modules inclus" (parametres/configurations.php) — coins carrés,
   pas le border-radius par défaut de .badge Bootstrap. */
.badge-module {
   border-radius: 0;
}

/* Case + texte du bloc "Situation" (front/connexion.php) — flex plutôt
   que le float classique de .form-check : reste vraiment aligné même
   quand le texte est long et s'enroule sur plusieurs lignes (ex. le
   libellé "Externe"), là où float peut faire passer tout le label sous
   la case au lieu de rester à sa droite. .situation-check plutôt que
   .form-check directement : celle-ci sert aussi ailleurs (form-switch
   compris), la retoucher globalement risquerait d'y casser autre chose. */
.situation-check {
   display: flex;
   align-items: center;
   flex-wrap: nowrap;
   gap: .5rem;
}

.situation-check .form-check-label {
   min-width: 0;
}

.situation-check .form-check-input {
   flex-shrink: 0;
   margin-top: .15rem;
}

.situation-check .form-check-label {
   margin: 0;
}

/* #navigoMembre : flèche à gauche au survol des liens, même motif exact
   que .menu-link::before/hover::before (backend.css) — scopé à cette
   classe précise plutôt que réutilisé sur .dropdown-item directement,
   pour ne pas affecter d'autres menus déroulants du projet. */
/* z-index explicite — le dropdown se retrouvait sous une carte d'inscription
   voisine (gestion-des-inscriptions.php) malgré le z-index Bootstrap par
   défaut du .dropdown-menu (1000). Reste sous les modals Bootstrap (1055). */
.mc-navigo-menu {
   z-index: 1500;
   padding: 0;
}

.mc-navigo-menu .dropdown-item {
   position: relative;
   overflow: hidden;
}

.mc-navigo-menu .dropdown-item::before {
   content: "";
   position: absolute;
   right: 0;
   top: 0;
   bottom: 0;
   width: 0;
   background: none;
   transition: all .35s ease;
   z-index: 0;
}

.mc-navigo-menu .dropdown-item:hover::before {
   width: 11px;
   background: var(--dominante-color);
   clip-path: polygon(100% 0, 100% 100%, 0 50%);
}

.mc-navigo-menu .dropdown-item:hover {
   border-left: 5px solid var(--dominante-color);
}

/* Le TEXTE passe en blanc au survol (abdAllah, 07/09) : le `::before` peint le
   fond en dominante, et une entrée colorée — « Supprimer » en rouge — devenait
   illisible dessus.

   `!important` : `text-danger` de Bootstrap le porte déjà. */
.mc-navigo-menu .dropdown-item:hover,
.mc-navigo-menu .dropdown-item:hover i {
   color: #ffffff !important;
}

/* Bouton "Retour à l'Admin" (navbar.php) — clignotant pour rester visible
   tant qu'une bascule de session (basculer-profil-traitement.php) est
   active ; #de2121, même rouge déjà établi ailleurs dans la charte pour
   signaler un état à ne pas ignorer (bannière FICHE EXTERNE). */
.mc-navbar-retour-admin {
   background-color: #de2121;
   color: #ffffff;
   animation: mc-navbar-clignote 1.2s ease-in-out infinite;
}

.mc-navbar-retour-admin:hover {
   color: #ffffff;
}

@keyframes mc-navbar-clignote {
   0%, 100% { opacity: 1; }
   50%      { opacity: .55; }
}

/* Carte-lien d'un membre (backend/inc/carte-membre-lien.php, colonne
   PROFILS de fiche-membre.php) — effet hover demandé par abdAllah. */
.mc-carte-membre-lien {
   transition: box-shadow .2s ease, transform .2s ease;
}

.mc-carte-membre-lien:hover {
   box-shadow: 0 .25rem .5rem rgba(0, 0, 0, .15);
   transform: translateY(-2px);
}

/* abdAllah (19/08) : les icônes de validation Bootstrap (coche verte /
   croix rouge) sont fixées à droite par background-position dans
   bootstrap.css — jamais inversées selon dir="rtl". Sur un champ RTL
   (ex. arabeMatiere), ça place l'icône du mauvais côté par rapport au
   sens de lecture. Repositionnée à gauche uniquement pour ces champs,
   sans toucher au padding (laissé tel quel, pas demandé). */
.was-validated .form-control[dir="rtl"]:valid,
.form-control[dir="rtl"].is-valid,
.was-validated .form-control[dir="rtl"]:invalid,
.form-control[dir="rtl"].is-invalid {
   background-position: left calc(.375em + .1875rem) center;
}

/* abdAllah (19/08) : colonne Actions de matieres-catalogue.php (dernière
   colonne du datatable) trop large en mode tableau classique — même
   piège que la police RTL au-dessus : width n'est qu'une indication en
   table-layout automatique. width:1% + nowrap force le navigateur à ne
   lui laisser que la place nécessaire. Volontairement dans une media
   query min-width:768px : mc-table.js bascule en mode "cartes" en
   dessous (voir .mc-cards plus haut dans tools.css), où chaque <td>
   passe en display:flex width:100% — un style inline aurait cassé cet
   alignement mobile déjà correct, d'où cette règle externe scoping à
   #tableMatieres et seulement au-dessus du breakpoint. */
@media (min-width: 768px) {
   #tableMatieres table.mc-table th:last-child,
   #tableMatieres table.mc-table td:last-child {
      width: 1%;
      white-space: nowrap;
   }
}

/* abdAllah (19/08) : libellé de classe dans matieres-affectations.php —
   passe en gras au survol pour bien signaler que c'est cliquable. */
.mc-lien-classe-affectation:hover {
   font-weight: var(--fw-bold);
}

/* abdAllah (19/08) : bouton matière du tableau matière×période, motif
   IADN — gris (case+coeff désactivées) par défaut, bleu (couleur
   dominante) une fois activé, avec un flip vertical au clic. */
.mc-btn-matiere {
   border: 1px solid var(--bs-border-color);
   background: linear-gradient(180deg, #f1f3f5, #dee2e6);
   color: var(--bs-secondary-color, #6c757d);
   padding: .375rem .75rem;
   border-radius: var(--bs-border-radius);
   text-align: left;
   transform: rotateX(0deg);
   transition: transform .5s cubic-bezier(.4, 0, .2, 1), background .3s ease, color .3s ease;
}

.mc-btn-matiere.active {
   background: linear-gradient(180deg, var(--dominante-color), var(--dominante-color-hover));
   color: #fff;
   border-color: var(--dominante-color);
   transform: rotateX(180deg);
}

/* Le texte tourne avec le bouton (rotateX(180deg) ci-dessus) — contre-
   rotation identique pour l'annuler visuellement (180+180=360=lisible),
   sans perdre l'effet de flip sur le bouton lui-même. */
.mc-btn-matiere-texte {
   display: inline-block;
   transition: transform .5s cubic-bezier(.4, 0, .2, 1);
}

.mc-btn-matiere.active .mc-btn-matiere-texte {
   transform: rotateX(180deg);
}

/* abdAllah (19/08) : cases/coeff. de la ligne d'une matière inactive
   restent techniquement toujours cliquables (plus de disabled HTML — un
   input disabled ne reçoit jamais de clic, donc cocher une case
   n'aurait alors jamais pu activer la matière). Uniquement grisées
   visuellement, pas pointer-events:none. */
.mc-ligne-inactive td {
   opacity: .5;
}

/* abdAllah (19/08) : en-tête du tableau matière×période — même fond que
   .card-header (dominante très diluée) au lieu du fond plein de
   .table-mc-entete (partagée par d'autres tableaux du projet, donc pas
   touchée ici — ciblage via .mc-table-affectations uniquement). Le fond
   étant clair, le texte redevient sombre (color: inherit) au lieu du
   blanc prévu pour un fond plein. */
.mc-table-affectations.table-mc-entete thead th {
   background: var(--dc-12-t, rgba(6, 93, 137, .12)) !important;
   color: inherit !important;
}

/* abdAllah (19/08) : card "Périodes" (matieres-affectations.php) — même
   effet rayé que .table-striped ailleurs dans le projet, mais sur une
   <ul class="list-group"> plutôt qu'un tableau — réutilise la même
   variable Bootstrap pour la cohérence visuelle. */
.mc-list-group-striped .list-group-item:nth-of-type(odd) {
   background-color: var(--bs-table-striped-bg);
}

/* abdAllah (19/08) : texte du select MCSelect (selectClasseAffectation)
   plus grand — ciblé via le conteneur .mc-select-classe-affectation
   plutôt que l'id "mcsel-N-field" généré dynamiquement par mc-select.js
   (incrémental selon le nombre de select sur la page, donc fragile). */
.mc-select-classe-affectation .mc-combo-field {
   font-size: 1.7rem;
}

/* ========================================================================
   NOTIFICATIONS — widget de tableau de bord (22/08)
   ========================================================================
   Une LIGNE par notification, pas une carte de largeur fixe comme
   .mc-rappel-carte : un message fait deux ou trois lignes de texte, une
   largeur imposée le couperait. Prendre toute la largeur disponible rend
   aussi le widget lisible sur téléphone sans media query — c'est le
   conteneur qui rétrécit, le contenu suit.
   ======================================================================== */

.mc-notif-ligne {
   display: flex;
   align-items: flex-start;
   gap: .75rem;
   padding: .6rem .75rem;
   border-radius: var(--radius-field, .5rem);
   background: var(--panel-soft, #f4f6f8);
   border-left: 3px solid var(--dominante-color, #065d89);
}

/* `min-width: 0` : sans lui, un mot long empêche l'élément flex de
   rétrécir et fait déborder toute la ligne sur petit écran. */
.mc-notif-corps {
   flex: 1 1 auto;
   min-width: 0;
}

.mc-notif-titre {
   font-weight: var(--fw-bold, 600);
   line-height: 1.3;
}

.mc-notif-texte {
   font-size: .9rem;
   line-height: 1.35;
}

.mc-notif-date {
   font-size: .8rem;
   color: var(--dc-90-777777, #777);
   margin-top: .15rem;
}

/* Les boutons ne rétrécissent jamais : ce sont des cibles tactiles, elles
   doivent garder leur taille quand le texte pousse. */
.mc-notif-actions {
   display: flex;
   gap: .3rem;
   flex-shrink: 0;
}

/* Sur téléphone, les actions passent SOUS le texte et s'alignent à droite :
   côte à côte, elles ne laissaient qu'une poignée de caractères au message. */
@media (max-width: 575.98px) {
   .mc-notif-ligne {
      flex-direction: column;
      gap: .5rem;
   }

   .mc-notif-actions {
      align-self: flex-end;
   }
}

/* Notification déjà lue : atténuée, pas masquée. Elle garde sa place dans
   l'ordre chronologique — c'est souvent APRÈS l'avoir lue qu'on cherche à
   la retrouver. */
.mc-notif-lue {
   opacity: .62;
   border-left-color: var(--dc-90-777777, #777);
}

/* ========================================================================
   ESPACE ENSEIGNANT — cartes de cours (22/08, revu sur capture)
   ========================================================================
   abdAllah : « élaboré et optimisé pour le format mobile, le plus utilisé ».
   Ces règles sont écrites POUR le téléphone d'abord ; l'écran large n'ajoute
   qu'un `grid-template-columns` en media query.

   Le menu d'actions est un dropdown Bootstrap posé DANS chaque carte
   (backend/inc/navigo-ustad.php) — pas de style propre à écrire ici, il
   reprend `.mc-navigo-menu` déjà utilisé par navigo-membre.php.
   ======================================================================== */

.mc-ustad-entete {
   display: flex;
   flex-wrap: wrap;
   align-items: center;
   gap: .75rem;
   margin-bottom: .75rem;
}

.mc-ustad-titre {
   font-size: 1.1rem;
   font-weight: var(--fw-bold, 600);
   color: var(--dominante-color, #065d89);
   margin: 0;
}

/* UNE colonne par défaut — donc sur téléphone, où les journées s'enchaînent
   verticalement. La grille passe à plusieurs colonnes seulement quand la
   place existe : c'est l'écran large qui est l'exception, pas le petit. */
.mc-ustad-jours {
   display: grid;
   grid-template-columns: 1fr;
   gap: .75rem;
   /* `align-items: start` : sans lui, chaque colonne s'étire à la hauteur de
      la plus longue et ses cartes se retrouvent espacées pour rien. */
   align-items: start;
}

@media (min-width: 768px) {
   .mc-ustad-jours {
      /* `auto-fill` + `minmax` : le nombre de colonnes suit la largeur
         disponible sans qu'on ait à le fixer par palier. */
      grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
   }
}

/* Les cours d'UN MÊME JOUR, empilés dans leur colonne (abdAllah, 23/08). */
.mc-ustad-jour {
   display: flex;
   flex-direction: column;
   /* PAS de `gap` : les cartes portent `mb-2` (abdAllah, 23/08). Cumuler les
      deux doublerait l'écart entre deux cours d'un même jour. */
   min-width: 0;
}

/* PAS d'`overflow: hidden` ici, même s'il arrondirait joliment l'en-tête :
   il ROGNE le menu déroulant, qui déborde forcément de la carte (abdAllah,
   23/08). Les coins sont donc arrondis sur l'en-tête lui-même.
   `isolation: auto` + z-index posé à l'ouverture : une carte ne doit pas
   passer par-dessus le menu d'une carte voisine. */
.mc-ustad-carte {
   position: relative;
   /* TRANSPARENTE au repos (abdAllah, 23/08) : elle n'apparaît qu'au survol,
      où elle prend la dominante. Déclarée quand même, et non ajoutée à ce
      moment-là : elle occupe déjà sa place, sinon la carte se décalerait d'un
      pixel au passage de la souris. */
   border: 1px solid transparent;
   border-radius: var(--radius-field, .5rem);
   /* Fond BLANC (abdAllah, 23/08). `--panel` et non `#fff` en dur : elle vaut
      déjà #ffffff en thème clair et bascule sur #1a1e29 en sombre, où une
      carte blanche serait aveuglante. */
   background: var(--panel, #ffffff);
   min-width: 0;
   box-shadow: rgba(0, 0, 0, .12) 0 1px 3px, rgba(0, 0, 0, .16) 0 1px 2px;
   /* Seules `box-shadow` et `border-color` sont animées : le navigateur les
      traite sans recalculer la mise en page, donc sans saccade sur mobile. */
   transition: box-shadow .18s ease, border-color .18s ease;
}

/* Survol : la carte se soulève légèrement. `:focus-within` en plus, pour que
   la navigation au clavier produise le même repère visuel que la souris. */
/* Survol REPRIS (abdAllah, 23/08 : le premier ne lui plaisait pas). Plus de
   `translateY` : la carte sautait, et dans une colonne serrée le mouvement
   attirait l'œil plus que l'information. Ici elle ne bouge pas — seule
   l'ombre se diffuse et la bordure prend la couleur dominante. L'effet se lit
   sans déplacer quoi que ce soit. */
.mc-ustad-carte:hover,
.mc-ustad-carte:focus-within {
   border-color: var(--dominante-color, #065d89);
   /* Ombre RESSERRÉE (abdAllah, 23/08) : la précédente s'étalait sur 20px et
      empiétait sur les cartes voisines dans une colonne serrée. */
   box-shadow: rgba(0, 0, 0, .16) 0 3px 6px, rgba(0, 0, 0, .20) 0 3px 6px;
}

/* Filet de filière. En ABSOLU plutôt qu'une `border-left` : le survol pose
   `border-color`, qui réécrit les quatre côtés d'un coup — la couleur de
   filière disparaissait alors du bord gauche. */
/* Filet de SALLE. Longe le CORPS seul — il commence sous l'en-tête, qui va
   lui d'un bord à l'autre (schéma d'abdAllah, 23/08).
   À l'intérieur de la bordure de la carte : posé dessus, il la masquait et la
   carte semblait n'être entourée que sur trois côtés au survol. */
.mc-ustad-carte-filet {
   position: absolute;
   top: 0;
   bottom: 0;
   left: 0;
   /* Épaissi de 5 à 7px (abdAllah, 23/08 : « plus grosse mais pas trop ») —
      la couleur de salle se lit d'un coup d'œil sans dominer la carte. */
   width: 7px;
   /* Seul le coin BAS gauche s'arrondit : en haut, le filet bute contre
      l'en-tête et doit rester d'équerre. */
   border-radius: 0 0 0 calc(var(--radius-field, .5rem) - 1px);
}

/* La carte qui porte un menu OUVERT passe au-dessus de ses voisines.
   `:has()` évite d'avoir à poser une classe en JavaScript. */
.mc-ustad-carte:has(.dropdown-menu.show) {
   z-index: 5;
}

.mc-ustad-carte-entete {
   padding: .35rem .75rem;
   font-size: .85rem;
   font-weight: var(--fw-bold, 600);
   text-align: center;
   /* L'en-tête porte lui-même SES DEUX coins hauts : il va d'un bord à
      l'autre (abdAllah, 23/08), et la carte ne peut pas les découper —
      `overflow: hidden` rognerait le menu déroulant. */
   border-radius: calc(var(--radius-field, .5rem) - 1px) calc(var(--radius-field, .5rem) - 1px) 0 0;
}

.mc-ustad-carte-corps {
   /* Référent du filet ET du menu d'actions, tous deux en absolu. */
   position: relative;
   /* Le rembourrage gauche laisse la place au filet, sans quoi le texte
      passerait dessous. */
   padding: .6rem .75rem .6rem calc(.75rem + 7px);
   min-width: 0;
}

/* Menu d'actions en ABSOLU et non flottant : un flottant rétrécit les
   conteneurs flex qui le suivent (ils ne peuvent pas le chevaucher), et la
   ligne du lieu s'arrêtait avant le bord de la carte — le badge « Ouvrir la
   salle » ne pouvait donc pas atteindre la droite (abdAllah, 23/08). */
.mc-ustad-carte-corps > .dropdown {
   position: absolute;
   /* Ajusté à l'œil par abdAllah (23/08), en deux passes : le bouton affleure
      le coin haut droit du corps sans mordre sur l'en-tête ni déborder. */
   top: 1px;
   right: 1px;
   z-index: 2;
}

/* Survol du bouton d'actions, ASSOMBRI (abdAllah, 23/08) : depuis que la
   carte est blanche, le `--panel-soft` d'origine (#eef0f4) ne s'en détachait
   presque plus. `--line` (#e1e4ea) reste discret mais se voit.
   Réglé ICI et non sur `.mc-info-classe-action`, qui est partagé avec
   fonctionnement/classes-de-la-session.php — la retoucher là-bas modifierait
   une page qui n'a rien demandé. */
.mc-ustad-carte-corps > .dropdown .mc-info-classe-action:hover,
.mc-ustad-carte-corps > .dropdown .mc-info-classe-action:focus-visible {
   background: var(--line, #e1e4ea);
}

.mc-ustad-carte-classe {
   font-weight: var(--fw-bold, 600);
   line-height: 1.3;
   /* Place réservée au menu d'actions, qui se superpose à ce coin : sans
      cette marge, un libellé long passerait dessous. Seul le titre en a
      besoin — le menu ne descend pas plus bas. */
   padding-right: 2.5rem;
}

.mc-ustad-carte-ligne {
   display: flex;
   align-items: center;
   gap: .35rem;
   font-size: .85rem;
   margin-top: .2rem;
   min-width: 0;
}

/* Le libellé se tronque, l'icône jamais : elle porte le sens (salle,
   distanciel, avertissement). */
.mc-ustad-carte-ligne i {
   flex-shrink: 0;
   font-size: 1.1rem;
}

/* `flex: 1 1 auto` : le libellé PREND l'espace restant, ce qui repousse le
   badge au bord droit. Sans cela il tronquait en occupant toute la place, il
   ne restait aucun espace libre, et `ms-auto` n'avait rien à distribuer — le
   badge collait au texte (abdAllah, 23/08). */
.mc-ustad-carte-ligne span {
   flex: 1 1 auto;
   overflow: hidden;
   text-overflow: ellipsis;
   white-space: nowrap;
}

/* Badge « Ouvrir la salle » : il ne se comprime jamais, et se distingue au
   survol pour signaler qu'il est cliquable — un badge ne le dit pas de
   lui-même. */
.mc-ustad-lien-salle {
   /* `margin-left: auto` POUSSE le badge au bord droit. Il manquait — je
      l'avais perdu en remplaçant le bouton (qui portait `ms-auto`) par ce
      badge (abdAllah, 23/08). Posé ici plutôt qu'en classe utilitaire dans le
      HTML : le badge ne s'emploie qu'ici, la règle ne peut pas se perdre au
      prochain remaniement. */
   margin-left: auto;
   flex-shrink: 0;
   text-decoration: none;
}

.mc-ustad-lien-salle:hover {
   filter: brightness(1.15);
}

/* Filière, en bas à droite et discrète : elle NOMME ce que la couleur de
   l'en-tête et du filet indique déjà. Atténuée à dessein — c'est un repère,
   pas une information qu'on vient chercher. */
.mc-ustad-carte-filiere {
   text-align: right;
   font-size: .72rem;
   color: var(--dc-90-777777, #777);
   margin-top: .3rem;
   /* Un libellé long ne doit pas pousser la carte : il se tronque. */
   overflow: hidden;
   text-overflow: ellipsis;
   white-space: nowrap;
}

/* ========================================================================
   SUIVI DES LECTURES D'UNE NOTIFICATION (23/08)
   ======================================================================== */

/* Le compteur « lues / destinataires » est un `btn btn-sm` (abdAllah, 23/08).
   Rien à ajouter : Bootstrap lui donne déjà son curseur, son survol et son
   focus. Seule la largeur est fixée, sinon la colonne bougeait d'une ligne à
   l'autre selon que le compteur affiche « 3 / 9 » ou « 12 / 145 ». */
.mc-suivi-compteur {
   min-width: 5rem;
}

.mc-suivi-groupe + .mc-suivi-groupe {
   margin-top: 1rem;
}

.mc-suivi-titre {
   display: flex;
   align-items: center;
   gap: .3rem;
   font-size: .82rem;
   font-weight: var(--fw-bold, 600);
   color: var(--dominante-color, #065d89);
   margin-bottom: .3rem;
}

/* Les icônes suivent la taille réduite du texte, sinon elles dominent la
   ligne (abdAllah, 23/08). */
.mc-suivi-titre i {
   font-size: 1rem;
}

/* Une LIGNE par destinataire : avatar, nom à gauche, horodatage à droite.
   Le fond reprend la couleur du genre, posée en ligne — même code visuel que
   les listings de membres. */
/* Posée SUR un `.list-group-item` : le rembourrage et le fond restent ceux de
   Bootstrap — sauf le fond de genre, mis en ligne, qui l'emporte. */
.mc-suivi-carte {
   display: flex;
   align-items: center;
   gap: .4rem;
   font-size: 1rem;
   min-width: 0;
   /* Bordure de la couleur du PANNEAU : elle sépare deux cartes voisines dont
      les fonds de genre se toucheraient sinon. `--panel` plutôt que `#fff` en
      dur — elle vaut déjà blanc en thème clair et suit le thème sombre, où un
      liseré blanc trancherait violemment. */
   border: 1px solid var(--panel, #ffffff);
}

/* Le nom PREND la place restante, ce qui repousse l'horodatage à droite, et
   se tronque plutôt que d'élargir la colonne. */
.mc-suivi-nom {
   flex: 1 1 auto;
   min-width: 0;
   overflow: hidden;
   text-overflow: ellipsis;
   white-space: nowrap;
}

/* L'horodatage ne se comprime jamais : tronqué, une date ne veut plus rien
   dire. */
.mc-suivi-date {
   flex-shrink: 0;
   font-size: .72rem;
   opacity: .7;
}

/* Calendrier de classe groupé PAR MOIS (23/08) : sur une année scolaire, la
   liste continue dépassait la centaine de dates.
   Les mois se suivent en COLONNES et non empilés (schéma d'abdAllah) : chacun
   garde ses dates les unes sous les autres, et l'on compare deux mois d'un
   coup d'œil. */
.mc-classe-calendrier-mois-liste {
   display: grid;
   grid-template-columns: 1fr;
   gap: .75rem;
   /* La hauteur fixe passe ICI : elle était sur `.mc-classe-calendrier`, qui
      ne contient plus qu'un mois — chaque bloc aurait alors défilé
      séparément. */
   max-height: 600px;
   overflow-y: auto;
}

@media (min-width: 576px) {
   .mc-classe-calendrier-mois-liste {
      /* `auto-fill` + `minmax` : le nombre de colonnes suit la largeur
         disponible, sans palier à fixer. `start` pour qu'un mois court ne
         s'étire pas à la hauteur du plus rempli. */
      grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
      align-items: start;
   }
}

.mc-classe-calendrier-mois-titre {
   display: flex;
   align-items: center;
   gap: .4rem;
   font-weight: var(--fw-bold, 600);
   color: var(--dominante-color, #065d89);
   border-bottom: 1px solid var(--line, #e1e4ea);
   padding-bottom: .2rem;
   margin-bottom: .4rem;
}

/* Le nombre de séances du mois, discret : c'est un repère, pas l'information
   principale. */
.mc-classe-calendrier-mois-nb {
   font-size: .75rem;
   font-weight: var(--fw-regular, 400);
   opacity: .65;
   /* Poussé à droite du titre, comme sur le schéma. */
   margin-left: auto;
}

/* DANS un bloc de mois, les dates s'empilent : la grille à 4 colonnes de
   `.mc-classe-calendrier` n'a plus lieu d'être, et sa hauteur fixe est
   remontée sur la liste des mois. */
.mc-classe-calendrier-mois .mc-classe-calendrier {
   grid-template-columns: 1fr;
   height: auto;
   overflow: visible;
}

/* ========================================================================
   ICÔNES D'ACTION « INFOS » (23/08)
   ========================================================================
   Modèle de référence : inscriptions/effectifs-des-classes.php.
   Une classe partagée plutôt qu'un `style=` répété sur chaque icône — elles
   étaient une douzaine, à des tailles légèrement différentes.
   ======================================================================== */

.mc-icone-infos {
   font-size: 1.3rem;
   /* Couleur de la CHARTE au repos (abdAllah, 23/08), et non `inherit` :
      l'icône avait autant de couleurs que de contextes — héritée du fond de
      filière ici, du texte du tableau là. Elle se reconnaît maintenant partout
      à la même teinte. */
   color: var(--dominante-color, #065d89);
   transition: color .15s ease;
}

/* Un BOUTON qui ne porte que cette icône se comporte comme un lien : ni fond,
   ni cadre, ni rembourrage (abdAllah, 23/08). Réglé ici plutôt qu'en ajoutant
   une classe au bouton — l'icône suffit à le désigner. */
button:has(> .mc-icone-infos) {
   background: none;
   border: none;
   padding: 0;
   line-height: 1;
}

/* `dropdown-toggle` ajoute un chevron : masqué quand le bouton n'est
   qu'une icône. */
button:has(> .mc-icone-infos)::after {
   display: none;
}

/* COULEUR D'ALERTE au survol, POUR TOUTES LES ITÉRATIONS (abdAllah, 23/08) :
   le sélecteur ne dépend plus de la balise porteuse — `:hover` directement sur
   l'icône couvre aussi bien un <a>, un <button> qu'un futur autre porteur.
   `:focus-visible` sur le parent reste nécessaire : c'est LUI qui reçoit le
   focus clavier, jamais l'icône.

   `--mc-alerte` est CALCULÉE par mc-theme.js : rouge d'ordinaire, mais bleue
   si la dominante de l'établissement est elle-même rouge — sans quoi le survol
   serait invisible. */
.mc-icone-infos:hover,
a:focus-visible > .mc-icone-infos,
button:focus-visible > .mc-icone-infos {
   color: var(--mc-alerte, #dc3545);
}

/* Entretien de la base (23/08) : une ligne par table, séparées d'un filet
   pour distinguer les blocs quand chacun porte deux rangs — le libellé et la
   purge par période. */
.mc-entretien-ligne + .mc-entretien-ligne {
   margin-top: .5rem;
   padding-top: .5rem;
   border-top: 1px solid var(--line, #e1e4ea);
}

/* Champ date suivi d'un bouton (23/08) : un `input[type=date]` natif ne se
   mesure pas comme un champ texte — le navigateur y insère ses propres
   commandes et lui impose sa hauteur, supérieure à celle que Bootstrap donne
   au bouton voisin. On aligne les deux sur une hauteur explicite plutôt que de
   laisser chacun décider. */
.mc-groupe-date > .form-control,
.mc-groupe-date > .input-group-text,
.mc-groupe-date > .btn {
   height: 31px;
   display: inline-flex;
   align-items: center;
}


/* ========================================================================
   CARTES DE PROFIL — espaces membres (24/08)
   ========================================================================
   La carte est celle du staff (`.mc-carte-membre-lien`), reprise sans son
   lien : rien à redéfinir ici, seulement ce que le menu d'actions ajoute.
   ======================================================================== */

/* Le menu vit maintenant DANS la ligne d'identité (24/08) : plus de
   positionnement absolu, c'est le flex qui le place. Reste à neutraliser le
   `float-end` du navigo, qui n'a plus lieu d'être et décalerait le menu. */
.mc-carte-membre-lien .dropdown {
   float: none;
}

/* MENU AU-DESSUS DES CARTES VOISINES (abdAllah, 24/08).

   `transform: translateY(-2px)` au survol — l'effet de la carte-lien — crée un
   CONTEXTE D'EMPILEMENT : le menu déroulant, même en `z-index` élevé, reste
   alors enfermé dans sa carte et passe sous les suivantes.

   La carte qui porte un menu OUVERT est donc remontée : `:has()` la désigne
   sans qu'aucun script n'ait à poser de classe. Même remède que pour les
   cartes de cours de l'espace enseignant, le 23/08. */
.mc-carte-membre-lien:has(.dropdown-menu.show) {
   z-index: 5;
}

/* La colonne de grille ferme elle aussi le passage : c'est ELLE qui empile les
   cartes voisines, remonter la carte seule ne suffirait pas. */
.col:has(.mc-carte-membre-lien .dropdown-menu.show) {
   position: relative;
   z-index: 5;
}

/* Et le `transform` est SUSPENDU tant que le menu est ouvert : un contexte
   d'empilement enferme son contenu quoi qu'on fasse du `z-index`, aucune
   valeur ne l'en fait sortir. La carte perd son soulèvement de 2px pendant ce
   temps — invisible, on regarde le menu. */
.mc-carte-membre-lien:has(.dropdown-menu.show):hover {
   transform: none;
}

/* Demande de suppression en cours : signalée sans crier — c'est une attente,
   pas une erreur. LIGNE À PART sous l'identité (abdAllah, 24/08) : en absolu,
   elle passait sous l'identifiant et devenait invisible. */
.mc-carte-membre-suppression {
   font-size: .75rem;
   font-weight: var(--fw-bold, 600);
   text-align: center;
   color: var(--bs-danger, #dc3545);
}

/* L'icône reste lisible à côté d'un texte en .75rem, sans le dominer. */
.mc-carte-membre-suppression > i {
   font-size: 1rem;
   margin-right: .3rem;
}

/* Grille des profils — espaces membres (24/08).
   `auto-fill` avec un maximum de 22rem : la carte ne dépasse jamais une
   largeur de téléphone, et le navigateur en place autant que l'écran permet.
   Un `row-cols-4` figeait le compte et étirait les cartes sur grand écran. */
.mc-profils-grille {
   display: grid;
   grid-template-columns: repeat(auto-fill, minmax(16rem, 22rem));
   gap: .75rem;
}

/* Sous 22rem d'écran utile, une seule colonne qui prend toute la largeur —
   sans quoi la carte déborderait. */
@media (max-width: 26rem) {
   .mc-profils-grille {
      grid-template-columns: 1fr;
   }
}

/* Inscriptions dans une carte de profil — espaces membres (24/08).
   Un bloc par inscription, empilés sous l'identité. */
.mc-carte-inscription {
   border: 1px solid var(--line, #e1e4ea);
   border-radius: var(--radius-field, .5rem);
   background: var(--panel, #fff);
   /* PAS d'`overflow: hidden` (abdAllah, 25/08) : les rebords du ruban
      « Désistée » débordent du bloc par construction — c'est ce qui leur donne
      leur relief — et se trouvaient rognés.
      Les coins hauts de l'en-tête de filière sont arrondis par sa propre
      règle, plus bas : l'`overflow` n'était pas nécessaire pour cela. */
}

/* En-tête aux couleurs de la filière : pleine largeur, coins hauts arrondis
   par l'`overflow: hidden` du parent — même motif que les cartes de cours de
   l'espace enseignant. */
.mc-carte-inscription-filiere {
   /* Coins hauts arrondis ICI, puisque le parent n'a plus d'`overflow`. */
   border-radius: calc(var(--radius-field, .5rem) - 1px) calc(var(--radius-field, .5rem) - 1px) 0 0;
   padding: .2rem .5rem;
   font-size: .7rem;
   font-weight: var(--fw-bold, 600);
   text-transform: uppercase;
   letter-spacing: .02em;
}

.mc-carte-inscription-corps {
   padding: .35rem .5rem;
}

.mc-carte-inscription-classe {
   font-weight: var(--fw-bold, 600);
   font-size: .85rem;
   line-height: 1.25;
}

.mc-carte-inscription-jours {
   font-size: .75rem;
   color: var(--slate-dim, #6c757d);
   margin-top: .1rem;
}

.mc-carte-inscription-jours > i {
   font-size: .9rem;
   margin-right: .25rem;
}

/* État d'une inscription sur une carte de profil (25/08) : « En liste
   d'attente », « En attente de validation ». Une inscription validée n'affiche
   rien — c'est le cas normal. */
/* L'état SOUS les jours, aligné à droite (25/08) : sur leur ligne, un libellé
   long comprimait la liste des jours. */
.mc-carte-inscription-etat {
   margin-top: .25rem;
   text-align: right;
}

.mc-carte-inscription-etat > .badge {
   font-size: .65rem;
   font-weight: var(--fw-bold, 600);
}

/* Icône de l'état d'inscription : à gauche du libellé, un peu plus grande que
   le texte du badge sans le dominer (25/08). */
.mc-carte-inscription-etat > .badge > i {
   font-size: .85rem;
   margin-right: .25rem;
   vertical-align: -.1rem;
}

/* Contrôle des profils (25/08) : la ligne du contrôle ouvert est repérée par
   un filet à gauche, aux couleurs de la charte. */
.mc-controle-actif {
   border-left: 3px solid var(--dominante-color, #065d89);
   padding-left: .5rem !important;
}

.mc-controle-icone {
   font-size: 1.5rem;
   flex-shrink: 0;
}

/* Hôte de toasts DANS la bande de titre (25/08) : le `.mc-toast-host` global
   est en position fixe en haut à droite — il recouvrait la barre et partait au
   bout de quelques secondes. Ici l'alerte reste tant qu'elle n'est pas traitée.

   Le conteneur ne réserve aucune place quand il est vide : `:empty` le retire
   du flux, sinon il creuserait un blanc au milieu de la bande. */
.mc-toast-host-header {
   position: static;
   display: flex;
   flex-wrap: wrap;
   gap: .5rem;
   margin: 0;
   max-width: none;
}

.mc-toast-host-header:empty {
   display: none;
}

/* Le toast de la bande est plus compact que celui qui flotte : il partage la
   ligne avec le titre et l'heure. */
.mc-toast-header {
   margin: 0;
   /* Rembourrage bas plus généreux : la jauge se pose en absolu sur le bord,
      et .4rem la faisait chevaucher le texte (abdAllah, 25/08). */
   padding: .4rem .75rem .6rem;
   font-size: var(--fs-sm);
   cursor: pointer;
}

.mc-toast-header .mc-toast-text strong {
   font-size: var(--fs-sm);
}

/* Les toasts de la bande s'EMPILENT au lieu de se superposer : l'hôte est en
   flex, chacun garde sa place. `flex: 0 1 auto` — ils se compriment si la
   bande manque de largeur, plutôt que de la faire déborder. */
.mc-toast-host-header > .mc-toast {
   position: relative;
   flex: 0 1 auto;
   min-width: 0;
}

/* L'hôte FLOTTANT descend sous la bande (abdAllah, 25/08) : il se cale sur
   `--navbar-h` seule, donc pile sur elle — un toast de confirmation recouvrait
   l'alerte des demandes de suppression.

   `--page-header-h` est mesurée au chargement par inc/page-header.php : la
   bande a une hauteur variable, selon qu'elle porte un sous-titre, une alerte
   ou rien. Repli à 0 sur les pages sans bande — l'écran de connexion, par
   exemple. */
.mc-toast-host:not(.mc-toast-host-header) {
   top: calc(var(--navbar-h, 60px) + var(--page-header-h, 0px) + .75rem);
}

/* Bilan d'une fusion de profils (25/08) : trois compteurs de ce que
   l'opération emporte, lus avant d'arbitrer les champs. */
.mc-fusion-bilan {
   display: flex;
   align-items: center;
   gap: .6rem;
   padding: .6rem .75rem;
   border: 1px solid var(--line, #e1e4ea);
   border-radius: var(--radius-field, .5rem);
   background: var(--panel, #fff);
   height: 100%;
}

.mc-fusion-bilan > i {
   font-size: 1.6rem;
   color: var(--dominante-color, #065d89);
   flex-shrink: 0;
}

/* Les inscriptions en double méritent l'œil : ce sont les seules que la fusion
   écarte au lieu de déplacer. */
.mc-fusion-bilan-alerte {
   border-color: var(--bs-warning, #ffc107);
}

.mc-fusion-bilan-alerte > i {
   color: var(--bs-warning, #ffc107);
}

/* Fusion de profils — colonne marquée « Supprimer » (25/08). La fiche entière
   se barre : on doit voir d'un coup d'œil laquelle va disparaître, pas
   seulement l'interrupteur en bas du tableau. */
.mc-fusion-supprime {
   background: var(--state-error-bg, #fdf0f0) !important;
   text-decoration: line-through;
   opacity: .7;
}

/* La ligne des interrupteurs ne se barre jamais : c'est elle qui commande. */
.mc-fusion-decision .mc-fusion-supprime {
   text-decoration: none;
   opacity: 1;
}

.mc-fusion-switch .form-check-input {
   cursor: pointer;
}

.mc-fusion-switch .form-check-label {
   cursor: pointer;
   font-weight: var(--fw-bold, 600);
}

/* Sélecteur d'absorption masqué sous une fiche qui s'en va (25/08) :
   `visibility` et non `display` — la cellule garde sa place, sinon la ligne
   se décale et les colonnes ne s'alignent plus. */
.mc-fusion-masque > .mc-fusion-cible {
   visibility: hidden;
}

/* Badge d'un profil rattaché, au fond de son genre (25/08).
   Les couleurs de `GENRES` sont PÂLES — pensées comme fond de carte : le texte
   blanc par défaut du badge y serait illisible, d'où la couleur sombre et la
   bordure qui le détache du fond de cellule. */
.mc-badge-genre {
   color: var(--ink, #1b2536);
   border: 1px solid rgba(0, 0, 0, .12);
   font-weight: var(--fw-normal, 400);
}

/* Badge d'un profil rattaché, comparatif de fusion (25/08). En ligne dans une
   colonne étroite, les noms se chevauchaient : un par ligne, le nom tronqué
   plutôt que débordant, l'âge poussé à droite. */
.mc-badge-rattache {
   display: flex;
   align-items: center;
   width: 100%;
   min-width: 0;
   text-align: left;
}

/* `min-width: 0` sur l'enfant : sans lui, `text-truncate` ne s'applique pas
   dans un conteneur flex — le texte force sa largeur au lieu de se couper. */
.mc-badge-rattache > span {
   min-width: 0;
}

/* L'âge reste lisible sur un fond pastel : `opacity` le faisait disparaître. */
.mc-badge-rattache > small {
   color: var(--slate-dim, #55607a);
}

/* Interrupteur de décision centré dans sa cellule (25/08) : `form-check` est
   en bloc, `justify-content` n'a d'effet qu'en flex. */
.mc-fusion-switch {
   display: flex;
   align-items: center;
   gap: .5rem;
   padding-left: 0;
}

/* Bootstrap décale l'input de `form-check` par une marge négative, prévue pour
   la puce du bloc — en flex elle sort du centrage. */
.mc-fusion-switch .form-check-input {
   margin-left: 0;
   float: none;
}

/* Inscription désistée dans le comparatif de fusion (25/08) : barrée, comme
   partout ailleurs dans le projet — elle compte encore pour l'historique mais
   n'est plus active. */
.mc-badge-desiste {
   text-decoration: line-through;
   opacity: .65;
}

/* Badge « M » d'un module, sur les cartes d'inscription (25/08).
   Repris de `.mc-combo-badge` du sélecteur — dimensions ET couleurs — pour
   qu'un module se reconnaisse pareil partout, y compris sur un fond de
   filière coloré. */
.mc-badge-module {
   flex: 0 0 auto;
   min-width: 1.35rem;
   padding: 0 .25rem;
   border-radius: .25rem;
   background: var(--dominante-color);
   color: #fff;
   font-size: .7rem;
   font-weight: var(--fw-bold);
   line-height: 1.35rem;
   text-align: center;
   cursor: default;
}

/* Jauge de remplissage d'une classe (effectifs-des-classes, 25/08).
   La barre et sa valeur se superposent : un pourcentage seul se lit, une barre
   se voit — les deux ensemble se comprennent sans effort. */
.mc-jauge-taux {
   position: relative;
   height: 1.15rem;
   /* Fond SOUTENU et non un gris à 6 % : la valeur est blanche, et sur un taux
      faible elle repose surtout sur ce fond-là, pas sur la barre. */
   background: var(--slate-dim, #55607a);
   border-radius: .25rem;
   overflow: hidden;
}

.mc-jauge-taux-barre {
   position: absolute;
   inset: 0 auto 0 0;
   display: block;
   transition: width .3s ease;
}

/* La valeur passe PAR-DESSUS la barre, centrée : à droite elle sortait du
   cadre sur les taux faibles, à gauche elle disparaissait sur les forts.
   `text-shadow` : le texte reste lisible sur les deux fonds qu'il traverse. */
.mc-jauge-taux-valeur {
   position: relative;
   z-index: 1;
   display: block;
   line-height: 1.15rem;
   font-weight: var(--fw-bold);
   font-size: .7rem;

   /* BLANC : les trois fonds que la valeur traverse — vert, orange, rouge —
      sont assez soutenus pour le porter.

      `!important` : la cellule peut porter une couleur de texte héritée d'un
      fond de filière ou de mode, dont la valeur prendrait la teinte. Ici le
      fond est toujours la jauge, jamais celui de la ligne. */
   color: #fff !important;
}



/* Récapitulatif du foyer, onglet Comptabilité (26/08).
   `width: auto` contre le `width: 100%` de Bootstrap : trois montants tiennent
   dans leur largeur, et le tableau se cale à droite sous les cartes.
   Fond clair de la charte : il se détache des cartes sans jurer avec elles. */
.mc-compta-recap {
   /* `!important` : `.table { width: 100% }` de Bootstrap a la même
      spécificité — une classe — et il est chargé APRÈS. Sans lui, la règle
      `50%` du grand écran ne passerait pas. */
   /* PLEINE LARGEUR sur mobile, MOITIÉ sur grand écran (abdAllah, 26/08) :
      serré, le récap se lit mieux — mais sur un écran étroit il n'y a pas la
      place de le serrer, et le réduire le rendait illisible. */
   width: 100% !important;
   max-width: 100%;

   /* Même repli que les autres usages de `--dc-10-w` dans ce fichier. */
   background: var(--dc-10-w, #e6eff3);
}

/* PLEINE LARGEUR sous `md` (abdAllah, 26/08) : serré à droite sur un écran
   étroit, le récapitulatif laissait une bande vide à sa gauche et se lisait
   mal. Il n'a plus de voisin à ce format, autant occuper la place. */
@media (max-width: 767.98px) {
   .mc-compta-recap {
      width: 100% !important;
   }
}

@media (min-width: 992px) {
   .mc-compta-recap {
      width: 50% !important;
   }
}

/* Les cellules héritent du fond de LEUR LIGNE : sans cela, le blanc de
   `.table` les recouvrirait et le fond ne se verrait qu'aux bordures. */
.mc-compta-recap > tbody > tr > td,
.mc-compta-recap > tfoot > tr > td {
   background: transparent;
}

/* Trois fonds pour trois natures : le sous-total en dominante claire, les
   remises en blanc — elles se retranchent —, le total sur `grady-light`.
   Le lecteur suit ainsi la descente vers le montant qui compte. */
.mc-compta-recap .mc-recap-soustotal {
   background: var(--dc-10-w, #e6eff3);
}

.mc-compta-recap .mc-recap-remises {
   background: #ffffff;
}

/* TOTAL DÛ sur `grady-light` (abdAllah, 26/08) : le fond dominante plein
   écrasait le récapitulatif, et le dégradé clair suffit à le détacher. */
.mc-compta-recap .mc-recap-total > td {
   /* Le dégradé vient de `.grady-light`, posée sur la ligne : le recopier ici
      aurait fait deux sources pour la même teinte. Seul le liseré reste. */
   border-top: 2px solid var(--dominante-color);
}

/* En-tête des tableaux de cotisations en dominante CLAIRE : le fond plein de
   `table-mc-entete` écrasait la carte, déjà colorée par le bloc du membre. */
.mc-table-cotisations.table-mc-entete thead th {
   background: var(--dc-10-w, #e6eff3) !important;
   color: var(--dominante-color, #065d89) !important;
}

/* Le libellé pousse le montant à droite, sans largeur fixe : « Remises et
   réductions » et « Sous-total » n'ont pas la même longueur. */
.mc-compta-recap td:first-child {
   padding-right: 2rem;
   white-space: nowrap;
}

/* Bloc du membre, onglet Comptabilité (26/08).
   9rem seulement à partir de `md`, où il est en colonne à gauche du tableau.
   Sous ce seuil il passe au-dessus, en pleine largeur : lui imposer une
   largeur minimale l'empêchait de se réduire et faisait déborder la carte. */
@media (min-width: 768px) {
   .mc-compta-membre {
      /* 9rem était taillé pour un nom ET un prénom ; il ne reste que le
         prénom, la colonne peut se resserrer d'autant. */
      min-width: 5.5rem;
      max-width: 7rem;
   }
}

/* Cellules des tableaux de comptabilité (26/08).
   `nowrap` : un montant coupé en deux lignes se lit mal, et « 1 250,00 »
   passait à la ligne sur sa propre espace insécable dès que la colonne
   serrait. Les colonnes de texte gardent leur `text-truncate`. */
.mc-compta-recap td,
.mc-compta-recap th,
.mc-compta-cout,
.mc-compta-net,
.mc-compta-total-net,
.mc-compta-total-remises {
   white-space: nowrap;
}

/* Les cellules laissent voir le fond de LEUR LIGNE : sans cela, `.table` les
   peint en blanc et les fonds du récap ne se verraient qu'aux bordures.
   `--bs-table-bg-type` est la variable de Bootstrap 5.3 — `accent-bg` a
   disparu dans cette version. */
.mc-compta-recap > tbody > tr > td,
.mc-compta-recap > tfoot > tr > td {
   /* Bootstrap 5.3 peint la cellule par un `box-shadow` interne, pas par
      `background` : le neutraliser est le seul moyen de laisser voir le fond
      de la ligne. */
   box-shadow: none;
   background-color: transparent;
}

/* En-tête COLLANT du tableau des versements (abdAllah, 07/09) : son conteneur
   est limité en hauteur, et les colonnes devenaient illisibles dès qu'on
   faisait défiler. `table-mc-entete` peint déjà le fond, sans quoi les lignes
   passeraient au travers. */
.mc-table-scroll thead th {
   position: sticky;
   top: 0;
   z-index: 2;
}

/* ========================================================================
   TABLEAUX AJUSTÉS À LEUR CONTENU (abdAllah, 07/09) — suivi des plans et
   synthèse des cotisations.
   ======================================================================== */

/* Ces deux listes comptent une dizaine de colonnes, là où les profils en ont
   quinze : étirées sur tout l'écran, leurs cellules se retrouvaient séparées
   par de larges vides.

   `width: max-content` mesure le contenu réel ; `max-width: 100%` empêche le
   débordement quand il dépasse la fenêtre, et `margin: auto` centre le reste
   du temps.

   La classe est posée sur la PAGE et non sur `.mc-table-wrapper` seul : le
   composant sert aussi les listes de profils, qui gardent leur pleine
   largeur. */
.mc-table-ajustee .mc-table-wrapper {
   width: max-content;
   max-width: 100%;
   margin-left: auto;
   margin-right: auto;
}

/* `table-layout: auto` : sans lui, le tableau reprend la largeur de son
   enveloppe et l'ajustement n'a plus d'effet. */
.mc-table-ajustee .mc-table-wrapper table {
   width: auto;
   table-layout: auto;
}
