/* ============================================================
   chargeur-app.css - Public shortcode styles
   Compatible with Divi 5 (uses CSS custom properties with fallbacks)
   ============================================================ */

/* ── Police auto-hébergée (audit préprod) ─────────────────────────────────
   Inter n'est plus chargée depuis fonts.googleapis.com/fonts.gstatic.com : ça
   transmettait l'IP/le user-agent de chaque visiteur à Google à chaque page,
   sans consentement ni mention, pour une application de données RH
   (nLPD/RGPD). Fichiers statiques (google-webfonts-helper, sous-ensemble
   latin) dans assets/fonts/ - chemin relatif depuis ce fichier
   (assets/css/chargeur-app.css → ../fonts/). Seules les 4 graisses
   effectivement utilisées par ce fichier (400/500/600/700, voir les
   `font-weight` ci-dessous) sont déclarées. */
@font-face {
    font-family: 'Inter';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('../fonts/inter-v20-latin-400.woff2') format('woff2');
}
@font-face {
    font-family: 'Inter';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url('../fonts/inter-v20-latin-500.woff2') format('woff2');
}
@font-face {
    font-family: 'Inter';
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url('../fonts/inter-v20-latin-600.woff2') format('woff2');
}
@font-face {
    font-family: 'Inter';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url('../fonts/inter-v20-latin-700.woff2') format('woff2');
}

/* ── Design tokens ────────────────────────────────────────────────────────── */
/* Palette "Data-Dense Dashboard" : bleu données + accent ambre (WCAG AA).
   Les couleurs sont volontairement fixes (plus d'héritage du thème Divi). */
:root {
    --chargeur-font:            'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    --chargeur-accent:          #1e40af;   /* primary : boutons, onglets, focus */
    --chargeur-accent-hover:    color-mix(in srgb, var(--chargeur-accent) 80%, #000 20%);
    --chargeur-accent-light:    color-mix(in srgb, var(--chargeur-accent) 10%, transparent);
    --chargeur-secondary:       #3b82f6;   /* liens, états hover */
    --chargeur-cta:             #d97706;   /* accent ambre : actions d'envoi (transmission) */
    --chargeur-text:            #1e3a8a;
    --chargeur-label:           #4b5563;
    --chargeur-border:          #dbeafe;
    --chargeur-icon-border:     #d1d5db;   /* gris neutre, uniforme pour toutes les actions icône au repos */
    --chargeur-radius:          8px;
    --chargeur-radius-card:     14px;
    --chargeur-bg:              #f8fafc;
    --chargeur-muted:           #e9eef6;   /* survol de lignes, zones neutres */
    --chargeur-input-bg:        #fff;
    --chargeur-card-bg:         #fff;
    --chargeur-card-shadow:     0 1px 3px rgba(0, 0, 0, .06), 0 4px 18px rgba(0, 0, 0, .09);
    --chargeur-success-bg:      #f0fdf4;
    --chargeur-success-border:  #16a34a;
    --chargeur-success-color:   #166534;
    --chargeur-error-bg:        #fef2f2;
    --chargeur-error-border:    #dc2626;
    --chargeur-error-color:     #991b1b;
    /* "En attente de transmission" (badge 3 états de
       Gestion) - même ambre que --chargeur-cta, jamais une teinte nouvelle. */
    --chargeur-warning-bg:      #fffbeb;
    --chargeur-warning-border:  #d97706;
    --chargeur-warning-color:   #92400e;
    --chargeur-gap:             1rem;
    --chargeur-transition:      0.3s ease;
}

/* ── Base font ────────────────────────────────────────────────────────────── */
.chargeur-login,
.chargeur-admin,
.chargeur-operations,
.chargeur-supervision,
.chargeur-nav,
.chargeur-nav-login,
.chargeur-nav-logout {
    font-family: var(--chargeur-font);
}

/* Form controls don't inherit the font by default. */
.chargeur-input,
.chargeur-btn,
.chargeur-link {
    font-family: inherit;
}

/* ── Login shortcode ──────────────────────────────────────────────────────── */
/* Carte auto-limitée : un formulaire de connexion (e-mail,
   mot de passe, code OTP à 6 chiffres) n'a jamais besoin de plus de ~420px -
   au-delà, les lignes de saisie s'étirent et deviennent inconfortables à lire/
   scanner (contredit `line-length-control`). Ne dépend plus de la largeur de la
   colonne Divi qui l'accueille (pleine largeur = beaucoup trop large si le
   shortcode est déposé dans une section/ligne pleine largeur) - se centre lui-
   même via `margin: auto`, quelle que soit la largeur du conteneur parent.
   `min(420px, calc(100vw - 2rem))` garantit aussi une marge de sécurité de 1rem
   de chaque côté sur un très petit écran, même si le conteneur parent n'en a
   aucune - même filet de sécurité déjà utilisé pour .chargeur-nav__centre-listbox. */
.chargeur-login {
    box-sizing: border-box;
    width: 100%;
    max-width: min(420px, calc(100vw - 2rem));
    margin: 0 auto;
    background: var(--chargeur-card-bg);
    border-radius: var(--chargeur-radius-card);
    box-shadow: var(--chargeur-card-shadow);
    padding: clamp(1.5rem, 4vw, 2.25rem);
}

.chargeur-login__title {
    margin: 0 0 0.2rem;
    font-size: 1.2rem;
    font-weight: 700;
    color: var(--chargeur-text);
    letter-spacing: -0.01em;
}

.chargeur-login__hint {
    margin: 0 0 var(--chargeur-gap);
    font-size: 0.8rem;
    color: var(--chargeur-label);
    line-height: 1.5;
}

.chargeur-login__form {
    display: flex;
    flex-direction: column;
    gap: var(--chargeur-gap);
}

/* Piège CSS [hidden] vs display auteur : une règle auteur (`display: ...`)
   l'emporte toujours sur le UA stylesheet `[hidden] { display: none }`,
   quelle que soit la spécificité - un sélecteur `[hidden]` explicite est
   donc nécessaire partout où une classe définit son propre `display` (voir
   aussi .chargeur-field[hidden]/.chargeur-btn[hidden]/
   .chargeur-tab-panel[hidden]/.chargeur-search-field__clear[hidden]/
   .chargeur-bulk-actions[hidden] plus bas, même piège). #chargeur-forgot-form
   (unique <form> de cette classe réellement masqué par JS -
   forgotForm.hidden = true, un coup unique après un envoi réussi pour
   éviter un renvoi immédiat, voir chargeur-app.js) restait donc visible
   malgré cet attribut. */
.chargeur-login__form[hidden] {
    display: none;
}

/* Step entry animation */
.chargeur-login__step:not([hidden]) {
    animation: chargeur-step-in 0.22s ease both;
}

@keyframes chargeur-step-in {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* Authenticated state */
.chargeur-login__authenticated .chargeur-login__greeting {
    margin: 0 0 var(--chargeur-gap);
    font-size: 1rem;
    color: var(--chargeur-text);
}

/* ── Form field ───────────────────────────────────────────────────────────── */
.chargeur-field {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

/* Même piège que .chargeur-login__form[hidden] plus haut (règle auteur
   `display` vs UA stylesheet [hidden]) - le champ "Absence" de la modale
   Ajouter/Modifier de Gestion restait visible ET focusable au clavier même
   masqué par JS (populateLigneAbsenceOptions() pour un Type = Timbrage)
   sans cet override. */
.chargeur-field[hidden] {
    display: none;
}

.chargeur-label {
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--chargeur-label);
    letter-spacing: 0.01em;
}

.chargeur-input {
    width: 100%;
    box-sizing: border-box;
    padding: 0.65rem 0.875rem;
    border: 1.5px solid var(--chargeur-border);
    border-radius: var(--chargeur-radius);
    background: var(--chargeur-input-bg);
    color: var(--chargeur-text);
    font-size: 1rem;
    transition:
        border-color var(--chargeur-transition),
        box-shadow   var(--chargeur-transition);
}

.chargeur-input:focus {
    outline: none;
    border-color: var(--chargeur-accent);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--chargeur-accent) 18%, transparent);
}

