:root {
    /* Bootstrap palette overrides - SGE Theme */
    --bs-primary: #2c3e50;
    --bs-primary-rgb: 44, 62, 80;
    --bs-secondary: #34495e;
    --bs-secondary-rgb: 52, 73, 94;
    --bs-info: #1566a8;
    --bs-info-rgb: 21, 102, 168;
    /* NE PAS surcharger --bs-tertiary-bg ici : Bootstrap l'utilise pour
       input-group-text (#f8f9fa) — écraser avec #fff le rend invisible. */

    /* === Modale de résultat (quittance) ===
       Un même composant sur deux écrans : le contrôle d'accueil et les
       départs/retours. La palette vit donc ici, dans la feuille chargée
       partout, et non dans `reception.css` qui n'est servie qu'aux postes
       accueil — un valideur de départs ne l'a pas.
       Chaque statut porte sa paire COMPLÈTE — fond, icône, halo — dans les
       deux thèmes. C'est le même piège que celui des badges `text-bg-*` :
       un fond posé sans sa couleur d'icône ne tombe sous le seuil que dans
       un seul des deux thèmes, donc jamais à la relecture. */
    --quittance-texte:             rgba(33, 37, 41, 0.84);
    --quittance-fond:              linear-gradient(180deg, #f2fbf6 0%, #d8f3e3 100%);
    --quittance-icone-fond:        rgba(25, 135, 84, 0.18);
    --quittance-icone-texte:       #0f6b42;
    --quittance-halo:              rgba(25, 135, 84, 0.08);
    --quittance-fond-erreur:       linear-gradient(180deg, #fff4f4 0%, #ffdede 100%);
    --quittance-icone-fond-erreur: rgba(220, 53, 69, 0.18);
    /* #b42318 ne rendait que 4,18:1 sur sa propre pastille : assombri jusqu'au
       premier rouge qui tienne le seuil AA sans cesser d'être un rouge. */
    --quittance-icone-texte-erreur: #a81f15;
    --quittance-halo-erreur:       rgba(220, 53, 69, 0.08);
}

[data-bs-theme="dark"] {
    /* Conserver la couleur navbar en mode sombre */
    --bs-primary: #2c3e50;
    --bs-primary-rgb: 44, 62, 80;
    /* Info plus clair pour lisibilité sur fond sombre */
    --bs-info: #5ba3d9;
    --bs-info-rgb: 91, 163, 217;
    /* Tertiary : fond légèrement distinct du body dark */
    --bs-tertiary-bg: #1e2125;
    --bs-tertiary-bg-rgb: 30, 33, 37;

    /* Modale de résultat — voir le bloc clair pour la règle. */
    --quittance-texte:             #dee2e6;
    --quittance-fond:              linear-gradient(180deg, #17241e 0%, #0e1a15 100%);
    --quittance-icone-fond:        rgba(117, 183, 152, 0.2);
    --quittance-icone-texte:       #75b798;
    --quittance-halo:              rgba(117, 183, 152, 0.1);
    --quittance-fond-erreur:       linear-gradient(180deg, #2b1416 0%, #1a0c0e 100%);
    --quittance-icone-fond-erreur: rgba(234, 134, 143, 0.2);
    --quittance-icone-texte-erreur: #ea868f;
    --quittance-halo-erreur:       rgba(234, 134, 143, 0.1);
}

/* Fix dark mode : bg-light reste #f8f9fa → éléments de saisie invisibles.
   Couvre : icône loupe, bouton reset (×), input des formulaires de recherche. */
[data-bs-theme="dark"] .input-group-text.bg-light,
[data-bs-theme="dark"] .btn.bg-light,
[data-bs-theme="dark"] .form-control.bg-light {
    background-color: var(--bs-tertiary-bg) !important;
    border-color: var(--bs-border-color) !important;
    color: var(--bs-body-color) !important;
}

/* Fix dark mode : Bootstrap hardcode --bs-table-color:#000 sur table-secondary
   → texte noir sur fond sombre = illisible. */
[data-bs-theme="dark"] .table-secondary {
    --bs-table-color: var(--bs-body-color);
    --bs-table-color-state: var(--bs-body-color);
    --bs-table-bg: rgba(108, 117, 125, 0.2);
    --bs-table-striped-color: var(--bs-body-color);
    --bs-table-hover-color: var(--bs-body-color);
}

/* ===========================================================================
 * PALETTE — les composants que Bootstrap fige en dur
 * ---------------------------------------------------------------------------
 * Les surcharges `--bs-primary` et consorts, en tête de fichier, ne portent
 * QUE sur les utilitaires écrits en `var()` : `.bg-*`, `.text-bg-*`. Les
 * COMPOSANTS, eux, sont compilés depuis Sass en hex littéraux — `.btn-primary`
 * vaut #0d6efd en dur — et n'ont jamais suivi la palette. D'où une couture
 * invisible : la moitié de l'interface au marine SGE, l'autre au bleu
 * Bootstrap, sans que rien ne le signale tant qu'on ne mesure pas.
 *
 * Le projet s'interdisant tout outil de construction frontend (cf. CLAUDE.md),
 * recompiler Sass n'est pas une option : on repeint donc à la main, ici, tous
 * les composants concernés QUE L'APPLICATION EMPLOIE. C'est la liste ci-dessous
 * — et c'est elle qu'il faut compléter si un composant Bootstrap nouveau entre
 * dans l'application.
 *
 * Trois règles, toutes vérifiables :
 *
 *  1. Le survol s'écarte de la teinte au repos DANS LA DIRECTION qui conserve
 *     4,5:1 avec le libellé blanc. Sur le marine, déjà sombre, c'est vers le
 *     clair — ce qui se lit d'ailleurs comme « pressable ». Sur le bleu moyen
 *     d'`info`, l'éclaircir tomberait à 4,41:1 : donc vers le sombre.
 *
 *  2. En thème sombre, un aplat marine ne se distingue plus du fond de page
 *     (1,40:1) alors que son libellé blanc y reste parfaitement lisible. Le
 *     projet s'en tient là : c'est le LIBELLÉ qui doit se lire, et il se lit.
 *     Lui ajouter un contour — bordure ou ombre interne — reviendrait à cercler
 *     d'un trait clair chaque bouton, chaque onglet actif et chaque entrée de
 *     menu sélectionnée d'une page sombre, pour un gain que personne ne
 *     réclamait. Les aplats sans libellé (barre de progression) échappent à la
 *     contrainte du texte et prennent la teinte claire du thème : là, la
 *     couleur suffit, et c'est bien de couleur qu'il s'agit.
 *
 *  3. `secondary` reste le gris neutre de Bootstrap pour les BOUTONS, alors que
 *     les badges gardent l'ardoise SGE. Ce n'est pas un oubli : un bouton
 *     neutre doit se distinguer du bouton principal qui le jouxte — « Annuler »
 *     à côté d'« Enregistrer ». Le gris s'en écarte de 2,34:1 ; l'ardoise, de
 *     1,18:1, soit deux boutons quasi identiques. Les deux teintes restent des
 *     neutres sombres de la même famille : la nuance se lit comme un ton, pas
 *     comme deux systèmes.
 *
 * Le travail d'accessibilité — jetons de texte, utilitaires, thème sombre — est
 * regroupé plus bas, dans le bloc « ACCESSIBILITÉ ». Il ne porte que sur la
 * COULEUR : aucun cadre, aucun anneau, aucun filet n'y est ajouté.
 */
.btn-primary {
    --bs-btn-color: #fff;
    --bs-btn-bg: #2c3e50;                    /* blanc dessus : 10,98:1 */
    --bs-btn-border-color: #2c3e50;
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: #455565;              /* +12 % de clair — 7,64:1 */
    --bs-btn-hover-border-color: #455565;
    --bs-btn-focus-shadow-rgb: 44, 62, 80;
    --bs-btn-active-color: #fff;
    --bs-btn-active-bg: #566573;             /* +20 % de clair — 6,02:1 */
    --bs-btn-active-border-color: #566573;
    --bs-btn-disabled-color: #fff;
    --bs-btn-disabled-bg: #2c3e50;
    --bs-btn-disabled-border-color: #2c3e50;
}

.btn-info {
    --bs-btn-color: #fff;
    --bs-btn-bg: #1566a8;                    /* blanc dessus : 6,00:1 */
    --bs-btn-border-color: #1566a8;
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: #125a94;              /* -12 % — 7,22:1 */
    --bs-btn-hover-border-color: #125a94;
    --bs-btn-focus-shadow-rgb: 21, 102, 168;
    --bs-btn-active-color: #fff;
    --bs-btn-active-bg: #115286;             /* -20 % — 8,19:1 */
    --bs-btn-active-border-color: #115286;
    --bs-btn-disabled-color: #fff;
    --bs-btn-disabled-bg: #1566a8;
    --bs-btn-disabled-border-color: #1566a8;
}

/* --- Sélection et état actif ---------------------------------------------
 *
 * Onglet, élément de liste, page courante, entrée de menu, case cochée : tous
 * peignaient le même #0d6efd compilé, à 4,50:1 pile — le minimum strict. Ils
 * passent au marine, à 10,98:1, et disent enfin la même chose que le reste de
 * l'interface : « ceci est sélectionné ».
 */
.nav-pills {
    --bs-nav-pills-link-active-color: #fff;
    --bs-nav-pills-link-active-bg: #2c3e50;      /* 10,98:1 */
}

.list-group {
    --bs-list-group-active-color: #fff;
    --bs-list-group-active-bg: #2c3e50;
    --bs-list-group-active-border-color: #2c3e50;
}

.pagination {
    --bs-pagination-active-color: #fff;
    --bs-pagination-active-bg: #2c3e50;
    --bs-pagination-active-border-color: #2c3e50;
}

.dropdown-menu {
    --bs-dropdown-link-active-color: #fff;
    --bs-dropdown-link-active-bg: #2c3e50;
    /* Rapatriées de la balise `<style>` que portait `partials/navbar.php` :
       des règles qui visent TOUS les menus de l'application n'ont rien à faire
       dans un partial. Valeurs inchangées. */
    border: none;
    border-radius: .75rem;
    /* Les entrées de Bootstrap, elles, sont à angles droits : la première et la
       dernière débordaient des coins arrondis dès qu'elles prenaient un fond —
       au survol, le bandeau rouge de « Déconnexion » ressortait carré sur un
       menu rond. Le menu découpe donc ce qu'il contient. */
    overflow: hidden;
}

/* Cases à cocher, boutons radio et interrupteurs : le coche et le bouton de
   l'interrupteur sont des SVG blancs figés par Bootstrap — le fond doit donc
   rester sombre pour les porter (blanc sur marine : 10,98:1). */
.form-check-input:checked {
    background-color: #2c3e50;
    border-color: #2c3e50;
}

.progress {
    --bs-progress-bar-bg: #2c3e50;
}

/* La barre de progression ne porte aucun libellé : libérée de la contrainte du
   texte, elle prend la teinte claire du thème, bien plus lisible sur sa piste
   sombre qu'un marine qui s'y noierait (1,40:1). */
[data-bs-theme="dark"] .progress {
    --bs-progress-bar-bg: var(--sge-text-primary);
    /* Les deux barres du projet font 4 px et ne portent rien. Si un pourcentage
       s'y écrivait un jour, le blanc de Bootstrap tomberait à 1,81:1 sur ce
       bleu clair : la couleur du libellé bascule donc, elle aussi. */
    --bs-progress-bar-color: #212529;
}

/* En thème sombre, le marine ne tranche plus sur le fond de page : le bouton
   perd son contour. La correction est une bordure, rangée avec les autres du
   même ordre dans le bloc « ACCESSIBILITÉ » (critère 1.4.11) plutôt qu'ici.

   En thème sombre, `--bs-info` passe au bleu clair : le libellé blanc y
   tomberait à 2,73:1, comme pour `.text-bg-info`. Même remède — le texte suit
   le thème, le fond aussi. */
[data-bs-theme="dark"] .btn-info {
    --bs-btn-color: #212529;
    --bs-btn-bg: #5ba3d9;                    /* 5,65:1 */
    --bs-btn-border-color: #5ba3d9;
    --bs-btn-hover-color: #212529;
    --bs-btn-hover-bg: #7bb5e0;
    --bs-btn-hover-border-color: #7bb5e0;
    --bs-btn-focus-shadow-rgb: 91, 163, 217;
    --bs-btn-active-color: #212529;
    --bs-btn-active-bg: #8cbfe4;
    --bs-btn-active-border-color: #8cbfe4;
    --bs-btn-disabled-color: #212529;
    --bs-btn-disabled-bg: #5ba3d9;
    --bs-btn-disabled-border-color: #5ba3d9;
}

/* Utility: tertiary background for neutral surfaces */
.bg-tertiary {
    background-color: var(--bs-tertiary-bg) !important;
}

/* ─── Coquille de l'application : barre supérieure + colonne + contenu ──────
   La navigation est verticale à toutes les largeurs. `offcanvas-lg` fige la
   colonne à partir de 992 px et la rend au tiroir en dessous ; tout ce qui suit
   n'a donc qu'à décrire une colonne, jamais deux mises en page concurrentes. */

:root {
    --sge-topbar-height: 3.25rem;
    --sge-sidebar-width: 16rem;
    --sge-sidebar-width-collapsed: 4.25rem;
}

/* Le décalage sous le bandeau de développement est déjà porté par
   `.dev-env .navbar` — la classe est conservée sur la barre, elle en hérite. */
.app-topbar {
    min-height: var(--sge-topbar-height);
    padding-top: .25rem;
    padding-bottom: .25rem;
}

/* Les deux commandes de droite ne sont pas posées de la même façon. L'avatar
   est l'enfant d'un lien `d-flex` : sa boîte est celle de la ligne, 30 px,
   centrée. L'icône du thème, elle, est un contenu EN LIGNE dans un bouton en
   bloc — le bloc « ACCESSIBILITÉ » la passe en `inline-flex` pour la centrer
   dans sa cible,
   mais cela ne centre que le glyphe DANS l'icône : la boîte de 20 px qui en
   résulte reste posée sur la ligne de base, donc haute. Mesuré : 3,5 px
   d'écart entre les deux centres, assez pour se voir.

   Le bouton devient donc lui-même le centreur, comme le lien voisin, et garde
   sa taille actuelle — un carré de 36 px, soit la cible tactile d'aujourd'hui
   (2.5.8 en demande 24). */
#themeToggleBtn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2.25rem;
    min-height: 2.25rem;
}

/* `data-bs-theme="dark"` sur la barre (voir navbar.php) rend déjà le trait du
   hamburger clair dans les deux thèmes, mais à 55 % d'opacité : 4,57:1 —
   conforme, et visiblement plus terne que le logo et les deux icônes qui
   l'entourent, tous en blanc plein. Même trait, même blanc : 9,3:1.

   La règle vise l'icône, pas la barre : Bootstrap repose le jeton sur
   `[data-bs-theme=dark] .navbar-toggler-icon`, c'est-à-dire sur l'élément
   lui-même. Posé sur l'ancêtre, le nôtre était écrasé — le contraste ne
   bougeait pas d'un pouce, ce que la mesure a montré avant la capture. */
.app-topbar .navbar-toggler-icon {
    --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.9%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
}

/* La barre ne porte aucune classe `.navbar-expand-*`, et pour cause : elle ne
   se replie jamais, la navigation vit dans la colonne. Bootstrap en conclut
   l'inverse — il réserve `position: absolute` au menu déroulant des barres
   `.navbar-expand-*` et laisse `position: static` partout ailleurs, en
   supposant un menu déplié À L'INTÉRIEUR d'une barre repliée.

   Le menu du compte entrait donc dans le flux : à son ouverture la barre
   passait de 54 à 203 px, le bouton de thème — centré sur une ligne devenue
   trois fois trop haute — descendait au milieu du menu, et l'ensemble se
   répartissait sur trois lignes sur smartphone. C'est l'effet « brouillon »
   des deux captures.

   `.navbar-expand` seul rétablirait la position, mais masquerait le hamburger
   (`display: none` sur `.navbar-toggler`) — or c'est lui qui ouvre le tiroir
   sous 992 px. On ne reprend donc que la règle utile. */
.app-topbar .navbar-nav .dropdown-menu {
    position: absolute;
}

/* Et la barre tient sur une ligne en toutes largeurs. C'est le nom
   d'événement qui cède : il est déjà borné à 45vw et se coupe en points de
   suspension. Sans cela, à 320 px, la barre se remettait à empiler. */
.app-topbar > .container-fluid {
    flex-wrap: nowrap;
}

/* Le nom de l'événement ouvert, porté par la barre sous 992 px — là où la
   colonne est cachée et ne peut plus le montrer. */
.app-topbar-event {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    min-width: 0;
    max-width: 45vw;
    /* Padding vertical symétrique, et il le reste. L'encre de la pilule sort
       0,63 px au-dessus de celle du hamburger, du logo, du thème et de l'avatar
       (tous à 26,94 px du haut de la barre) : sous un interligne de 1,2, le
       glyphe se pose haut dans sa boîte. Une compensation de 0,64 px répartie
       sur le padding a été essayée et retirée — le navigateur cale les lignes
       de base sur des pixels ENTIERS : le libellé et le chevron sautaient d'un
       pixel plein, le calendrier ne bougeait pas, et les trois se retrouvaient
       désalignés entre eux. Sous le pixel, on ne corrige pas : on aligne les
       éléments les uns sur les autres, ce que font les deux règles ci-dessous. */
    padding: .35rem .65rem;
    border: 1px solid rgba(255, 255, 255, .25);
    border-radius: 999px;
    background: rgba(255, 255, 255, .08);
    color: #fff;
    font-size: .95rem;
    line-height: 1.2;
}

/* Les deux icônes de la pilule prennent la même largeur de boîte. Sans cela,
   le calendrier (15 px) et le chevron (13 px, réduit par `.small`) ne se
   compensent pas : le libellé se retrouve 1 px à droite du centre de la
   pilule. Mesuré, et ramené à 0. */
.app-topbar-event > .bi {
    flex: 0 0 1rem;
    text-align: center;
}

/* Le chevron, lui, était 1,6 px plus haut que le calendrier et le libellé —
   de loin l'écart le plus visible de la barre. La cause n'est pas la mise en
   page : les trois boîtes sont bien centrées par le flex. C'est le glyphe qui
   est posé haut dans la sienne, et l'écart grandit quand `.small` rétrécit la
   police sous un interligne serré. On le redescend d'autant, en em pour que la
   correction suive la taille du texte. Après : les trois encres sur la même
   ligne, mesurées au pixel. */
.app-topbar-event > .bi-chevron-down {
    position: relative;
    top: .08em;
}

.app-shell {
    display: flex;
    align-items: flex-start;
}

.app-shell > main {
    flex: 1 1 auto;
    min-width: 0;
}

/* Le `!important` répond à celui de Bootstrap : au-dessus de son point de
   rupture, `.offcanvas-lg` force `background-color: transparent !important` pour
   se fondre dans la page. Sans cette réponse, la colonne perd son fond marine
   sur écran large et son texte blanc se retrouve sur blanc — c'est le banc de
   contraste qui l'a signalé, l'œil ne voyait qu'une colonne vide. */
.app-sidebar.offcanvas-lg {
    background-color: var(--bs-primary) !important;
    color: #fff;
    /* La colonne est une pile : le corps défilant, puis la version en pied.
       Bootstrap ne donne cette pile qu'en tiroir (`display: flex` sur
       `.offcanvas-lg` est enfermé dans son `max-width: 991.98px`) ; au-dessus
       de 992 px la colonne redevenait un bloc ordinaire, et son corps
       débordait par le bas d'une hauteur qu'il ne pouvait plus négocier. */
    display: flex;
    flex-direction: column;
}

/* Le corps défile, et c'est lui seul qui défile : la version reste en pied.
   Le sélecteur porte volontairement deux classes — `.app-sidebar-body` seul
   (0,1,0) perd contre `.offcanvas-lg .offcanvas-body` (0,2,0), qui impose
   au-dessus de 992 px `flex-grow: 0`, `overflow-y: visible` et `padding: 0`.
   Ces trois-là ensemble faisaient déborder la colonne sans la faire défiler :
   sur un écran de 830 px de haut, tout le bloc « Admin » (Paramètres,
   Permissions, Outils de développement, Journal) et « Réduire le menu »
   tombaient sous le bord bas du marine, en blanc sur le fond de page — donc
   invisibles, et sans aucun moyen de les atteindre. À deux classes, le
   sélecteur fait jeu égal et l'emporte par l'ordre des feuilles : style.css
   est chargée après Bootstrap. */
.app-sidebar .app-sidebar-body {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    /* Sans quoi un enfant flex ne descend pas sous la hauteur de son contenu,
       et `overflow-y: auto` n'a jamais rien à faire défiler. */
    min-height: 0;
    padding: .75rem .75rem calc(1.5rem + env(safe-area-inset-bottom, 0px));
    overflow-y: auto;
}

.app-sidebar-nav {
    display: flex;
    flex-direction: column;
    gap: .1rem;
}

.app-sidebar-heading {
    margin: 1.25rem 0 .35rem;
    padding: 0 .75rem;
    font-size: .7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 1.2px;
    color: rgba(255, 255, 255, .75);
}

.app-sidebar-nav > .app-sidebar-heading:first-child {
    margin-top: .25rem;
}

/* Gabarit tactile : ces écrans se tiennent à bout de bras, dehors, parfois
   gantés. 24 px suffisent au critère 2.5.8 ; ils ne suffisent pas au terrain. */
.app-sidebar .nav-link {
    display: flex;
    align-items: center;
    gap: .75rem;
    padding: .7rem .75rem;
    border-radius: 8px;
    color: rgba(255, 255, 255, .85);
    text-align: left;
    background: none;
    border: 0;
    width: 100%;
    min-height: 44px;
}

.app-sidebar .nav-link > i {
    width: 1.25rem;
    text-align: center;
    flex-shrink: 0;
}

.app-sidebar .nav-link:hover,
.app-sidebar .nav-link:focus-visible {
    color: #fff;
    background-color: rgba(255, 255, 255, .1);
}

.app-sidebar .nav-link.active {
    color: #fff;
    background-color: rgba(255, 255, 255, .16);
    font-weight: 600;
}

/* ─── Bloc « Événement » ────────────────────────────────────────────────────
   Le retrait n'est pas décoratif : c'est lui qui dit que ces entrées vivent
   dans l'événement nommé au-dessus, et non à côté de lui. */

.app-sidebar-event-head {
    display: flex;
    align-items: center;
    gap: .5rem;
    width: 100%;
    min-height: 44px;
    padding: .5rem .75rem;
    border: 1px solid rgba(255, 255, 255, .25);
    border-radius: 8px;
    background: rgba(255, 255, 255, .08);
    color: #fff;
    text-align: left;
}

.app-sidebar-event-head:hover,
.app-sidebar-event-head:focus-visible {
    background: rgba(255, 255, 255, .16);
}

.app-sidebar-event-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
    flex: 1 1 auto;
}

.app-sidebar-event-name {
    font-weight: 600;
    line-height: 1.25;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.app-sidebar-event-dates {
    font-size: .78rem;
    color: rgba(255, 255, 255, .7);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.app-sidebar-event-dates:empty {
    display: none;
}

.app-sidebar-event-empty {
    padding: .5rem .75rem .25rem;
    font-size: .82rem;
    color: rgba(255, 255, 255, .7);
}

.app-sidebar-event-links {
    margin-top: .25rem;
    padding-left: .5rem;
    border-left: 2px solid rgba(255, 255, 255, .2);
    margin-left: .5rem;
}

.app-sidebar-close-event {
    margin-top: .35rem;
    color: rgba(255, 255, 255, .65) !important;
    font-size: .88rem;
}

.app-sidebar-collapse {
    align-items: center;
    gap: .6rem;
    margin-top: auto;
    padding: .7rem .75rem;
    border: 0;
    border-top: 1px solid rgba(255, 255, 255, .12);
    border-radius: 0;
    background: none;
    color: rgba(255, 255, 255, .7);
    min-height: 44px;
}

.app-sidebar-collapse:hover {
    color: #fff;
}

/* Numéro de version : dernier étage de la pile, hors du corps défilant — il
   reste donc au pied de la colonne sans se superposer à ce qui défile.
   Il fut un temps ancré en `position: absolute`, ce qui posait à chaque fois
   la même question — sur quel référent ? — et donnait chaque fois une réponse
   fragile : le corps ne fait que la hauteur de son contenu au-dessus de
   992 px, la colonne n'a de hauteur pleine que là. Un élément de flux dans une
   pile flex n'a pas de référent à choisir.
   Masquée quand la colonne est réduite aux icônes, faute de place. */
.app-sidebar-version {
    flex: 0 0 auto;
    padding: .35rem .75rem calc(.45rem + env(safe-area-inset-bottom, 0px));
    font-size: .62rem;
    line-height: 1.2;
    /* 4.89:1 sur le marine `--bs-primary` (#2c3e50) : au-dessus du seuil AA de
       4.5:1, qu'exigent ces 0.62rem de texte. Un `.28` plus feutré tombait à
       2.30:1 — discret jusqu'à l'illisible. */
    color: rgba(255, 255, 255, .58);
    text-align: center;
    user-select: none;
    pointer-events: none;
}

.sidebar-collapsed .app-sidebar-version {
    display: none;
}

@media (min-width: 992px) {
    .app-sidebar {
        width: var(--sge-sidebar-width);
        flex: 0 0 var(--sge-sidebar-width);
        position: sticky;
        top: calc(var(--dev-banner-offset, 0px) + var(--sge-topbar-height));
        height: calc(100dvh - var(--dev-banner-offset, 0px) - var(--sge-topbar-height));
        transition: width .15s ease, flex-basis .15s ease;
    }

    /* Repli : choix de l'utilisateur, jamais imposé par la largeur. Les
       libellés disparaissent, les infobulles prennent le relais. */
    .sidebar-collapsed .app-sidebar {
        width: var(--sge-sidebar-width-collapsed);
        flex-basis: var(--sge-sidebar-width-collapsed);
    }

    .sidebar-collapsed .app-sidebar .nav-link > span,
    .sidebar-collapsed .app-sidebar-collapse > span,
    .sidebar-collapsed .app-sidebar-event-text,
    .sidebar-collapsed .app-sidebar-event-empty,
    .sidebar-collapsed .app-sidebar-heading {
        display: none;
    }

    .sidebar-collapsed .app-sidebar .nav-link,
    .sidebar-collapsed .app-sidebar-event-head,
    .sidebar-collapsed .app-sidebar-collapse {
        justify-content: center;
    }

    .sidebar-collapsed .app-sidebar-event-links {
        border-left: 0;
        margin-left: 0;
        padding-left: 0;
    }

    .sidebar-collapsed .app-sidebar-collapse > i {
        transform: rotate(180deg);
    }
}

@media (max-width: 991.98px) {
    .app-sidebar {
        width: 280px;
    }
    .dev-env .app-sidebar {
        top: var(--dev-banner-offset, 31px);
        height: calc(100dvh - var(--dev-banner-offset, 31px));
    }
    .app-sidebar .offcanvas-header {
        border-bottom: 1px solid rgba(255, 255, 255, .1);
        padding: 1.25rem;
    }
    /* La marge haute de `.container` sépare le contenu d'une barre horizontale
       qui n'existe plus : sur écran étroit, elle ne fait que creuser un vide. */
    .app-shell > main.container {
        margin-top: 1.25rem;
    }
}

#currentEventLabel {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    display: inline-block;
    vertical-align: middle;
}


@font-face {
    font-family: 'Numberplate';
    src: url('../font/NumberplateLL-SwitzerlandRd.woff2') format('woff2');
    font-weight: normal;
    font-style: normal;
    font-display: swap;
}


.container { 
    margin-top: 50px; 
    max-width: 1280px;
    padding-bottom: 3rem;
}

/* Un en-tête triable porte un vrai bouton, pour être atteignable au clavier —
   un `<th>` ne se focalise pas. Mais Bootstrap remet `text-transform: none` et
   sa propre fonte sur `.btn` : les colonnes triables dépareillaient des autres,
   en minuscules au milieu de capitales. Le bouton reprend donc la typographie
   de la cellule qui le contient, et n'en garde que le comportement. */
.table thead th > .btn {
    font: inherit;
    text-transform: inherit;
    letter-spacing: inherit;
    color: inherit;
    vertical-align: baseline;
}

/* Table enhancements */

.table thead th {
    text-transform: uppercase;
    font-size: 0.875em;
    letter-spacing: 0.05em;
    font-weight: 700;
    color: var(--bs-secondary-color);
    background-color: var(--bs-tertiary-bg);
}

/* Horizontal scroll indicators for tables */
.table-responsive {
    background: 
        linear-gradient(to right, var(--bs-card-bg) 30%, transparent), 
        linear-gradient(to left, var(--bs-card-bg) 30%, transparent) 100% 0, 
        radial-gradient(farthest-side at 0 50%, rgba(0, 0, 0, .12), transparent), 
        radial-gradient(farthest-side at 100% 50%, rgba(0, 0, 0, .12), transparent) 100% 0;
    background-repeat: no-repeat;
    background-color: var(--bs-card-bg);
    background-size: 40px 100%, 40px 100%, 14px 100%, 14px 100%;
    background-attachment: local, local, scroll, scroll;
    -webkit-overflow-scrolling: touch;
}

.table-responsive .table,
.table-responsive .table > :not(caption) > * > * {
    background-color: transparent !important;
}

@media (max-width: 768px) {
    .table-responsive::-webkit-scrollbar {
        height: 4px;
        display: block;
    }
    .table-responsive::-webkit-scrollbar-thumb {
        background: var(--bs-border-color);
        border-radius: 4px;
    }
}

/* Layout components */
#public-login-stage {
    --public-login-banner-offset: 0px;
    --public-login-notice-top: 1.5rem;
    position: relative;
    min-height: calc(100vh - var(--public-login-banner-offset));
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.5rem;
}

#public-login-notice {
    position: fixed;
    top: var(--public-login-notice-top);
    left: 50%;
    transform: translateX(-50%);
    width: min(720px, calc(100vw - 2rem));
    z-index: 1030;
}

#login-container {
    width: min(400px, 100%);
    max-width: 400px;
    margin: 0 auto;
}