/* Un <select class="chargeur-input"> natif (Type, options statiques)
   s'affiche dans le bleu de `--chargeur-text`, mais un <select> aux options
   peuplées en JS (Code, libellés plus longs) peut retomber sur le rendu
   natif du navigateur (texte noir, police système) selon le chemin de
   rendu interne déclenché. Apparence entièrement reprise à notre charge
   pour rester uniforme sur tout `.chargeur-input`. Le panneau OUVERT reste
   en partie hors de portée de CSS dans certains navigateurs (chrome natif
   du système) - seule la boîte fermée est garantie identique. */
select.chargeur-input {
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
    padding-right: 2.25rem;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%234b5563' d='M3.7 5.7a1 1 0 0 1 1.4 0L8 8.6l2.9-2.9a1 1 0 1 1 1.4 1.4l-3.6 3.6a1 1 0 0 1-1.4 0L3.7 7.1a1 1 0 0 1 0-1.4z'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 0.75rem center;
    background-size: 0.85rem;
}

/* Respecté par la plupart des navigateurs pour le contenu du menu déroulant
   (contrairement à la police, voir plus haut) - jamais laissé au noir par
   défaut du navigateur. Sert aussi de repli si chargeur-select.js ne
   s'exécute pas (JS désactivé/erreur) : le <select> natif reste alors
   affiché tel quel, avec cette même apparence de base. */
select.chargeur-input option {
    color: var(--chargeur-text);
    background: var(--chargeur-input-bg);
}

/* ── Menu déroulant maison (chargeur-select.js) ───────────────────────────
   Remplace l'apparence de CHAQUE <select class="chargeur-input"> - un select
   natif ne permet aucun contrôle fiable sur son panneau ouvert (police,
   tailles, surlignage : chrome de l'OS/du navigateur). Le <select> d'origine
   reste dans le DOM (voir chargeur-select.js), cet ensemble de règles ne
   concerne que le trigger + le panneau construits par-dessus. */

.chargeur-select {
    position: relative;
    display: block;
}

/* Masqué plutôt qu'un simple visually-hidden : chargeur-select.js le rend
   volontairement non focalisable (tabIndex=-1, aria-hidden) et jamais
   affiché - display:none n'affecte en rien .value/dispatchEvent(), utilisés
   normalement par tout le reste du code. */
.chargeur-select__native {
    display: none;
}

.chargeur-select__trigger {
    display: flex;
    align-items: center;
    width: 100%;
    text-align: left;
    cursor: pointer;
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
    padding-right: 2.25rem;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%234b5563' d='M3.7 5.7a1 1 0 0 1 1.4 0L8 8.6l2.9-2.9a1 1 0 1 1 1.4 1.4l-3.6 3.6a1 1 0 0 1-1.4 0L3.7 7.1a1 1 0 0 1 0-1.4z'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 0.75rem center;
    background-size: 0.85rem;
}

.chargeur-select__trigger:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.chargeur-select__label {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* position/top/left/width posés en ligne par chargeur-select.js
   (getBoundingClientRect() du trigger, à chaque ouverture) - rattaché à
   <body> (jamais à .chargeur-select), même raison que .chargeur-modal
   (portalModalToBody()) : échapper à l'overflow:auto d'un ancêtre
   (.chargeur-modal__dialog) qui rognerait sinon un menu Code de 9 options. */
.chargeur-select__listbox {
    z-index: 2147483647;
    margin: 0.25rem 0 0;
    padding: 0.25rem;
    list-style: none;
    max-height: 260px;
    overflow-y: auto;
    /* `overflow-x` explicite -
       laisser `overflow-x` à sa valeur initiale (`visible`) alors que
       `overflow-y` vaut autre chose que `visible` force le navigateur, par
       spec, à promouvoir aussi `overflow-x` à `auto` : le panneau devenait
       scrollable horizontalement sans qu'aucune règle ne le demande, et la
       molette de la souris pouvait alors faire défiler cet axe au lieu du
       vertical prévu. Jamais de défilement horizontal ici - un libellé long
       passe à la ligne (voir .chargeur-select__option ci-dessous), il ne
       déborde jamais. */
    overflow-x: hidden;
    background: var(--chargeur-input-bg);
    border: 1.5px solid var(--chargeur-border);
    border-radius: var(--chargeur-radius);
    box-shadow: var(--chargeur-card-shadow);
    font-size: 1rem;
    color: var(--chargeur-text);
}

.chargeur-select__listbox[hidden] {
    display: none;
}

.chargeur-select__option {
    padding: 0.5rem 0.75rem;
    border-radius: calc(var(--chargeur-radius) - 2px);
    cursor: pointer;
    white-space: normal; /* jamais tronqué dans le panneau ouvert, contrairement au trigger fermé - la lisibilité prime sur la compacité ici */
    overflow-wrap: anywhere; /* même un mot unique très long (sans espace) passe à la ligne plutôt que de forcer une largeur supérieure au panneau */
}

.chargeur-select__option.is-active {
    background: var(--chargeur-muted);
}

.chargeur-select__option.is-selected {
    background: color-mix(in srgb, var(--chargeur-accent) 12%, transparent);
    font-weight: 600;
}

.chargeur-select__option.is-disabled {
    opacity: 0.5;
    cursor: not-allowed;
    pointer-events: none;
}

/* Validation de format en ligne : jamais la couleur
   seule (`color-not-only`) : toujours accompagnée du message texte dans le
   .chargeur-msg du même formulaire, jamais du bord seul. */
.chargeur-input.is-invalid {
    border-color: var(--chargeur-error-border);
}

.chargeur-input.is-invalid:focus {
    border-color: var(--chargeur-error-border);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--chargeur-error-border) 18%, transparent);
}

/* Explicit placeholder color instead of the browser default (which varies
   by browser/OS and was never intentionally styled here) - same muted gray
   already used for labels/secondary text, so every field's hint text
   looks consistent and stays readable (~7:1 contrast on white). */
.chargeur-input::placeholder {
    color: var(--chargeur-label);
    opacity: 1;
}

.chargeur-input--otp {
    max-width: 180px;
    letter-spacing: 0.35em;
    font-size: 1.5rem;
    font-family: ui-monospace, 'Courier New', monospace;
    font-weight: 700;
    text-align: center;
    padding: 0.75rem 1rem;
}

/* ── Buttons ──────────────────────────────────────────────────────────────── */
.chargeur-form__actions {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
}

.chargeur-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0.6rem 1.5rem;
    min-height: 42px;
    border: none;
    border-radius: var(--chargeur-radius);
    font-size: 1rem;
    font-weight: 600;
    line-height: 1.4;
    cursor: pointer;
    transition:
        background  var(--chargeur-transition),
        box-shadow  var(--chargeur-transition),
        opacity     var(--chargeur-transition);
}

.chargeur-btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* Même piège que .chargeur-login__form[hidden] plus haut (règle auteur
   `display` vs UA stylesheet [hidden]) - jamais remarqué avant le bouton
   "Importer" de la modale d'upload : premier endroit du code à faire
   varier `.hidden` sur un `.chargeur-btn`. Jamais !important. */
.chargeur-btn[hidden] {
    display: none;
}

.chargeur-btn--primary {
    background: var(--chargeur-accent);
    color: #fff;
    box-shadow: 0 1px 2px rgba(0, 0, 0, .08);
}

.chargeur-btn--primary:hover:not(:disabled) {
    background: var(--chargeur-accent-hover);
    box-shadow: 0 2px 8px color-mix(in srgb, var(--chargeur-accent) 35%, transparent);
}

.chargeur-btn--secondary {
    background: transparent;
    color: var(--chargeur-accent);
    border: 1.5px solid var(--chargeur-accent);
}

.chargeur-btn--secondary:hover:not(:disabled) {
    background: var(--chargeur-accent-light);
}

/* Ghost / link-style button */
.chargeur-link {
    background: none;
    border: none;
    padding: 0;
    margin-top: 0.5rem;
    font-size: 1rem;
    color: var(--chargeur-label);
    cursor: pointer;
    text-decoration: none;
    opacity: 0.8;
    transition: opacity var(--chargeur-transition), color var(--chargeur-transition);
}

.chargeur-link:hover {
    opacity: 1;
    color: var(--chargeur-secondary);
}

/* Password field + show/hide toggle */
.chargeur-field__with-toggle {
    position: relative;
    display: flex;
    align-items: center;
}

.chargeur-field__with-toggle .chargeur-input {
    padding-right: 5rem;
}

.chargeur-field__toggle {
    position: absolute;
    right: 0.875rem;
    margin-top: 0;
    font-size: 0.85rem;
    white-space: nowrap;
}

/* Ligne secondaire sous un champ (ex. nom + prénom du collaborateur sous
   Matricule, modale Ajouter/Modifier de Gestion) -
   même style que .chargeur-table__muted, dédié aux formulaires plutôt qu'aux
   tableaux (nom de classe explicite, jamais réutilisé hors de son contexte). */
.chargeur-field__hint {
    margin-top: 0.25rem;
    font-size: 0.8rem;
    color: var(--chargeur-label);
}

/* ── Spinner ──────────────────────────────────────────────────────────────── */
.chargeur-spinner {
    display: inline-block;
    width: 1.25rem;
    height: 1.25rem;
    border: 2px solid color-mix(in srgb, var(--chargeur-accent) 20%, transparent);
    border-top-color: var(--chargeur-accent);
    border-radius: 50%;
    animation: chargeur-spin 0.65s linear infinite;
    vertical-align: middle;
    flex-shrink: 0;
    align-self: center;
}

.chargeur-spinner[hidden] {
    display: none;
}

/* Injected into a .chargeur-btn--small (table row actions) or next to an
   inline-edit input (référentiel prefixe/code cells) - smaller footprint,
   see setLoading()/injectSpinner() in chargeur-app-admin.js. */
.chargeur-btn--small .chargeur-spinner,
.chargeur-spinner--field {
    width: 0.9rem;
    height: 0.9rem;
    border-width: 2px;
}

.chargeur-btn--small .chargeur-spinner {
    margin-right: 0.4rem;
}

.chargeur-spinner--field {
    margin-left: 0.5rem;
}

@keyframes chargeur-spin {
    to { transform: rotate(360deg); }
}

/* ── Inline messages ─────────────────────────────────────────────────────── */
.chargeur-msg {
    font-size: 1rem;
    line-height: 1.7rem;
    margin: 0;
    min-height: 1.25em;
    border-radius: 6px;
}

/* Padding symétrique 12px/16px (grille 8pt). border-left ne consomme pas
   le padding-left (négligeable à 3px, pas compensé - pratique standard des
   callouts). :not(.has-background) neutralise un style global de
   WordPress - `p:not(.has-background):last-of-type { padding-bottom: 0 }`,
   spécificité (0,2,1) - qui l'emporterait sur un simple
   `.chargeur-msg:not(:empty)` (0,2,0) dès que ce message est le dernier
   <p> de son type parmi ses frères (cas de #chargeur-email-msg, seul <p>
   de son <form>) : padding-bottom retomberait silencieusement à 0. Nos
   messages n'ont jamais `.has-background` : l'ajouter ici l'emporte
   légitimement (0,3,0), sélecteur explicite plutôt que !important. */
.chargeur-msg:not(:empty):not(.has-background) {
    padding: 0.75rem 1rem;
    border-left: 3px solid transparent;
}

.chargeur-msg--success {
    color: var(--chargeur-success-color);
    background: var(--chargeur-success-bg);
    border-left-color: var(--chargeur-success-border);
}

.chargeur-msg--error {
    color: var(--chargeur-error-color);
    background: var(--chargeur-error-bg);
    border-left-color: var(--chargeur-error-border);
}

/* ── Nav shortcode ────────────────────────────────────────────────────────── */
/* Conteneur neutre : uniquement les liens, sans décoration propre. */
.chargeur-nav {
    box-sizing: border-box;
    width: 100%;
    margin: 0;
    padding: 0;
}

/* Sélecteur .chargeur-nav .chargeur-nav__list (pas juste .chargeur-nav__list)
   - Divi définit "#left-area ul, .entry-content ul, ..." (une classe + un
   élément, plus spécifique qu'une seule classe) qui impose un padding-bottom
   et padding-left sinon, sans jamais recourir à !important (même discipline
   que .chargeur-msg). */
.chargeur-nav .chargeur-nav__list {
    list-style: none;
    list-style-type: none;
    /* Compense le padding horizontal des liens-pilules (0.9rem) pour que le
       texte du premier item soit aligné au bord gauche du conteneur. */
    margin: 0 0 0 -0.9rem;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 1em;
}

.chargeur-nav__item {
    margin: 0;
}
/* ── Admin / Opérations / Supervision shortcodes ─────────────────────────── */
/* Pleine largeur : la largeur max est gérée par la section/colonne Divi. */
.chargeur-admin,
.chargeur-operations,
.chargeur-supervision {
    box-sizing: border-box;
    width: 100%;
    background: var(--chargeur-card-bg);
    border-radius: var(--chargeur-radius-card);
    box-shadow: var(--chargeur-card-shadow);
    padding: clamp(1rem, 2.5vw, 2rem);
    color: var(--chargeur-text);
}