#app-content { display: none; }

/* Specific UI Elements */
.brand-tag {
    background-color: var(--bs-tertiary-bg);
    color: var(--bs-secondary-color);
    border: 1px solid var(--bs-border-color);
    border-radius: 6px;
    padding: 3px 10px;
    font-size: 0.875em;
    font-weight: 600;
    text-transform: uppercase;
}

.cursor-pointer { cursor: pointer; }

/* Quill editor: use inherited font-size inside settings pane */
#pane-settings-content .ql-container { font-size: inherit !important; }

/* Numéro de version, discret, en bas de chaque page */
.app-version-footer {
    position: fixed;
    left: .5rem;
    bottom: .3rem;
    font-size: .62rem;
    color: var(--bs-secondary-color, #6c757d);
    opacity: .4;
    pointer-events: none;
    z-index: 5;
    user-select: none;
}

/* Dev environment banner */
.dev-banner {
    background-color: #ff9800;
    color: #000;
    text-align: center;
    padding: 5px;
    font-size: 0.8rem;
    font-weight: bold;
    position: sticky;
    top: 0;
    z-index: 1060;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.dev-env .navbar {
    top: var(--dev-banner-offset, 31px);
}
.dev-env #public-login-stage {
    --public-login-banner-offset: var(--dev-banner-offset, 31px);
    --public-login-notice-top: calc(var(--dev-banner-offset, 31px) + 1rem);
}

@media (max-width: 767.98px) {
    #public-login-stage {
        align-items: flex-start;
        padding-top: 8.5rem;
    }

    #public-login-notice {
        width: calc(100vw - 1rem);
    }
}