/* Page sub-nav ([chargeur_operations]/[chargeur_supervision]) - "centre
   actif" widget sits beside the tabs it conditions instead of above them;
   .chargeur-tabs keeps its own border-bottom below, unaffected. */
.chargeur-page-subnav {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 1rem;
    margin-bottom: 0.5rem;
}

.chargeur-page-subnav .chargeur-tabs {
    flex: 1 1 auto;
}

/* Tabs */
.chargeur-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
    border-bottom: 1.5px solid var(--chargeur-border);
    margin-bottom: var(--chargeur-gap);
}

.chargeur-tabs__tab {
    background: none;
    border: none;
    border-bottom: 2.5px solid transparent;
    padding: 0.6rem 1rem;
    font-size: 1rem;
    font-weight: 600;
    color: var(--chargeur-label);
    cursor: pointer;
    transition: color var(--chargeur-transition), border-color var(--chargeur-transition);
}

.chargeur-tabs__tab:hover {
    color: var(--chargeur-accent);
}

.chargeur-tabs__tab.is-active {
    color: var(--chargeur-accent);
    border-bottom-color: var(--chargeur-accent);
}

.chargeur-tab-panel {
    padding-top: 0.5rem;
}

/* Même piège que .chargeur-login__form[hidden] plus haut (règle auteur
   `display` vs UA stylesheet [hidden]) - without this, every panel's
   `hidden` attribute is toggled correctly by JS but has no visual effect,
   and all tab panels render stacked at once. */
.chargeur-tab-panel[hidden] {
    display: none;
}

/* ── Vue d'ensemble ────────────────────────────────────────────────────────
   Grille de cartes - 2 cartes "stat" (une seule valeur : Date de
   verrouillage, Collaborateurs actifs) et 2 cartes "répartition" (Lignes,
   Types d'horaire - un sous-total par catégorie). Même langage visuel que
   .chargeur-login/.chargeur-card ailleurs (fond blanc, ombre légère, coins
   arrondis), jamais un nouveau style de carte inventé pour cet écran. */
.chargeur-overview {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: var(--chargeur-gap);
}

.chargeur-overview__card {
    background: var(--chargeur-card-bg);
    border: 1px solid var(--chargeur-border);
    border-radius: var(--chargeur-radius-card);
    padding: 1.25rem;
}

.chargeur-overview__card-title {
    margin: 0 0 0.75rem;
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--chargeur-label);
}

/* Répartition en sous-catégories (Lignes, Types d'horaire) - un chiffre par
   catégorie, jamais un simple total qui masquerait le détail. */
.chargeur-overview__breakdown {
    display: flex;
    gap: var(--chargeur-gap);
}

.chargeur-overview__breakdown-item {
    flex: 1 1 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 0.2rem;
    padding: 0.5rem 0.25rem;
    border-radius: var(--chargeur-radius);
}

.chargeur-overview__breakdown-value {
    font-size: 1.5rem;
    font-weight: 700;
    line-height: 1;
    color: var(--chargeur-text);
    font-variant-numeric: tabular-nums;
}

.chargeur-overview__breakdown-label {
    font-size: 0.75rem;
    color: var(--chargeur-label);
}

/* Catégories "problème" (Rejetées / Sans code / Mal configurés) - même
   teinte d'erreur que .chargeur-table__row--probleme ailleurs dans l'app,
   jamais une nouvelle couleur inventée pour ce même signal. */
.chargeur-overview__breakdown-item--warn {
    background: var(--chargeur-error-bg);
}

.chargeur-overview__breakdown-item--warn .chargeur-overview__breakdown-value,
.chargeur-overview__breakdown-item--warn .chargeur-overview__breakdown-label {
    color: var(--chargeur-error-color);
}

/* Toolbar & inline forms */
.chargeur-toolbar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.75rem;
    justify-content: flex-end;
    margin-bottom: var(--chargeur-gap);
}

/* Toolbars that also carry a search field (Centres/Utilisateurs/Référentiel/
   Dates de verrouillage): search pinned left, everything else grouped right -
   .chargeur-toolbar--split has exactly 2 children (search + actions group). */
.chargeur-toolbar--split {
    justify-content: space-between;
}

/* Toolbars dont le seul contenu est un champ de saisie/filtre (ex. le
   sélecteur de centre de l'onglet Types d'horaires) - alignées à gauche
   comme n'importe quel champ de formulaire, plutôt que poussées à droite
   par .chargeur-toolbar (pensé pour un groupe d'actions). */
.chargeur-toolbar--start {
    justify-content: flex-start;
}

.chargeur-toolbar__search {
    flex: 1 1 200px;
    max-width: 280px;
}

/* Inline "clear" (x) button inside a search field - replaces the native
   type="search" cancel button, which is inconsistent across browsers
   (styleable/visible in Chrome/Edge, absent by default in Firefox) and
   can't be restyled. .chargeur-search-field wraps the input + our own
   button; the native one is hidden below so there's never a double x. */
.chargeur-search-field {
    position: relative;
    display: flex;
}

.chargeur-search-field .chargeur-input {
    width: 100%;
    padding-right: 2rem;
}

.chargeur-search-field input[type="search"]::-webkit-search-cancel-button {
    -webkit-appearance: none;
    appearance: none;
}

.chargeur-search-field__clear {
    position: absolute;
    top: 50%;
    right: 0.5rem;
    transform: translateY(-50%);
    width: 1.25rem;
    height: 1.25rem;
    padding: 0;
    border: none;
    background: transparent;
    color: var(--chargeur-label);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* Même piège que .chargeur-login__form[hidden] plus haut (règle auteur
   `display` vs UA stylesheet [hidden]) - ce bouton porte `hidden` par
   défaut dans chaque template (masqué tant que le champ de recherche est
   vide, voir bindSearch() dans chargeur-app-admin.js et son équivalent
   dans chargeur-app-operations.js) mais restait visible en permanence sans
   cet override. */
.chargeur-search-field__clear[hidden] {
    display: none;
}

.chargeur-search-field__clear svg {
    width: 0.65rem;
    height: 0.65rem;
    fill: currentColor;
}

.chargeur-search-field__clear:hover {
    color: var(--chargeur-text);
}

.chargeur-toolbar__actions {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.75rem;
}

.chargeur-form--inline {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--chargeur-gap);
    margin-bottom: var(--chargeur-gap);
}

.chargeur-form--inline .chargeur-field {
    flex: 1 1 160px;
    min-width: 140px;
}

/* Generic "keep these two controls glued together on one line" wrapper -
   a control (button or input) plus its adjacent spinner. Used (a) inside
   .chargeur-form--inline so flex-wrap can never split a submit button from
   its spinner onto separate lines (chargeur-centre-form / chargeur-lockdate-
   form), and (b) inside a plain <td> (Référentiel Tipee prefixe/code cells)
   where the input's own width:100% would otherwise fill the whole cell and
   push the spinner onto its own line. Deliberately NOT applied to the <td>
   itself (display:flex on a table-cell changes its box type and can break
   the table's column sizing) - always an inner wrapper instead. */
.chargeur-inline-group {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    max-width: 100%;
    flex-shrink: 0;
}

.chargeur-inline-group > .chargeur-input {
    width: auto;
    flex: 1 1 auto;
    min-width: 0;
}

.chargeur-inline-group > .chargeur-spinner {
    flex-shrink: 0;
}

/* Tables */
.chargeur-table-wrap {
    overflow-x: auto;
}

.chargeur-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.9rem;
}

.chargeur-table th,
.chargeur-table td {
    text-align: left;
    padding: 0.55rem 0.75rem;
    border-bottom: 1px solid var(--chargeur-border);
    vertical-align: middle;
}

.chargeur-table th {
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--chargeur-label);
    background: var(--chargeur-bg);
}

.chargeur-table tbody tr:hover {
    background: var(--chargeur-muted);
}

/* En-tête de colonne triable (Journal) - le <th> entier reste le
   conteneur, le <button> hérite de son style de texte (majuscules, taille,
   couleur) et n'ajoute qu'un pointeur + un espacement pour le chevron. */
.chargeur-sort-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.3em;
    border: none;
    background: none;
    padding: 0;
    margin: 0;
    font: inherit;
    text-transform: inherit;
    letter-spacing: inherit;
    color: inherit;
    cursor: pointer;
}

.chargeur-sort-btn:hover,
.chargeur-sort-btn:focus-visible {
    color: var(--chargeur-text);
}

.chargeur-sort-btn svg {
    width: 0.7em;
    height: 0.7em;
    fill: currentColor;
    transition: transform 0.15s ease;
}

/* Ascendant = chevron vers le haut (inverse du "vers le bas" par défaut de
   l'icône) - jamais de 3e état neutre, cette colonne est toujours triée. */
.chargeur-sort-btn[aria-sort="ascending"] svg {
    transform: rotate(180deg);
}

@media (prefers-reduced-motion: reduce) {
    .chargeur-sort-btn svg {
        transition: none;
    }
}

.chargeur-table__empty {
    text-align: center;
    color: var(--chargeur-label);
    padding: 1.25rem;
}

.chargeur-table__actions {
    display: flex;
    /* Never wrap: with icon-only buttons the column's natural width shrinks
       enough that wrap could stack them vertically one per line instead of
       staying in a row - the table already scrolls horizontally
       (.chargeur-table-wrap) if a row genuinely needs more space. */
    flex-wrap: nowrap;
    align-items: center;
    gap: 0.4rem;
}

.chargeur-table__actions .chargeur-btn {
    flex-shrink: 0;
}

/* Small buttons (table actions, pagination) */
.chargeur-btn--small {
    padding: 0.3rem 0.7rem;
    min-height: 30px;
    font-size: 0.8rem;
    background: transparent;
    color: var(--chargeur-text);
    border: 1.5px solid var(--chargeur-border);
}

.chargeur-btn--small:hover:not(:disabled) {
    border-color: var(--chargeur-accent);
}

.chargeur-btn--small.chargeur-btn--primary {
    background: var(--chargeur-accent);
    color: #fff;
    border-color: var(--chargeur-accent);
}

.chargeur-btn--small.chargeur-btn--danger {
    color: var(--chargeur-error-border);
}

/* Sans cette règle, .chargeur-btn--small:hover ci-dessus (bordure bleue
   accent) s'appliquerait aussi à un bouton danger au survol - même
   logique que .chargeur-btn--small.chargeur-btn--icon:hover ci-dessous
   (currentColor, déjà rouge ici via la règle juste au-dessus). */
.chargeur-btn--small.chargeur-btn--danger:hover:not(:disabled) {
    border-color: currentColor;
}

/* Icon-only action buttons - SVG in place of text, accessible name carried
   by title (hover tooltip) + aria-label (screen readers), never by text
   content. See iconBtn() in chargeur-app-admin.js and the "Synchroniser"
   buttons in shortcode-admin.php.

   Uniform across every action (Modifier/Supprimer/Activer/…): light gray
   border at rest regardless of the icon's own color (blue normally, red
   for Supprimer via .chargeur-btn--danger above); on hover the border
   simply becomes the icon's own color (currentColor) - the icon color
   itself never changes, on any action, only the border does. */
.chargeur-btn--icon {
    padding: 0.5rem;
    gap: 0;
}

.chargeur-btn--small.chargeur-btn--icon:hover:not(:disabled) {
    border-color: currentColor;
}

.chargeur-btn--icon svg {
    width: 1rem;
    height: 1rem;
    fill: currentColor;
    flex-shrink: 0;
    display: block;
}

.chargeur-btn--small.chargeur-btn--icon {
    padding: 0.4rem;
    min-width: 30px;
    border-color: var(--chargeur-icon-border);
}

/* Status & role badges */
.chargeur-badge {
    display: inline-block;
    padding: 0.15rem 0.6rem;
    border-radius: 999px;
    font-size: 0.75rem;
    font-weight: 600;
    white-space: nowrap;
}

.chargeur-badge--active {
    background: var(--chargeur-success-bg);
    color: var(--chargeur-success-color);
}

.chargeur-badge--inactive {
    background: var(--chargeur-error-bg);
    color: var(--chargeur-error-color);
}

/* "En attente de transmission" (3e état du badge de
   Gestion) - ligne importée mais pas encore transmise, ni Accepté ni
   Rejeté. Le libellé du badge porte toujours l'information en toutes
   lettres (voir Chargeur_Public::centre_scoped_page_i18n()) - la couleur
   seule ne la distingue jamais des deux autres états. */
.chargeur-badge--pending {
    background: var(--chargeur-warning-bg);
    color: var(--chargeur-warning-color);
}

/* "Verrouillée" (tableau détail de la modale d'import)
   - ligne exclue de toute persistance, ni une erreur de saisie (rouge) ni
   un simple avertissement (ambre) : neutre à dessein, le libellé porte
   l'information. */
.chargeur-badge--neutral {
    background: var(--chargeur-muted);
    color: var(--chargeur-label);
}

/* Badge "Rejeté" cliquable (Gestion) - déplie la ligne pour lister les
   causes de rejet. Le chevron signale que c'est actionnable : un badge
   coloré seul n'invite pas spontanément au clic. */
.chargeur-badge--clickable {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    border: none;
    font-family: inherit;
    cursor: pointer;
}

.chargeur-badge--clickable::after {
    content: '';
    width: 0.4em;
    height: 0.4em;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(45deg);
    margin-top: -0.15em;
    transition: transform var(--chargeur-transition);
}

.chargeur-badge--clickable[aria-expanded="true"]::after {
    transform: rotate(-135deg);
    margin-top: 0.15em;
}

/* Ligne dépliée listant les causes de rejet (Gestion) - une par <li>. */
.chargeur-lignes-detail {
    background: var(--chargeur-muted);
}

.chargeur-lignes-detail ul {
    margin: 0;
    padding: 0.6rem 1rem;
    list-style: disc;
    list-style-position: inside;
    color: var(--chargeur-error-color);
    font-size: 0.9rem;
}