/* Correctif Bootstrap 5 : modal-dialog-centered + modal-fullscreen-*-down
   Sur écran étroit, align-items:center + le pseudo-élément ::before (espaceur
   de centrage vertical) empêchent modal-content de remplir toute la hauteur,
   laissant des zones transparentes. On force stretch et on supprime le spacer. */
@media (max-width: 991.98px) {
    .modal-fullscreen-lg-down.modal-dialog-centered {
        align-items: stretch;
    }
    .modal-fullscreen-lg-down.modal-dialog-centered::before {
        display: none;
    }
}

/* Correctif : <form> enfant direct de .modal-content casse le flex column de Bootstrap.
   Le form doit se comporter comme un flex item qui grandit, avec le body scrollable. */
#organizerModal .modal-content > form {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    min-height: 0;
}
#organizerModal .modal-content > form .modal-body {
    flex: 1 1 auto;
    overflow-y: auto;
    min-height: 0;
}

/* Modal improvements for event editor */
.modal-event-dialog {
    max-width: min(1140px, calc(100vw - 96px));
}
#eventModal .modal-content {
    max-height: calc(100vh - 96px);
}
#eventModal .modal-body {
    overflow-y: auto;
    max-height: calc(100vh - 220px);
}

/* Animations */
@keyframes spin {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
}
.bi-spin {
    display: inline-block;
    animation: spin 1s linear infinite;
}