/* Liste des causes de rejet dans la modale Ajouter/Modifier de Gestion -
   même traitement visuel que .chargeur-lignes-detail
   ul ci-dessus (Gestion), affichée seulement quand il y a plus d'une cause
   à énumérer (une seule reste un simple texte dans .chargeur-msg). */
.chargeur-modal__causes {
    margin: -0.5rem 0 0;
    padding: 0.6rem 1rem;
    list-style: disc;
    list-style-position: inside;
    background: var(--chargeur-error-bg);
    border-left: 3px solid var(--chargeur-error-border);
    border-radius: var(--chargeur-radius);
    color: var(--chargeur-error-color);
    font-size: 0.9rem;
}

.chargeur-modal__causes[hidden] {
    display: none;
}

/* Colonne étroite portant uniquement l'icône tooltip "dernière modification". */
.chargeur-table__icon-col {
    width: 2rem;
}

/* Colonne de sélection multiple (Gestion) -
   même largeur étroite que .chargeur-table__icon-col ci-dessus, une seule
   case à cocher par cellule. */
.chargeur-table__checkbox-col {
    width: 2rem;
}

/* Barre d'actions groupées contextuelle (Gestion) - n'apparaît que
   lorsqu'au moins une ligne est cochée. Même piège que
   .chargeur-login__form[hidden] plus haut (règle auteur `display` vs UA
   stylesheet [hidden]) - ce conteneur porte lui aussi son propre
   `display: flex`. */
.chargeur-bulk-actions {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.6rem 0.9rem;
    margin-bottom: var(--chargeur-gap);
    background: var(--chargeur-muted);
    border-radius: var(--chargeur-radius);
    font-size: 0.9rem;
    color: var(--chargeur-text);
}

.chargeur-bulk-actions[hidden] {
    display: none;
}

/* Deuxième ligne secondaire dans une cellule (ex. matricule sous le nom,
   "Timbrage"/"Absence" sous le code) - Gestion. */
.chargeur-table__muted {
    font-size: 0.8rem;
    color: var(--chargeur-label);
}

/* Inline edit input + inline confirm */
.chargeur-input--inline {
    padding: 0.35rem 0.5rem;
    font-size: 0.9rem;
}

/* Référentiel Tipee tab */
.chargeur-field--inline {
    flex-direction: row;
    align-items: center;
    gap: 0.5rem;
}

.chargeur-field--inline .chargeur-label {
    margin: 0;
    white-space: nowrap;
}

.chargeur-field--inline .chargeur-input {
    width: auto;
    min-width: 220px;
}

/* "Synchro : <horodatage> <icône>" - partagé par les 5
   actions "Synchroniser" du plugin (Gestion Opérateur/Superviseur, Centres,
   Référentiel Enveloppes, Codes absence). Le texte n'est jamais cliquable -
   seule l'icône l'est ("c'est l'icône seul qui fonctionne comme un bouton") -
   dimensionnée en unités em pour rester à la taille du texte plutôt qu'au
   gabarit standard de .chargeur-btn--icon (16px fixes, avec bordure/fond).
   Toujours le dernier élément de sa toolbar (margin-left: auto le pousse à
   droite, quoi que contienne le reste de la toolbar). */
.chargeur-sync-status {
    display: inline-flex;
    align-items: center;
    gap: 0.4em;
    font-size: 0.85rem;
    color: var(--chargeur-label);
    margin-left: auto;
}

.chargeur-sync-status__btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1em;
    height: 1em;
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    cursor: pointer;
    border-radius: 3px;
    flex-shrink: 0;
}

.chargeur-sync-status__btn:hover:not(:disabled) {
    color: var(--chargeur-accent);
}

.chargeur-sync-status__btn:focus-visible {
    outline: 2px solid var(--chargeur-accent);
    outline-offset: 2px;
}

.chargeur-sync-status__btn svg {
    width: 1em;
    height: 1em;
    fill: currentColor;
    display: block;
}

/* L'icône elle-même tourne pendant la synchro (classe posée par
   setSyncLoading()). Réutilise directement @keyframes chargeur-spin (déjà
   utilisée par .chargeur-spinner plus haut).
   Le <svg> brut de Chargeur_Icons::get('sync') ne porte aucune classe
   `chargeur-*` - la règle prefers-reduced-motion générique plus bas dans ce
   fichier (sélecteur `[class^="chargeur-"]`) ne l'attraperait donc PAS ;
   ce sélecteur est ajouté explicitement à cette règle-là, voir plus bas. */
.chargeur-sync-status__btn.is-syncing svg {
    animation: chargeur-spin 0.65s linear infinite;
}

.chargeur-referentiel-warnings {
    list-style: none;
    margin: 0 0 var(--chargeur-gap);
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.chargeur-referentiel-warnings li {
    background: var(--chargeur-error-bg);
    color: var(--chargeur-error-color);
    border-radius: var(--chargeur-radius);
    padding: 0.6rem 0.9rem;
    font-size: 0.85rem;
}

/* Onglet Référentiel, table Types d'horaires :
   ligne entière signalée quand 0 ou plusieurs enveloppes sont actives
   simultanément (même condition que refuse déjà
   Chargeur_Referentiel_Repository::find_schedule_template_for_machine_name()
   au moment de transmettre) - mêmes jetons --chargeur-error-* que
   .chargeur-referentiel-warnings ci-dessus, jamais une nouvelle couleur. */
.chargeur-table tr.chargeur-table__row--probleme td {
    background: var(--chargeur-error-bg);
    color: var(--chargeur-error-color);
}

/* Onglet Centres, colonne "Nombre d'opérateurs" :
   signal sur le seul champ concerné (pas la ligne entière, contrairement à
   .chargeur-table__row--probleme ci-dessus - un centre à 0 opérateur reste
   par ailleurs valide) quand ce nombre est 0. Mêmes jetons --chargeur-error-*
   que les autres signaux d'alerte de l'app, jamais une nouvelle couleur. */
.chargeur-table__value--warn {
    color: var(--chargeur-error-color);
    font-weight: 600;
}

/* Checkbox list (centre attachments) */
.chargeur-checkbox-list {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    max-height: 180px;
    overflow-y: auto;
    border: 1.5px solid var(--chargeur-border);
    border-radius: var(--chargeur-radius);
    padding: 0.6rem 0.75rem;
}

.chargeur-checkbox {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.9rem;
    color: var(--chargeur-text);
    cursor: pointer;
}

/* Modal - rattachée à <body> au chargement du script (voir
   portalModalToBody() dans chargeur-app-*.js), jamais laissée dans la
   hiérarchie générée par Divi : un ancêtre du builder Divi (transform/
   filter/will-change, fréquent pour ses animations) en ferait sinon le
   bloc conteneur de ce `position: fixed`, invalidant à la fois son cadrage
   (mauvais calage au zoom navigateur) et son empilement (z-index comparé
   seulement dans le contexte de cet ancêtre, jamais contre le header Divi,
   même si celui-ci est en z-index: auto). z-index très
   élevé volontairement : une fois au niveau racine, doit encore battre
   d'éventuels overlays tiers (bannière cookies, lightbox Divi) qui peuvent
   eux-mêmes utiliser des valeurs à 6-10 chiffres. */
.chargeur-modal {
    /* Explicite ici, pas seulement hérité de .chargeur-operations/
       .chargeur-admin/etc. - portalModalToBody() (voir chargeur-app-
       operations.js/chargeur-app-admin.js) détache la modale de cette
       hiérarchie pour la rattacher directement à <body>, hors de la portée
       de leur `font-family: var(--chargeur-font)` : sans cette règle, une
       modale retombe sur la police du thème (Divi) au lieu d'Inter. */
    font-family: var(--chargeur-font);
    position: fixed;
    inset: 0;
    z-index: 2147483647;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
}

.chargeur-modal[hidden] {
    display: none;
}

.chargeur-modal__backdrop {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
}

.chargeur-modal__dialog {
    position: relative;
    background: var(--chargeur-card-bg);
    border-radius: var(--chargeur-radius-card);
    box-shadow: var(--chargeur-card-shadow);
    padding: 1.5rem 1.75rem;
    width: 100%;
    max-width: 480px;
    max-height: 90vh;
    overflow-y: auto;
    /* `overflow-x` explicite - laisser `overflow-x` à sa valeur initiale
       (`visible`) alors que `overflow-y` vaut autre chose que `visible`
       force le navigateur, par spec, à promouvoir aussi `overflow-x` à
       `auto` (même piège que .chargeur-select__listbox plus haut). Jamais
       de défilement horizontal ici - la modale d'import Excel (la seule
       à contenir un tableau large) gère déjà son propre défilement
       horizontal via .chargeur-table-wrap. */
    overflow-x: hidden;
    display: flex;
    flex-direction: column;
    gap: var(--chargeur-gap);
}

.chargeur-modal__dialog form {
    display: flex;
    flex-direction: column;
    gap: var(--chargeur-gap);
}

.chargeur-modal__title {
    margin: 0;
    font-size: 1.1rem;
    font-weight: 700;
    color: var(--chargeur-text);
}

.chargeur-modal__actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.75rem;
}

/* Modale d'import Excel plus large que le gabarit standard - le
   tableau détail par ligne (Ligne/Matricule/Statut/Motif)
   a besoin de plus d'espace que le formulaire minimal des autres modales
   (créer un utilisateur, éditer une date de verrouillage...), qui restent
   au gabarit standard. */
.chargeur-modal__dialog--wide {
    max-width: 720px;
}

/* Plusieurs motifs pour une même ligne (cellule Motif du tableau détail
   d'import) - une seule ligne de texte suffit dans le cas courant (un seul
   motif), voir renderImportDetail(). */
.chargeur-import-detail__messages {
    margin: 0;
    padding: 0 0 0 1.1rem;
    list-style: disc;
}

/* Pagination */
.chargeur-pagination {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-top: var(--chargeur-gap);
}

.chargeur-pagination__label {
    font-size: 0.85rem;
    color: var(--chargeur-label);
}

/* ── Bonnes pratiques UI/UX (uniformité plateforme) ───────────────────────── */

/* Focus clavier visible sur tous les éléments interactifs - indépendant du thème */
.chargeur-btn:focus-visible,
.chargeur-link:focus-visible,
.chargeur-tabs__tab:focus-visible,
.chargeur-nav__link:focus-visible,
.chargeur-nav-login:focus-visible,
.chargeur-nav-logout:focus-visible,
.chargeur-nav__centre-toggle:focus-visible,
.chargeur-nav__centre-option:focus-visible,
.chargeur-checkbox input:focus-visible,
.chargeur-select__trigger:focus-visible {
    outline: 2px solid var(--chargeur-accent);
    outline-offset: 2px;
    border-radius: var(--chargeur-radius);
}

/* Navigation : liens uniformes avec état actif (aria-current) */
.chargeur-nav__link {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: 0.4rem 0.9rem;
    border-radius: var(--chargeur-radius);
    font-family: var(--chargeur-font);
    font-weight: 500;
    color: var(--chargeur-text);
    text-decoration: none;
    transition: background var(--chargeur-transition), color var(--chargeur-transition);
}

.chargeur-nav__link:hover {
    background: var(--chargeur-muted);
    color: var(--chargeur-secondary);
}

.chargeur-nav__link[aria-current="page"] {
    background: var(--chargeur-accent-light);
    color: var(--chargeur-accent);
    font-weight: 600;
}

/* "Centre actif" - toujours le premier item de la nav.
   Le cas à un seul centre est du texte statique, sans JS ; le sélecteur
   multi-centres (chargeur-app-nav.js) est un bouton+listbox personnalisé -
   un <select> natif ne peut pas afficher un texte différent replié/déplié. */
.chargeur-nav__centre-static {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: 0.4rem 0.9rem;
    font-weight: 600;
    color: var(--chargeur-accent);
}

.chargeur-nav__centre {
    position: relative;
}

.chargeur-nav__centre-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    min-height: 44px;
    padding: 0.4rem 0.9rem;
    border: 1.5px solid var(--chargeur-border);
    border-radius: var(--chargeur-radius);
    background: var(--chargeur-input-bg);
    font-family: var(--chargeur-font);
    font-weight: 600;
    color: var(--chargeur-accent);
    cursor: pointer;
    transition:
        border-color var(--chargeur-transition),
        background   var(--chargeur-transition);
}

.chargeur-nav__centre-toggle:hover {
    background: var(--chargeur-muted);
    border-color: var(--chargeur-accent);
}

.chargeur-nav__centre-toggle:active {
    background: var(--chargeur-accent-light);
}

/* Ouvert : même traitement que le survol pour que le bouton reste "actif" à
   l'oeil tant que la liste est dépliée, indépendamment de la position de la
   souris. */
.chargeur-nav__centre-toggle[aria-expanded="true"] {
    background: var(--chargeur-muted);
    border-color: var(--chargeur-accent);
}

.chargeur-nav__centre-toggle::after {
    content: '';
    flex: none;
    width: 0.5em;
    height: 0.5em;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(45deg);
    margin-top: -0.25em;
    transition: transform 0.15s ease;
}

.chargeur-nav__centre-toggle[aria-expanded="true"]::after {
    transform: rotate(-135deg);
    margin-top: 0.25em;
}