.export-icon {
    width: 70px;
    height: 70px;
    border-radius: 20px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 2rem;
}

.table-user-deactivated {
    background-color: #fff5f5 !important;
}

.table-user-deactivated td {
    color: #880000;
}

[data-bs-theme="dark"] .table-user-deactivated {
    background-color: rgba(136, 0, 0, 0.2) !important;
}

[data-bs-theme="dark"] .table-user-deactivated td {
    color: #ff8080;
}


/* License Plate Styles */
.license-plate {
    width: 54px;
    height: 42px;
    background-color: #ffffff !important;
    color-scheme: light;
    border: 1px solid #000;
    border-radius: 4px;
    display: inline-flex;
    flex-direction: column;
    padding: 3px;
    box-shadow: 0 1px 2px rgba(0,0,0,0.1);
    font-family: 'Numberplate', 'Inter', -apple-system, sans-serif;
    position: relative;
    user-select: none;
    vertical-align: middle;
}

.license-plate .top-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    height: 14px;
    margin-top: 1px;
    margin-bottom: 4px;
}

.license-plate .crest {
    width: 11px;
    height: 13px;
    background-color: #ffffff !important;
}

/* Action buttons styling: consistent sizing and inactive state */
.btn-action {
    min-width: 40px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding-left: .5rem;
    padding-right: .5rem;
}

.table-actions-wrap {
    display: inline-flex;
    flex-wrap: wrap;
    /* Toutes les actions d'une ligne prennent la hauteur de la plus haute —
       celle du bouton libellé. Sans quoi un bouton d'icône, dont la hauteur ne
       tient qu'à la fonte de l'icône, arrivait un ou deux pixels plus court que
       le « Gérer » posé juste à côté. */
    align-items: stretch;
    justify-content: flex-end;
    gap: 0.25rem;
}

.table-actions-wrap > .btn,
.table-actions-wrap > a.btn,
.table-actions-wrap > .dropdown {
    flex: 0 0 auto;
}

/* Le conteneur de menu n'est qu'un emballage de positionnement : il se laisse
   traverser pour que le bouton qu'il porte s'étire comme ses voisins. */
.table-actions-wrap > .dropdown {
    display: flex;
}

/* Bouton d'icône dans une ligne de tableau : carré, exactement aussi haut que
   le bouton libellé voisin.

   La hauteur vient de l'étirement ci-dessus ; la largeur est recalculée à
   l'identique depuis les variables que Bootstrap pose lui-même sur `.btn` —
   elle suit donc `.btn-sm` comme `.btn-lg` sans qu'on ait à figer une valeur.
   `aspect-ratio` ne convient pas ici : sur un élément flex en `flex: 0 0 auto`,
   la largeur est déjà résolue depuis le contenu, et le ratio n'est pas appliqué.
   Le `min-width: 40px` de `.btn-action` donnait, lui, 40 × 31 px — un rectangle,
   que le padding latéral étirait encore selon la largeur de l'icône. */
.table-actions-wrap .btn-action {
    --sge-taille-action: calc(
        var(--bs-btn-line-height, 1.5) * var(--bs-btn-font-size, 1rem)
        + var(--bs-btn-padding-y, 0.375rem) * 2
        + var(--bs-btn-border-width, 1px) * 2
    );
    width: var(--sge-taille-action);
    min-width: var(--sge-taille-action);
    padding-left: 0;
    padding-right: 0;
}

/* Disabled buttons */
.btn:disabled,
.dropdown-item:disabled {
    opacity: 0.6;
}


.license-plate .canton, .license-plate .number {
    font-size: 16px;
    font-weight: normal;
    color: #1a1a1a !important;
    line-height: 1;
}

.license-plate .bottom-row {
    flex-grow: 1;
    display: flex;
    justify-content: center;
    align-items: center;
    margin-top: 1px;
}

/* Trials Section Styles */


.trial-validations-container {
    line-height: 1.4;
}

.trials-table {
    table-layout: auto;
}

.trials-table th:nth-child(1) {
    width: 20%;
}

.trials-table th:nth-child(2) {
    width: 20%;
}

.trials-table th:nth-child(3) {
    width: 25%;
}

.trials-table th:nth-child(4) {
    width: 25%;
}

.trials-table th:nth-child(5) {
    width: 10%;
}

/* ID formatting: bold part slightly larger */
.id-bold {
    font-size: 1.1em;
    letter-spacing: 0.02em;
}

/* ===== Largeur des listes déroulantes ==================================== */

/* Une liste déroulante doit montrer l'option retenue en entier : « Responsable
   d'éq… » ne dit plus quel rôle porte la personne. Deux régimes, selon l'origine
   des libellés.

   1. Libellés écrits dans le code (rôle, statut) : leur longueur est connue, la
   largeur doit donc s'y plier. `fit-content` la calcule sur l'option la plus
   longue — aucune valeur en pixels à maintenir, et reformuler un libellé ne peut
   plus rétrécir la liste sous son contenu. */
.form-select-fit {
    width: auto;
    min-width: fit-content;
    max-width: 100%;
}

/* 2. Libellés venus des données (événement, stand, exposant, organisateur) :
   leur longueur est imprévisible et imposerait un défilement horizontal sur
   mobile. Ces listes-là s'autorisent à rétrécir, mais la coupe se fait en points
   de suspension ; l'intitulé entier reste lisible en ouvrant la liste. */
.form-select-elastic {
    /* Un plancher, sinon « Yamaha Motor Suisse SA » se réduit à « Yam… » et la
       liste ne distingue plus rien. `min()` le désarme quand le conteneur est
       lui-même plus étroit : sur mobile la liste occupe sa place, sans jamais
       imposer de défilement horizontal à la page. */
    min-width: min(100%, 9rem);
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ===== Harmonized Search Header Styles ===== */

/* Card headers with search integration */
.card-header {
    flex-wrap: wrap;
}

.search-input-group {
    flex: 1;
    max-width: 350px;
    min-width: 0;
}

.search-header-btn {
    white-space: nowrap;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.25rem;
    transition: all 0.2s ease;
}

/* Icon-only buttons on smaller screens */
.search-header-btn span {
    transition: opacity 0.2s ease;
}

/* Responsive behavior for buttons */
@media (max-width: 991.98px) {
    .search-input-group {
        flex-basis: 100%;
        max-width: 100%;
        margin-bottom: 0.5rem;
    }
    
    .card-header {
        padding: 0.75rem !important;
        row-gap: 0.75rem;
    }
    
    .card-header h5 {
        font-size: 0.95rem;
    }
    
    .btn-group {
        flex-wrap: wrap;
    }
}

@media (max-width: 767.98px) {
    .card-header {
        padding: 0.5rem !important;
        column-gap: 0.5rem;
    }
    
    .card-header h5 {
        font-size: 0.9rem;
    }
    
    .search-input-group {
        flex-basis: 100%;
        margin-bottom: 0.5rem;
    }
    
    .search-input-group .input-group-text,
    .search-input-group .form-control,
    .search-input-group .btn {
        font-size: 0.875rem;
    }
    
    /* Hide text labels on extra small screens */
    .search-header-btn span.d-lg-inline {
        font-size: 0.75rem;
    }
}

/* Tooltips styling for better visibility */
.tooltip-inner {
    padding: 0.5rem 0.75rem;
    font-size: 0.875rem;
    font-weight: 500;
    border-radius: 0.375rem;
}

/* Button group responsiveness */
.btn-group {
    flex-shrink: 0;
    display: flex;
    gap: 0; /* remove gap between grouped buttons to avoid visual space */
}

/* Table-specific action buttons: align right and ensure consistent icon-button widths */
.table td .btn-group {
    justify-content: flex-end;
}

/* Global application table utility: consistent layout and ellipsis handling */
.app-table {
    width: 100%;
    table-layout: auto; /* allow natural column sizing unless specific width utilities applied */
}

/* 1.4.12 — les cellules étaient tenues sur une ligne puis coupées à l'ellipse.
   Tout allongement du texte — espacement des lettres imposé par l'utilisateur,
   agrandissement, traduction — se traduisait alors par une perte pure et simple
   de contenu, y compris là où le conteneur lui-même rogne
   (`#usersSection .table-responsive` est en `overflow-x: clip`). Le texte revient
   donc à la ligne : la hauteur des lignes s'adapte, rien ne disparaît. Les
   cellules qui doivent rester insécables (colonne d'actions, en-têtes triables)
   posent leur propre `nowrap` plus bas. */
.app-table th, .app-table td {
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
}

/* Force uniform sizing for action buttons inside tables regardless of group composition */
.table td .btn-group > .btn,
.table td .btn-group > a.btn {
    box-sizing: border-box !important;
    flex: 0 0 44px !important; /* fixed width, prevents growing/shrinking */
    width: 44px !important;
    min-width: 44px !important;
    padding-left: 0 !important;
    padding-right: 0 !important;
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
}

/* Center icon only, avoid icon stretching */
.table td .btn-group > .btn i,
.table td .btn-group > a.btn i {
    display: inline-block !important;
    width: auto !important;
    text-align: center !important;
}

/* Hide any accidental text label inside table action buttons to keep consistent width */
.table td .btn-group > .btn > span,
.table td .btn-group > a.btn > span {
    display: none !important;
}

.app-table td.users-actions-cell {
    overflow: visible !important;
    text-overflow: clip !important;
    white-space: nowrap !important;
}

#usersSection .table-responsive {
    overflow-x: clip;
    overflow-y: visible;
}

@media (max-width: 767.98px) {
    #usersSection .table-responsive {
        overflow-x: auto;
        overflow-y: auto;
    }
}

/* ─── Liste des événements ──────────────────────────────────────────────────
   Même remède que pour l'annuaire : `.table-responsive` pose un contexte de
   défilement qui rogne le menu ⋮ dès qu'il s'ouvre vers le bas. On rend le
   débordement vertical visible, et on ne rétablit le défilement horizontal que
   là où il est vraiment nécessaire. */

#eventsSection td.events-actions-cell {
    overflow: visible !important;
    text-overflow: clip !important;
    white-space: nowrap !important;
}

#eventsSection .table-responsive {
    overflow-x: clip;
    overflow-y: visible;
}

@media (max-width: 767.98px) {
    #eventsSection .table-responsive {
        overflow-x: auto;
        overflow-y: auto;
    }
}

/* En-tête de groupe : un vrai bouton, pour être atteignable au clavier et
   annoncer son état replié — mais qui doit reprendre la typographie de la
   cellule, Bootstrap remettant sa propre fonte sur tout `.btn`. */
.events-group-btn {
    display: inline-flex;
    align-items: center;
    width: 100%;
    padding: .35rem 0;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    text-transform: inherit;
    letter-spacing: inherit;
    text-align: left;
}

.events-group-head th {
    padding-top: .5rem;
    padding-bottom: .5rem;
}

/* Le nom ouvre l'événement, comme le bouton « Gérer » de la même ligne. Le
   critère 2.5.8 tolère un petit doublon lorsqu'un contrôle équivalent atteint
   la taille requise — ce qui est le cas ici. On le rend confortable quand même :
   sans hauteur propre, une ligne de texte plafonne à 19 px. */
.events-name-link {
    display: inline-block;
    padding: .15rem 0;
    min-height: 24px;
}

.users-actions-wrap {
    display: inline-flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 0.25rem;
}

.users-actions-dropdown {
    flex: 0 0 auto;
}

.users-actions-dropdown > .dropdown-toggle {
    width: auto !important;
    min-width: 0 !important;
    padding-left: 0.65rem !important;
    padding-right: 0.65rem !important;
}

#usersSection .dropdown-menu {
    min-width: 16rem;
    z-index: 1070;
    background-color: var(--bs-body-bg) !important;
    border: 1px solid var(--bs-border-color);
    opacity: 1 !important;
    backdrop-filter: none !important;
}

#usersSection .dropdown-menu.show {
    opacity: 1 !important;
}

#usersSection .dropdown-menu .dropdown-item {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    gap: 0.5rem;
    white-space: normal;
    text-align: left;
    opacity: 1 !important;
}

#usersSection .dropdown-menu .dropdown-item i {
    flex: 0 0 1rem;
    width: 1rem;
    text-align: center;
}

/* Ensure badge alignment in headers */
.badge.small {
    font-size: 0.75rem;
    padding: 0.25rem 0.5rem;
}

/* Basic brand logo sizing used across teams lists */
.brand-logo {
    height: 40px;
    width: auto;
    vertical-align: middle;
}

/* Brand logo sizing in team details: occupy one third of the containing column */
#teamDetailsBrandsList .brand-logo {
    width: 27%;
    height: auto;
    max-width: 100%;
    display: inline-block;
    vertical-align: middle;
    margin-right: 0.5rem;
    margin-bottom: 0.5rem;
}

#teamDetailsBrandsList .brand-tag {
    margin-right: 0.5rem;
    margin-bottom: 0.5rem;
}


/* Séparateur vertical côté gauche à partir de lg */
@media (min-width: 992px) {
    .border-start-lg {
        border-left: 1px solid var(--bs-border-color) !important;
        padding-left: 1.5rem !important;
    }
}

/* En-têtes de tableau triables */
.th-sortable {
    cursor: pointer;
    user-select: none;
    white-space: nowrap;
}
.th-sortable:hover {
    background-color: var(--bs-table-hover-bg, rgba(0,0,0,.04));
}
.th-sortable .sort-indicator {
    opacity: 0.25;
    font-size: .85em;
}
.th-sortable .sort-indicator--active {
    opacity: 0.8;
}

/* Icône Tabler motorbike (inline, style mask comme Bootstrap Icons) */
.ti-motorbike {
    display: inline-block;
    width: 1em;
    height: 1em;
    vertical-align: -0.125em;
    background-color: currentColor;
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2 16a3 3 0 1 0 6 0a3 3 0 1 0 -6 0'/%3E%3Cpath d='M16 16a3 3 0 1 0 6 0a3 3 0 1 0 -6 0'/%3E%3Cpath d='M7.5 14h5l4 -4h-10.5m1.5 4l4 -4'/%3E%3Cpath d='M13 6h2l1.5 3l2 4'/%3E%3C/svg%3E");
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2 16a3 3 0 1 0 6 0a3 3 0 1 0 -6 0'/%3E%3Cpath d='M16 16a3 3 0 1 0 6 0a3 3 0 1 0 -6 0'/%3E%3Cpath d='M7.5 14h5l4 -4h-10.5m1.5 4l4 -4'/%3E%3Cpath d='M13 6h2l1.5 3l2 4'/%3E%3C/svg%3E");
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-size: 100% 100%;
    mask-size: 100% 100%;
}

/* --- Suggestions d'adresses (registres officiels CH et FR) ------------ *
 * La liste est posée sur <body> en position fixe : les panneaux du parcours
 * d'inscription sont en « overflow: hidden » et rogneraient un positionnement
 * absolu. Les coordonnées sont calculées en JS (address-autocomplete.js). */
.address-suggest {
    position: fixed;
    z-index: 1080; /* au-dessus des modales Bootstrap (1055) */
    margin: 0;
    padding: .25rem;
    list-style: none;
    width: max-content;
    max-height: 16rem;
    overflow-y: auto;
    background: var(--bs-body-bg, #fff);
    border: 1px solid var(--bs-border-color);
    border-radius: var(--bs-border-radius, .375rem);
    box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .12);
}

.address-suggest-item {
    padding: .4rem .6rem;
    border-radius: calc(var(--bs-border-radius, .375rem) - .125rem);
    cursor: pointer;
    line-height: 1.3;
}

/* Le fond de survol était `--bs-tertiary-bg`, à 1,05:1 de celui de la liste :
   l'option parcourue au clavier ne se distinguait pas. Un cran plus soutenu
   suffit à la montrer, et c'est un changement de couleur — pas le liseré de
   4 px qui tenait ce rôle auparavant. */
.address-suggest-item:hover,
.address-suggest-item.is-active {
    background: var(--bs-secondary-bg);
}

.address-suggest-primary {
    display: block;
}

.address-suggest-secondary {
    display: block;
    font-size: .85em;
    color: var(--bs-secondary-color);
}

.address-suggest-empty {
    padding: .4rem .6rem;
    font-size: .88rem;
    color: var(--bs-secondary-color);
}

/* ===========================================================================
 * ACCESSIBILITÉ — contraste et perception (WCAG 2.2 AA)
 * ---------------------------------------------------------------------------
 * Ce bloc remplace `a11y.css`, une surcouche chargée après toutes les autres
 * feuilles et qui, à force de corriger, imposait sa propre allure — au premier
 * chef un cadre gris foncé autour de chaque zone de saisie, un anneau de focus à
 * trois épaisseurs, et un trait rendu à tout composant dont l'aplat se fondait
 * dans son fond. Aucun de ces traits n'est déplacé ici : tous sont SUPPRIMÉS
 * (voir la section 4, qui en donne la liste). Ce bloc ne contient plus que de la
 * couleur :
 *
 *   1.4.3  Contraste minimum      — jetons de texte recalculés (clair + sombre)
 *   1.4.11 Contraste non-texte    — la seule part qui soit affaire de couleur :
 *                                   la barre de progression sur sa piste
 *   2.4.11 Focus non masqué       — décalage de défilement sous la barre collée
 *   2.5.8  Taille de cible (min)  — 24x24 px pour toute cible pointée
 *   2.3.3 / 2.2.2  Animations     — respect de prefers-reduced-motion
 *
 * La règle qui a présidé au tri : une correction de COULEUR ne se voit pas tant
 * qu'on ne la mesure pas, et ne coûte donc rien à l'allure de l'interface ; une
 * correction de FORME — un cadre, un anneau, un filet — se voit toujours,
 * partout, tout le temps. Les premières sont reprises telles quelles. Les
 * secondes, aucune.
 *
 * Les `!important` ne subsistent que face aux utilitaires de Bootstrap, qui en
 * portent eux-mêmes : à spécificité égale, sans lui, la correction ne passe
 * pas. Partout ailleurs ils ont été retirés — cette feuille est chargée après
 * Bootstrap, cela suffit.
 * ======================================================================== */

/* ---------------------------------------------------------------------------
 * 1. Jetons de couleur accessibles
 *
 * Bootstrap expose deux familles : `--bs-<couleur>` (fond, pensé pour porter du
 * texte blanc) et `--bs-<couleur>-text-emphasis` (texte, pensé pour être posé
 * sur le fond du corps). Les utilitaires `.text-success`, `.text-warning`, etc.
 * utilisent la PREMIÈRE — d'où des textes jaunes à 1,6:1 sur blanc. On les
 * réaffecte ici à des valeurs mesurées ≥ 4,5:1 sur les fonds réellement
 * employés (#fff, #f8f9fa, #e9ecef en clair ; #212529, #2b3035 en sombre).
 * ------------------------------------------------------------------------ */
/* Les deux sélecteurs sont indispensables : `:root` pour la page, et
   `[data-bs-theme="light"]` pour tout sous-arbre épinglé au thème clair au
   milieu d'une page sombre — sans quoi ce sous-arbre hériterait des jetons
   sombres de la racine. */
:root,
[data-bs-theme="light"] {
    /* Texte — thème clair (ratios sur #f8f9fa, le fond le plus clair-critique) */
    --sge-text-primary:   #24384f; /*  9,9:1 */
    --sge-text-secondary: #55595e; /*  6,4:1 */
    --sge-text-success:   #146c43; /*  6,3:1 */
    --sge-text-danger:    #b02a37; /*  6,3:1 */
    --sge-text-warning:   #8a5a00; /*  5,7:1 */
    --sge-text-info:      #1566a8; /*  5,8:1 */
    --sge-text-muted:     #5c636a; /*  5,9:1 */

    /* Ce jeton ne sert plus à cercler quoi que ce soit : il ne reste que deux
       emplois, et dans les deux le trait existe déjà, on ne fait que lui donner
       une teinte. La bande de section du tableau des permissions
       (`sections/permissions.php`), qui a ses filets par dessin ; et la bordure
       de `.btn-outline-secondary` en thème sombre, qu'un bouton « outline » a
       par définition. Pour tout le reste, voir la section 4. */
    --sge-border-strong: #6c757d;

    /* Liens et code en ligne : les valeurs Bootstrap (#0d6efd et #d63384)
       tiennent tout juste sur blanc et tombent sous 4,5:1 dès qu'un fond
       d'alerte se glisse dessous. */
    --bs-link-color: #0a58ca;
    --bs-link-color-rgb: 10, 88, 202;
    --bs-link-hover-color: #08417f;
    --bs-link-hover-color-rgb: 8, 65, 127;
    --bs-code-color: #a01a55;

}

/* `.dropdown-menu-dark` est ajouté ici, et pas seulement au bloc des jetons de
   focus plus bas : ce menu reste sombre dans les DEUX thèmes. En thème clair,
   il héritait donc de jetons calculés pour du texte sur blanc — `.text-danger`
   y tombait à #b02a37 sur #212529, soit 2,2:1. C'est ce que `navbar.php`
   compensait par un `#ff8787` écrit à la main dans une balise `<style>`. */
[data-bs-theme="dark"],
.dropdown-menu-dark {
    /* Liens et code : les teintes claires reprennent la main. */
    --bs-link-color: #6ea8fe;
    --bs-link-color-rgb: 110, 168, 254;
    --bs-link-hover-color: #9ec5fe;
    --bs-link-hover-color-rgb: 158, 197, 254;
    --bs-code-color: #f18ebe;

    /* Texte — thème sombre (ratios sur #212529) */
    --sge-text-primary:   #9ec5e8; /*  8,0:1 */
    --sge-text-secondary: #b6bec6; /*  9,3:1 */
    --sge-text-success:   #75b798; /*  6,6:1 */
    --sge-text-danger:    #ea868f; /*  6,1:1 */
    --sge-text-warning:   #ffda6a; /* 11,4:1 */
    --sge-text-info:      #5ba3d9; /*  5,7:1 */
    --sge-text-muted:     #adb5bd; /*  8,0:1 */

    --sge-border-strong: #8b949e;
}