.chargeur-nav__centre-listbox {
    position: absolute;
    top: calc(100% + 0.35rem);
    left: 0;
    z-index: 100;
    /* max-content : la liste s'élargit pour tenir le plus long "code –
       libellé" sur une seule ligne (ex. "CRA – CRA Centre de rencontres La
       Source") plutôt que de forcer un retour à la ligne à une largeur
       arbitraire - le retour à la ligne naturel du texte reste le filet de
       sécurité sur un petit écran (max-width alors dicté par le viewport). */
    width: max-content;
    min-width: 220px;
    max-width: min(440px, calc(100vw - 2rem));
    max-height: 320px;
    overflow-y: auto;
    margin: 0;
    padding: 0.35rem;
    list-style: none;
    background: var(--chargeur-card-bg);
    border: 1.5px solid var(--chargeur-border);
    border-radius: var(--chargeur-radius-card);
    box-shadow: var(--chargeur-card-shadow);
    /* Entrée douce (150ms, sous le seuil de 300ms recommandé pour un menu) -
       neutralisée par la règle prefers-reduced-motion globale plus bas.
       Départ/arrivée pilotés par JS via .is-open (ajoutée une frame après le
       retrait de [hidden], pour que la transition parte bien de l'état
       replié plutôt que de sauter directement à l'état ouvert). */
    opacity: 0;
    transform: translateY(-4px) scale(0.98);
    transform-origin: top left;
    transition: opacity 0.15s ease, transform 0.15s ease;
}

/* Un style du thème (même mécanisme que le list-style plus haut : une règle
   ul/li du thème avec une spécificité équivalente, chargée après la nôtre)
   réaffirmait padding-left/padding-bottom en longhand (14px/23px, valeurs du
   thème) malgré le padding uniforme ci-dessus. Reset ciblé avec un
   sélecteur plus spécifique (ancré sur le conteneur parent) pour trancher
   sans dépendre de l'ordre de chargement des feuilles de style. */
.chargeur-nav__centre .chargeur-nav__centre-listbox {
    padding: 0.35rem;
}

.chargeur-nav__centre-listbox.is-open {
    opacity: 1;
    transform: translateY(0) scale(1);
}

.chargeur-nav__centre-listbox[hidden] {
    display: none;
}

/* list-style sur le <ul> ne suffit pas toujours à supprimer la puce - une
   règle du thème ciblant `li` directement peut l'emporter par spécificité.
   Reset explicite sur l'élément li lui-même (pas de classe dédiée côté JS,
   ciblé via le parent). */
.chargeur-nav__centre-listbox li {
    margin: 0;
    padding: 0;
    list-style: none;
}

.chargeur-nav__centre-option {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    width: 100%;
    min-height: 44px;
    padding: 0.5rem 0.65rem;
    border: none;
    border-radius: 0;
    background: transparent;
    font-family: var(--chargeur-font);
    font-size: 0.9rem;
    text-align: left;
    color: var(--chargeur-text);
    cursor: pointer;
    transition: background var(--chargeur-transition);
}

.chargeur-nav__centre-option:hover {
    background: var(--chargeur-muted);
}

.chargeur-nav__centre-option:active {
    background: var(--chargeur-accent-light);
}

.chargeur-nav__centre-option.is-active {
    background: var(--chargeur-accent-light);
    color: var(--chargeur-accent);
    font-weight: 600;
}

/* Coche devant le centre actif - jamais la couleur seule pour porter le sens
   (déjà doublée par le fond/le gras ci-dessus), utile aussi en cas de
   daltonisme. Pas d'espace réservé équivalent sur les lignes inactives
   (retour visuel : un espace vide à gauche de chaque ligne
   était perçu comme un défaut) - le texte des lignes inactives démarre au
   padding du bouton, seule la ligne active décale légèrement le sien pour
   la coche, comme la plupart des menus natifs (macOS, Windows). */
.chargeur-nav__centre-option.is-active::before {
    content: '';
    flex: none;
    width: 0.55em;
    height: 0.3em;
    border-left: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(-45deg);
    margin-top: -0.15em;
}

.chargeur-nav__centre-error {
    position: absolute;
    top: calc(100% + 0.35rem);
    left: 0;
    z-index: 100;
    margin: 0;
    padding: 0.5rem 0.75rem;
    max-width: 260px;
    font-size: 0.85rem;
    color: var(--chargeur-error-color);
    background: var(--chargeur-error-bg);
    border: 1.5px solid var(--chargeur-error-border);
    border-radius: var(--chargeur-radius);
}

.chargeur-nav__centre-error[hidden] {
    display: none;
}

/* Données chiffrées : chiffres tabulaires (pas de décalage entre lignes),
   et césure des valeurs longues (e-mails, messages) plutôt que débordement */
.chargeur-table td {
    font-variant-numeric: tabular-nums;
    overflow-wrap: break-word;
}

/* Réserver l'espace des tables chargées en JS (limite le saut de layout) */
.chargeur-table-wrap {
    min-height: 140px;
}

/* Squelette de chargement (remplacé par les données à l'arrivée du fetch) */
.chargeur-skeleton {
    display: block;
    height: 0.9rem;
    border-radius: 4px;
    background: linear-gradient(
        90deg,
        var(--chargeur-muted) 25%,
        var(--chargeur-bg) 50%,
        var(--chargeur-muted) 75%
    );
    background-size: 200% 100%;
    animation: chargeur-shimmer 1.4s ease infinite;
}

@keyframes chargeur-shimmer {
    to { background-position: -200% 0; }
}

/* Refetch of an already-populated table (filter/reset/pagination) - the
   skeleton above is for the true first load only (empty tbody). Here the
   existing rows stay on screen, just dimmed and inert, instead of being
   wiped and replaced by a skeleton flash - avoids the layout jump and
   context loss a full skeleton causes on a refinement of visible data. */
.chargeur-table--loading tbody {
    opacity: 0.5;
    pointer-events: none;
    transition: opacity 0.15s ease;
}

/* Cibles tactiles ≥ 44px sur écrans tactiles (Apple HIG / Material) */
@media (pointer: coarse) {
    /* .chargeur-btn de base est à 42px (voir plus haut) - suffisant à la
       souris, 2px sous le minimum tactile. Bouton principal du formulaire de
       connexion notamment (chargeur-email-submit/otp-submit/forgot-submit). */
    .chargeur-btn {
        min-height: 44px;
    }

    .chargeur-btn--small {
        min-height: 44px;
        padding: 0.5rem 0.9rem;
    }

    .chargeur-btn--small.chargeur-btn--icon {
        min-width: 44px;
        padding: 0.5rem;
    }

    .chargeur-tabs__tab {
        min-height: 44px;
    }

    .chargeur-checkbox {
        min-height: 44px;
    }
}

/* Respect de prefers-reduced-motion : neutralise animations et transitions */
@media (prefers-reduced-motion: reduce) {
    [class^="chargeur-"],
    [class*=" chargeur-"],
    [class^="chargeur-"]::before,
    [class*=" chargeur-"]::after,
    /* Le <svg> brut du bouton "Synchroniser" ne
       porte aucune classe chargeur-* (voir .chargeur-sync-status__btn.
       is-syncing svg plus haut) - ajouté explicitement, sinon jamais
       neutralisé par les sélecteurs génériques ci-dessus. */
    .chargeur-sync-status__btn.is-syncing svg {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }

    /* Le spinner reste perceptible sans rotation */
    .chargeur-spinner {
        border-color: var(--chargeur-accent);
        opacity: 0.5;
    }

    .chargeur-skeleton {
        background: var(--chargeur-muted);
    }
}

/* Badge de type d'événement (journaux) */
.chargeur-badge--type {
    background: var(--chargeur-muted);
    color: var(--chargeur-text);
    cursor: help; /* la clé technique est dans le title */
}