/* Les jetons ci-dessus sont calculés sur #212529, la surface sombre de
   l'application. Bootstrap donne au menu sombre son gris 800 (#343a40), plus
   clair : `--sge-text-danger` y perdait sa marge (4,50:1, tout juste). On
   aligne donc le fond du menu sur la surface pour laquelle les jetons ont été
   établis — le rouge y retrouve ses 6,1:1, et le menu s'accorde au reste des
   surfaces sombres du projet. */
.dropdown-menu-dark {
    --bs-dropdown-bg: #212529;
}

/* Le survol de Bootstrap est un blanc à 15 % : sur ce fond il donne #3c4145,
   où le rouge du jeton retombe à 3,8:1. La ligne de déconnexion prend donc un
   fond plein et un texte blanc — 6,5:1 sur ce rouge, et un survol qui se voit
   au lieu de s'éclaircir de trois pour cent. */
.dropdown-menu-dark .dropdown-item.text-danger:hover,
.dropdown-menu-dark .dropdown-item.text-danger:focus {
    color: #fff !important;
    background-color: #b02a37;
}

/* ---------------------------------------------------------------------------
 * 2. Texte, liens et badges — 1.4.3
 * ------------------------------------------------------------------------ */

/* Utilitaires de texte Bootstrap réaffectés aux jetons accessibles.
   Les utilitaires de FOND (`.bg-*`, `.text-bg-*`) ne sont pas touchés : ils
   sont déjà conformes, et les modifier casserait les badges et les boutons. */
.text-primary   { color: var(--sge-text-primary)   !important; }
.text-secondary { color: var(--sge-text-secondary) !important; }
.text-success   { color: var(--sge-text-success)   !important; }
.text-danger    { color: var(--sge-text-danger)    !important; }
.text-warning   { color: var(--sge-text-warning)   !important; }
.text-info      { color: var(--sge-text-info)      !important; }
.text-muted     { color: var(--sge-text-muted)     !important; }

/* Idem pour les liens colorés. */
.link-primary   { color: var(--sge-text-primary)   !important; }
.link-secondary { color: var(--sge-text-secondary) !important; }
.link-success   { color: var(--sge-text-success)   !important; }
.link-danger    { color: var(--sge-text-danger)    !important; }
.link-warning   { color: var(--sge-text-warning)   !important; }
.link-info      { color: var(--sge-text-info)      !important; }

/* `.text-bg-info` est compilé par Bootstrap avec du texte NOIR, calculé sur son
   cyan d'origine (#0dcaf0). Le projet remplace `--bs-info` par un bleu bien
   plus sombre : le fond change, la couleur du texte non, et la paire tombe à
   3,5:1. C'est notamment le bandeau des notifications d'information. */
.text-bg-info {
    color: #fff !important;
}

/* … mais `--bs-info` change de camp avec le thème : bleu foncé #1566a8 en
   clair, bleu clair #5ba3d9 en sombre. Le blanc ci-dessus, laissé tel quel,
   retombait à 2,73:1 en thème sombre — le même défaut, dans l'autre sens.
   La couleur du texte doit donc suivre le thème comme le fond.
   Concerne les badges du journal d'audit, des essais et du détail événement,
   ainsi que le statut « Ouverte » des invitations. */
[data-bs-theme="dark"] .text-bg-info {
    color: #212529 !important;
}

/* `.text-bg-orange` n'existe pas : ni Bootstrap ni le projet ne la définissent.
   Le journal d'audit la pose pourtant sur `reception.duplicate_override`, et le
   badge sortait alors avec la couleur de texte par défaut de `.badge` — du
   blanc — sur un fond resté transparent : illisible en thème clair. On la
   définit ici avec l'orange brûlé déjà employé par l'écran des départs. */
.text-bg-orange {
    color: #fff !important;
    background-color: #b83b00 !important;
}

/* `.text-dark` fixe #212529 quel que soit le thème : posé sur une surface
   thématisée, il devient invisible en mode sombre. Là où il sert à appuyer du
   texte courant (et non à écrire sur un fond clair fixe), on le ramène sur la
   couleur de corps. Les combinaisons volontaires avec un fond clair figé
   (`bg-light text-dark`, `text-bg-*`) sont exclues juste après. */
[data-bs-theme="dark"] .text-dark:not([class*="bg-"]) {
    color: var(--bs-body-color) !important;
}

/* … sauf lorsqu'il est posé sur une surface claire figée, où il doit rester
   sombre. Ces sélecteurs sont plus spécifiques que la règle ci-dessus.

   `bg-warning` et `bg-info` sont de la même famille que `bg-light` : jaune et
   cyan ne bougent pas avec le thème. Elles manquaient à la liste, et le titre
   « Renvoi des identifiants » — un `text-dark` sur en-tête `bg-warning` —
   tombait à 1,25:1 en sombre. Les autres teintes (`primary`, `success`,
   `danger`) sont sombres et appellent du texte clair : elles n'ont rien à
   faire ici. */
[data-bs-theme="dark"] .bg-light .text-dark,
[data-bs-theme="dark"] .bg-white .text-dark,
[data-bs-theme="dark"] .bg-warning .text-dark,
[data-bs-theme="dark"] .bg-info .text-dark,
[data-bs-theme="dark"] .bg-warning.text-dark,
[data-bs-theme="dark"] .bg-info.text-dark,
[data-bs-theme="dark"] [class*="text-bg-"] .text-dark {
    color: #212529 !important;
}

/* Surfaces claires FIGÉES sous thème sombre.
 *
 * `.bg-light`, `.bg-white` et `.table-light` peignent un fond clair quel que
 * soit le thème, tandis que le texte qu'elles contiennent continue de tirer
 * ses couleurs du thème sombre : on obtenait du #dee2e6 sur du #f8f9fa, soit
 * 1,2:1. Plutôt que de traiter chaque descendant, on redéclare les jetons de
 * texte pour tout le sous-arbre — le mécanisme même dont Bootstrap 5.3 se sert
 * pour cantonner un thème. */
/* Exception : sur un `<thead>`, `.table-light` ne peint aucun fond — elle n'y
 * règle que des variables, et les cellules restent transparentes. Le postulat
 * ci-dessus tombait donc à faux : on forçait le texte en #212529 sur le fond
 * sombre du tableau, soit un contraste de 1,00:1 — l'en-tête devenait
 * strictement invisible. Sept tableaux étaient concernés. Un en-tête suit
 * désormais le thème comme le reste du tableau. */
[data-bs-theme="dark"] .bg-light,
[data-bs-theme="dark"] .bg-white,
[data-bs-theme="dark"] .table-light:not(thead),
[data-bs-theme="dark"] [class*="text-bg-light"] {
    --bs-body-color: #212529;
    --bs-body-color-rgb: 33, 37, 41;
    --bs-emphasis-color: #000000;
    --bs-secondary-color: rgba(33, 37, 41, 0.75);
    --bs-heading-color: inherit;
    --sge-text-primary: #24384f;
    --sge-text-secondary: #55595e;
    --sge-text-success: #146c43;
    --sge-text-danger: #b02a37;
    --sge-text-warning: #8a5a00;
    --sge-text-info: #1566a8;
    --sge-text-muted: #5c636a;
    color: #212529;
}

/* ---------------------------------------------------------------------------
 * 3. Boutons — couleur du libellé (1.4.3) et limite de l'aplat (1.4.11)
 * ------------------------------------------------------------------------ */

/* Boutons « outline » en thème clair : Bootstrap y écrit avec la teinte de
   marque brute, calibrée pour porter du blanc, pas pour être lue sur un fond
   clair. `.btn-outline-warning` tombait à 1,6:1, `.btn-outline-info` à 2,0:1,
   et les autres passaient sous le seuil dès qu'un fond gris de carte se
   glissait dessous. La bordure, elle, garde la teinte vive : 3:1 lui suffit. */
/* `primary` suit la palette SGE (cf. le bloc « PALETTE » plus haut) : le marine
   se lit déjà à 10,98:1 sur blanc, il sert donc de couleur de texte ET de
   bordure, sans dérivée plus sombre. */
.btn-outline-primary   { --bs-btn-color: #2c3e50; --bs-btn-border-color: #2c3e50; }
.btn-outline-secondary { --bs-btn-color: #5c636a; }
.btn-outline-success   { --bs-btn-color: #146c43; }
.btn-outline-danger    { --bs-btn-color: #b02a37; }
.btn-outline-warning   { --bs-btn-color: #8a5a00; --bs-btn-border-color: #997404; }
.btn-outline-info      { --bs-btn-color: #1566a8; --bs-btn-border-color: #1566a8; }

/* Boutons « outline » en thème sombre : la teinte de bordure/texte vient de
   `--bs-<couleur>`, pensée pour porter du blanc, donc trop sombre sur fond
   sombre (1,4:1 pour `.btn-outline-secondary`). On repasse sur les jetons de
   texte du thème. */
[data-bs-theme="dark"] .btn-outline-primary   { --bs-btn-color: var(--sge-text-primary);   --bs-btn-border-color: var(--sge-text-primary);   --bs-btn-hover-bg: var(--sge-text-primary);   --bs-btn-hover-border-color: var(--sge-text-primary);   --bs-btn-hover-color: #10161d; --bs-btn-active-bg: var(--sge-text-primary);   --bs-btn-active-border-color: var(--sge-text-primary);   --bs-btn-active-color: #10161d; --bs-btn-disabled-color: var(--sge-text-primary);   --bs-btn-disabled-border-color: var(--sge-text-primary); }
[data-bs-theme="dark"] .btn-outline-secondary { --bs-btn-color: var(--sge-text-secondary); --bs-btn-border-color: var(--sge-border-strong);  --bs-btn-hover-bg: var(--sge-text-secondary); --bs-btn-hover-border-color: var(--sge-text-secondary); --bs-btn-hover-color: #10161d; --bs-btn-active-bg: var(--sge-text-secondary); --bs-btn-active-border-color: var(--sge-text-secondary); --bs-btn-active-color: #10161d; --bs-btn-disabled-color: var(--sge-text-secondary); --bs-btn-disabled-border-color: var(--sge-border-strong); }
[data-bs-theme="dark"] .btn-outline-success   { --bs-btn-color: var(--sge-text-success);   --bs-btn-border-color: var(--sge-text-success);   --bs-btn-hover-bg: var(--sge-text-success);   --bs-btn-hover-border-color: var(--sge-text-success);   --bs-btn-hover-color: #10161d; --bs-btn-active-bg: var(--sge-text-success);   --bs-btn-active-border-color: var(--sge-text-success);   --bs-btn-active-color: #10161d; --bs-btn-disabled-color: var(--sge-text-success);   --bs-btn-disabled-border-color: var(--sge-text-success); }
[data-bs-theme="dark"] .btn-outline-danger    { --bs-btn-color: var(--sge-text-danger);    --bs-btn-border-color: var(--sge-text-danger);    --bs-btn-hover-bg: var(--sge-text-danger);    --bs-btn-hover-border-color: var(--sge-text-danger);    --bs-btn-hover-color: #10161d; --bs-btn-active-bg: var(--sge-text-danger);    --bs-btn-active-border-color: var(--sge-text-danger);    --bs-btn-active-color: #10161d; --bs-btn-disabled-color: var(--sge-text-danger);    --bs-btn-disabled-border-color: var(--sge-text-danger); }
[data-bs-theme="dark"] .btn-outline-warning   { --bs-btn-color: var(--sge-text-warning);   --bs-btn-border-color: var(--sge-text-warning);   --bs-btn-hover-bg: var(--sge-text-warning);   --bs-btn-hover-border-color: var(--sge-text-warning);   --bs-btn-hover-color: #10161d; --bs-btn-active-bg: var(--sge-text-warning);   --bs-btn-active-border-color: var(--sge-text-warning);   --bs-btn-active-color: #10161d; --bs-btn-disabled-color: var(--sge-text-warning);   --bs-btn-disabled-border-color: var(--sge-text-warning); }
[data-bs-theme="dark"] .btn-outline-info      { --bs-btn-color: var(--sge-text-info);      --bs-btn-border-color: var(--sge-text-info);      --bs-btn-hover-bg: var(--sge-text-info);      --bs-btn-hover-border-color: var(--sge-text-info);      --bs-btn-hover-color: #10161d; --bs-btn-active-bg: var(--sge-text-info);      --bs-btn-active-border-color: var(--sge-text-info);      --bs-btn-active-color: #10161d; --bs-btn-disabled-color: var(--sge-text-info);      --bs-btn-disabled-border-color: var(--sge-text-info); }

/* Un sous-arbre épinglé au thème clair dans une page sombre — le contrôle
   d'accueil — reste atteint par les sélecteurs descendants ci-dessus. Ce bloc
   les annule ; à spécificité égale, c'est l'ordre d'écriture qui tranche, d'où
   sa place juste après. */
[data-bs-theme="light"] .btn-outline-primary,
[data-bs-theme="light"] .btn-outline-secondary,
[data-bs-theme="light"] .btn-outline-success,
[data-bs-theme="light"] .btn-outline-danger,
[data-bs-theme="light"] .btn-outline-warning,
[data-bs-theme="light"] .btn-outline-info {
    --bs-btn-hover-color: #fff;
    --bs-btn-active-color: #fff;
}

[data-bs-theme="light"] .btn-outline-primary   { --bs-btn-color: #2c3e50; --bs-btn-border-color: #2c3e50; --bs-btn-hover-bg: #2c3e50; --bs-btn-hover-border-color: #2c3e50; --bs-btn-active-bg: #2c3e50; --bs-btn-active-border-color: #2c3e50; --bs-btn-disabled-color: #2c3e50; --bs-btn-disabled-border-color: #2c3e50; }
[data-bs-theme="light"] .btn-outline-secondary { --bs-btn-color: #5c636a; --bs-btn-border-color: #6c757d; --bs-btn-hover-bg: #6c757d; --bs-btn-hover-border-color: #6c757d; --bs-btn-active-bg: #6c757d; --bs-btn-active-border-color: #6c757d; --bs-btn-disabled-color: #5c636a; --bs-btn-disabled-border-color: #6c757d; }
[data-bs-theme="light"] .btn-outline-success   { --bs-btn-color: #146c43; --bs-btn-border-color: #198754; --bs-btn-hover-bg: #198754; --bs-btn-hover-border-color: #198754; --bs-btn-active-bg: #198754; --bs-btn-active-border-color: #198754; --bs-btn-disabled-color: #146c43; --bs-btn-disabled-border-color: #198754; }
[data-bs-theme="light"] .btn-outline-danger    { --bs-btn-color: #b02a37; --bs-btn-border-color: #dc3545; --bs-btn-hover-bg: #dc3545; --bs-btn-hover-border-color: #dc3545; --bs-btn-active-bg: #dc3545; --bs-btn-active-border-color: #dc3545; --bs-btn-disabled-color: #b02a37; --bs-btn-disabled-border-color: #dc3545; }
[data-bs-theme="light"] .btn-outline-warning   { --bs-btn-color: #8a5a00; --bs-btn-border-color: #997404; --bs-btn-hover-bg: #ffc107; --bs-btn-hover-border-color: #ffc107; --bs-btn-hover-color: #000; --bs-btn-active-bg: #ffc107; --bs-btn-active-border-color: #ffc107; --bs-btn-active-color: #000; --bs-btn-disabled-color: #8a5a00; --bs-btn-disabled-border-color: #997404; }
[data-bs-theme="light"] .btn-outline-info      { --bs-btn-color: #1566a8; --bs-btn-border-color: #1566a8; --bs-btn-hover-bg: #1566a8; --bs-btn-hover-border-color: #1566a8; --bs-btn-active-bg: #1566a8; --bs-btn-active-border-color: #1566a8; --bs-btn-disabled-color: #1566a8; --bs-btn-disabled-border-color: #1566a8; }

/* `.btn-link` et `.nav-link` héritent de `--bs-link-color` ; en thème clair le
   bleu Bootstrap sur blanc tient (4,6:1), en sombre il faut la teinte claire. */
[data-bs-theme="dark"] .btn-link { --bs-btn-color: var(--sge-text-primary); --bs-btn-hover-color: #cfe2f3; }

/* 1.4.11 — barres de progression. Ce qui doit se voir n'est pas la barre sur la
   page mais la part REMPLIE sur la part vide : c'est elle qui porte l'état, et
   à 4 px de haut elle n'a que sa couleur pour le dire. Deux paires échouaient.

   En thème sombre, la piste (#343a40) est trop proche des teintes de barre :
   2,54 à 2,56:1 pour le bleu, le vert et le rouge. On ramène la piste au fond
   de page, plus sombre, et les quatre teintes repassent (3,40 à 9,46:1).

   En thème clair, seul le jaune manque — 1,37:1 sur une piste très claire.
   Assombrir la piste pour lui seul dégraderait toutes les autres barres : c'est
   donc le jaune qui prend l'or sombre déjà employé par `.btn-outline-warning`
   (3,65:1), la teinte d'avertissement restant reconnaissable. */
[data-bs-theme="dark"] .progress,
[data-bs-theme="dark"] .progress-stacked {
    --bs-progress-bg: var(--bs-body-bg);
}

.progress-bar.bg-warning {
    background-color: #997404 !important;
}

/* ---------------------------------------------------------------------------
 * 4. Aucun trait ajouté par l'accessibilité — et les deux de Bootstrap qui
 *    encadraient un champ au clic, ramenés à leur plus simple expression
 *
 * Le critère 1.4.11 (« contraste non textuel ») demande que la LIMITE d'un
 * contrôle atteigne 3:1 avec ce qui l'entoure. Bootstrap ne l'atteint nulle
 * part, et le seul moyen d'y arriver est de peindre un trait : une bordure là
 * où le composant en a une, une ombre interne là où il n'en a pas. C'est ce que
 * le socle d'accessibilité avait fait, partout, et c'est ce dont l'interface
 * s'est trouvée couverte. Tout a été retiré :
 *
 *   - la bordure #6c757d (4,69:1) autour de `.form-control`, `.form-select`,
 *     `.input-group-text` et de la croix collée aux champs de recherche ;
 *   - la même autour de `.form-check-input`, case cochée comprise ;
 *   - l'anneau de focus de 3 px décalé de 2 px, doublé d'une ombre blanche ;
 *   - les bordures rendues aux aplats qui se fondent dans leur fond —
 *     `.btn-warning`, `.btn-light`, et en thème sombre `.btn-primary` et
 *     `.btn-dark` ;
 *   - l'ombre interne de 1 px cerclant, en thème sombre, l'onglet actif,
 *     l'élément de liste actif, l'entrée de menu active et la page courante ;
 *   - le liseré de 4 px sur la suggestion d'adresse survolée.
 *
 * Ne reste que la couleur. Les composants reprennent les traits de Bootstrap,
 * discrets, et l'application ressemble de nouveau à ce qu'elle est.
 *
 * Le coût, dit franchement plutôt que passé sous silence : le critère 1.4.11
 * n'est plus atteint pour la limite des contrôles. C'est un arbitrage assumé,
 * pas un oubli. La liste `COMPOSANTS_NON_TEXTUELS` de `assets/js/dev/contrast.js`
 * a été réduite en conséquence et porte la même note — un banc qui échoue à
 * chaque exécution sur un point que personne ne corrigera cesse d'être un
 * signal. Ce que le projet tient, et que le banc vérifie, c'est le contraste
 * des TEXTES (1.4.3), partout et dans les deux thèmes, plus le seul contraste
 * non textuel qui reste affaire de couleur : la part remplie d'une barre de
 * progression sur sa piste.
 * ------------------------------------------------------------------------ */

/* Et deux traits de Bootstrap lui-même, que le ménage ci-dessus laissait en
   place et qui restaient les plus voyants de l'interface.

   1. LE HALO DE FOCUS D'UN CHAMP. Bootstrap pose
      `box-shadow: 0 0 0 .25rem rgba(13,110,253,.25)` sur `.form-control:focus`,
      `.form-select:focus` et `.form-check-input:focus` — quatre pixels de bleu
      tout autour. Et sur un champ de saisie, contrairement à un bouton, la
      règle est en `:focus` et non en `:focus-visible` : elle se déclenche au
      simple CLIC, pas seulement à la tabulation. Cliquer dans une recherche
      encadrait donc le champ et sa croix d'un bandeau bleu épais.

      Le halo ne tombe pas — il maigrit. Quatre pixels sur les quatre côtés,
      c'est ce qui donnait un bandeau ; deux se voient encore sans encadrer, et
      quelque chose doit bien dire où se trouve le curseur. La teinte et
      l'opacité ne bougent pas : c'est l'épaisseur, et elle seule, qui faisait
      le cadre. La bordure continue par ailleurs de passer au bleu (#86b7fe)
      dans la même règle de Bootstrap.

      À noter pour qui relira : ce halo est ANTÉRIEUR au socle d'accessibilité
      (il est dans Bootstrap depuis toujours, et l'interface l'avait déjà avant
      le 12 août). Le retirer entièrement n'aurait donc pas été « défaire le
      WCAG », mais changer le comportement d'origine — d'où ce réglage plutôt
      qu'une suppression.

   2. LA BORDURE D'UN BOUTON COLLÉ À UN CHAMP. La croix qui vide une recherche
      est un `.btn-outline-secondary` : Bootstrap lui donne le gris #6c757d,
      quand le champ auquel elle est soudée porte #dee2e6. Deux gris dans un
      même bloc, dont le plus sombre dessine un cadre là où l'on attend une
      continuité. La croix prend donc le gris du champ. Les boutons colorés
      d'un groupe de saisie gardent le leur : ce sont des actions, pas des
      accessoires de champ. */
.form-control:focus,
.form-select:focus,
.form-check-input:focus {
    box-shadow: 0 0 0 .125rem rgba(13, 110, 253, .25);   /* Bootstrap : .25rem */
}

.input-group .btn-outline-secondary {
    --bs-btn-border-color: var(--bs-border-color);
    --bs-btn-hover-border-color: var(--bs-border-color);
    --bs-btn-disabled-border-color: var(--bs-border-color);
}

/* ---------------------------------------------------------------------------
 * 5. Focus — 2.4.11 (non masqué) et lien d'évitement
 *
 * L'anneau lui-même est celui de Bootstrap (voir la section 4). Ne restent ici
 * que les deux réglages qui ne peignent rien, plus le lien d'évitement, qui est
 * un élément à lui seul et non un trait posé sur un autre.
 * ------------------------------------------------------------------------ */

/* 2.4.11 Focus non masqué (minimum) — la navbar est `sticky top:0` et le
   bandeau de développement `sticky` au-dessus. Sans marge de défilement, le
   navigateur amène l'élément focalisé exactement sous eux, donc invisible. */
html {
    scroll-padding-top: calc(var(--dev-banner-offset, 0px) + 5rem);
    scroll-padding-bottom: 2rem;
}

/* Le lien d'évitement doit rester au-dessus de la navbar collée. */
.skip-link {
    position: absolute;
    top: 0;
    left: 0;
    z-index: 2000;
    margin: 0.5rem;
    padding: 0.75rem 1.25rem;
    border-radius: 0.375rem;
    background-color: #ffffff;
    color: #10161d;
    font-weight: 600;
    text-decoration: underline;
    box-shadow: 0 0 0 3px #003a8c;
    transform: translateY(-200%);
}

.skip-link:focus,
.skip-link:focus-visible {
    transform: translateY(0);
}

/* La cible du lien d'évitement reçoit `tabindex="-1"` : sans cette règle,
   certains navigateurs y dessinent un anneau permanent au clic. */
[tabindex="-1"]:focus:not(:focus-visible) {
    outline: none !important;
    box-shadow: none !important;
}

/* ---------------------------------------------------------------------------
 * 6. Taille de cible minimale — 2.5.8 (AA, nouveau en WCAG 2.2)
 *
 * 24x24 px CSS pour toute cible pointée. Les liens en ligne dans un paragraphe
 * bénéficient de l'exception « en ligne » et ne sont pas visés. Ces règles ne
 * peignent rien : elles posent un plancher de dimension, que la plupart des
 * composants dépassent déjà. Elles se vérifient par `composer cibles:check`.
 * ------------------------------------------------------------------------ */
.btn,
.page-link,
.dropdown-item,
.btn-close,
.nav-link,
.navbar-toggler,
.accordion-button,
[role="button"],
.th-sortable {
    min-width: 24px;
    min-height: 24px;
}

/* Les boutons réduits à leur icône (`p-0`, `btn-link`) tombent sous le seuil :
   on les recentre dans une cible de 24 px sans changer la taille de l'icône. */
.btn.p-0,
.btn.btn-link.p-0 {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.5rem;
    min-height: 1.5rem;
}

/* 2.5.8 — Bootstrap dimensionne la case à cocher en `1em`, soit 16 px. Elle
   passe à 24 px, le minimum du critère, pour que la CASE elle-même soit une
   cible atteignable et pas seulement son étiquette : sur le terrain, on coche
   avec le pouce, gant compris, sans viser le mot à côté. Les retraits suivent,
   pour conserver l'écart de 0,5 rem entre la case et son étiquette. */
.form-check-input {
    width: 1.5rem;
    height: 1.5rem;
    margin-top: 0;
}

.form-check {
    min-height: 1.5rem;
    padding-left: 2rem;
}

.form-check .form-check-input {
    margin-left: -2rem;
}

/* L'interrupteur garde sa proportion de 2:1 : 48 × 24 px. */
.form-switch {
    padding-left: 3.5rem;
}

.form-switch .form-check-input {
    width: 3rem;
    margin-left: -3.5rem;
}

/* ---------------------------------------------------------------------------
 * 7. Mouvement — 2.3.3 (animations à la demande) et 2.2.2 (arrêt du mouvement)
 * ------------------------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.001ms !important;
        scroll-behavior: auto !important;
    }

    /* Les indicateurs d'attente perdraient tout sens en s'arrêtant net : on les
       ralentit fortement au lieu de les supprimer. */
    .spinner-border,
    .spinner-grow,
    .bi-spin {
        animation-duration: 2s !important;
        animation-iteration-count: infinite !important;
    }
}

/* ---------------------------------------------------------------------------
 * 8. Divers
 * ------------------------------------------------------------------------ */

/* 1.4.1 — l'état « désactivé » ne doit pas reposer sur la seule opacité. */
[aria-disabled="true"] {
    cursor: not-allowed;
}

/* 1.4.11 — un indicateur de tri porte de l'information : à 25 % d'opacité il
   n'est plus perceptible. */
.th-sortable .sort-indicator {
    opacity: 0.7;
}

.th-sortable .sort-indicator--active {
    opacity: 1;
}

/* 2.4.7 — le survol seul ne suffit pas à signaler un en-tête triable ;
   on double par le focus clavier. */
.th-sortable:hover,
.th-sortable:has(.th-sort-btn:focus-visible) {
    background-color: var(--bs-table-hover-bg, rgba(0, 0, 0, .06));
}

/* Le bouton de tri glissé dans l'en-tête ne doit rien changer à son allure :
   il hérite typographie et couleur, et n'apporte que la focalisation. */
.th-sort-btn {
    display: inline;
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    color: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    text-align: inherit;
    cursor: pointer;
}

/* Les icônes décoratives ne doivent jamais porter d'information seule : quand
   une icône est le seul contenu d'un bouton, le nom accessible vient de
   `aria-label`. Cette règle sert de garde-fou visuel en cas d'oubli côté
   markup — l'icône reste centrée dans sa cible. */
.btn > i:only-child,
.btn > .ti-motorbike:only-child {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

/* 1.4.13 — une infobulle doit pouvoir être survolée sans disparaître.
   Bootstrap la rend inerte au pointeur ; combinée au délai de masquage posé
   dans app.js, cette règle permet d'aller lire une infobulle longue, par
   exemple à la loupe. */
.tooltip {
    pointer-events: auto;
}

/* ---------------------------------------------------------------------------
 * 9. Éditeur Quill
 *
 * Le thème « snow » de Quill n'existe qu'en clair : barre d'outils blanche,
 * icônes tracées en #444, zone de saisie blanche. Posé dans une page sombre,
 * il formait un rectangle clair, et le texte saisi — qui hérite de la couleur
 * du corps — s'y écrivait en clair sur clair. On lui donne sa déclinaison.
 * ------------------------------------------------------------------------ */
[data-bs-theme="dark"] .ql-toolbar.ql-snow,
[data-bs-theme="dark"] .ql-container.ql-snow {
    border-color: var(--bs-border-color);
}

[data-bs-theme="dark"] .ql-toolbar.ql-snow {
    background-color: var(--bs-tertiary-bg);
}

[data-bs-theme="dark"] .ql-container.ql-snow,
[data-bs-theme="dark"] .ql-editor {
    background-color: var(--bs-body-bg);
    color: var(--bs-body-color);
}

[data-bs-theme="dark"] .ql-editor.ql-blank::before {
    color: var(--sge-text-muted);
}

/* La règle ci-dessus ne vaut que pour le texte qui hérite. Or les contenus
   éditoriaux traînent des couleurs en ligne, laissées par un copier-coller ou
   par Quill lui-même : le gabarit livré de la modale s'ouvre sur un paragraphe
   en `color: rgb(33, 37, 41)` — soit exactement le fond de l'éditeur sombre.
   La première ligne ne s'affichait pas faiblement : elle disparaissait. Les
   conditions générales, elles, sont truffées de
   `background-color: rgb(255, 255, 255)`, qui rallume des bandes claires au
   milieu d'une page sombre.

   On rend donc ces contenus au thème, texte et fond. Sans condition de thème,
   pour deux raisons : les surfaces de lecture neutralisent déjà les mêmes
   déclarations (`.rf-terms` et `#registrationFlowModal` dans registration.css,
   `.ticket-conditions-body` dans ticket.php) et l'éditeur doit montrer ce que
   le lecteur verra ; et ces deux barres d'outils — CG et modale — n'ont aucun
   sélecteur de couleur, donc rien de ce qui tombe ici n'est un choix d'auteur.

   L'éditeur d'email reste à l'écart : sa barre d'outils a ses boutons de
   couleur et de fond, et son rendu final est un message lu dans un client
   mail, sur fond blanc. Là, la couleur est voulue. */
#det-termsEditorContainer .ql-editor [style*="color"],
#det-modalEditorContainer .ql-editor [style*="color"] {
    color: inherit !important;
}

#det-termsEditorContainer .ql-editor [style*="background"],
#det-modalEditorContainer .ql-editor [style*="background"] {
    background-color: transparent !important;
}

/* Même famille, autre cause : le lien de Quill est peint en `#06c` en dur dans
   la feuille du thème « snow ». Sur blanc il passe (5,6:1), sur le fond sombre
   de l'éditeur il tombe à 2,8:1. On lui rend la teinte de lien du thème, qui
   est tenue des deux côtés (--bs-link-color, plus haut dans ce fichier). */
.ql-editor a {
    color: var(--bs-link-color);
}

/* Les icônes de la barre d'outils sont des SVG tracés : c'est `stroke` et
   `fill` qu'il faut reprendre, pas `color`. */
[data-bs-theme="dark"] .ql-snow .ql-stroke,
[data-bs-theme="dark"] .ql-snow .ql-stroke-miter {
    stroke: var(--bs-body-color);
}

[data-bs-theme="dark"] .ql-snow .ql-fill,
[data-bs-theme="dark"] .ql-snow .ql-stroke.ql-fill {
    fill: var(--bs-body-color);
}

[data-bs-theme="dark"] .ql-snow .ql-picker,
[data-bs-theme="dark"] .ql-snow .ql-picker-label {
    color: var(--bs-body-color);
}

[data-bs-theme="dark"] .ql-snow .ql-picker-options {
    background-color: var(--bs-body-bg);
    border-color: var(--bs-border-color);
}

[data-bs-theme="dark"] .ql-snow.ql-toolbar button:hover .ql-stroke,
[data-bs-theme="dark"] .ql-snow.ql-toolbar button.ql-active .ql-stroke,
[data-bs-theme="dark"] .ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke {
    stroke: var(--sge-text-primary);
}

[data-bs-theme="dark"] .ql-snow.ql-toolbar button:hover .ql-fill,
[data-bs-theme="dark"] .ql-snow.ql-toolbar button.ql-active .ql-fill {
    fill: var(--sge-text-primary);
}

[data-bs-theme="dark"] .ql-snow.ql-toolbar button:hover,
[data-bs-theme="dark"] .ql-snow.ql-toolbar button.ql-active,
[data-bs-theme="dark"] .ql-snow.ql-toolbar .ql-picker-item:hover {
    color: var(--sge-text-primary);
}

/* 2.5.8 — les boutons de la barre d'outils tombent à 24x18 px chez Quill. */
.ql-snow.ql-toolbar button,
.ql-snow .ql-toolbar button {
    min-width: 24px;
    min-height: 24px;
}
