/*
 * Socle front Projecture.
 *
 * ⚠️ FEUILLE STATIQUE, SANS ÉTAPE DE BUILD. Elle se sert telle quelle depuis
 * public/. Pas de Vite, pas de Tailwind à compiler : le déploiement est un
 * copier-coller de fichiers sur OVH, et une chaîne de build y serait une
 * dépendance de plus à faire tourner sur un serveur qui n'en a pas besoin.
 *
 * ⚠️ TOUT CE QUI EST ICI NE DOIT PLUS ÊTRE RÉÉCRIT DANS UNE VUE. C'était la
 * principale dette front : chaque module reposait ses variables de couleur,
 * ses .card, ses .field, ses .button, dans un <style> inline. Quatre copies
 * d'une même charte divergent, c'est mécanique.
 *
 * Les modules existants — wiki, projets, agenda, matériel — ne sont PAS migrés
 * ici : leur reprise est un chantier à part. Ce socle sert le RH, et servira
 * aux autres le jour où on les reprendra.
 */

:root {
    color-scheme: light;

    /* Identité Projecture et couleurs sémantiques. */
    --teal: #0f6f6c;
    --magenta: #be2d62;
    --bg: #f4f6f8;
    --card: #fff;
    --surface-muted: #f8fafb;
    --surface-strong: #eef2f4;
    --field-bg: #fff;
    --field-disabled: #f2f5f7;
    --ink: #17212b;
    --ink-soft: #52616c;
    --muted: #697680;
    --line: #dfe5e9;
    --line-strong: #c9d2d9;
    --brand: var(--teal);
    --brand-ink: #fff;
    --on-color: #fff;
    --brand-bg: #e7f4f3;
    --danger: var(--magenta);
    --danger-bg: #fceef3;
    --danger-line: #e8a9c0;
    --ok: #067647;
    --ok-bg: #ecfdf3;
    --ok-line: #9ed8bd;
    --warn: #b54708;
    --warn-bg: #fffaeb;
    --warn-line: #f2c38b;
    --focus-ring: color-mix(in srgb, var(--brand) 24%, transparent);
    --shadow-sm: 0 1px 4px rgb(15 33 43 / 8%);
    --shadow-lg: 0 24px 70px rgb(15 23 32 / 28%);
    --overlay: rgb(15 23 32 / 55%);

    /*
     * ⚠️ **Le menu latéral a SES jetons.** Ce sont les seules couleurs de la
     * palette qui vivent sur fond sombre : les réutiliser ailleurs donnerait du
     * texte gris pâle sur du blanc.
     */
    --lateral-bg: #1e2b38;
    --lateral-ink: #c9d6e0;

    /*
     * ⚠️ **La hauteur de la barre du haut, recopiée.** Elle est écrite en dur
     * dans `partials/app-menu.blade.php`, qui est partagé : le jeton évite au
     * moins qu'elle soit répétée trois fois de ce côté-ci. Si elle change
     * là-bas, c'est ici qu'il faut la suivre — 58 px, 52 sous 760.
     */
    --barre-haut: 58px;

    /* La hauteur du bouton qui replie le menu du module sur téléphone (§ .lateral__bascule). */
    --bascule-haut: 44px;

    --radius-card: 14px;
    --radius-control: 9px;
    --radius-pill: 999px;

    --page: 1400px;
    --gap: 14px;

    --font: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}

@media (prefers-color-scheme: dark) {
    :root {
        color-scheme: dark;
        --bg: #0f1719;
        --card: #172225;
        --surface-muted: #1c292c;
        --surface-strong: #243337;
        --field-bg: #111c1f;
        --field-disabled: #243034;
        --ink: #eef7f6;
        --ink-soft: #c1cfcd;
        --muted: #9eafad;
        --line: #334548;
        --line-strong: #496064;
        --brand: #55b8b3;
        --brand-ink: #081313;
        --brand-bg: #163c3b;
        --danger: #ef77a1;
        --danger-bg: #3d1d2a;
        --danger-line: #74334c;
        --ok: #69d5a2;
        --ok-bg: #143629;
        --ok-line: #2d7658;
        --warn: #f2b66d;
        --warn-bg: #3c2b16;
        --warn-line: #76552a;
        --shadow-sm: 0 1px 4px rgb(0 0 0 / 28%);
        --shadow-lg: 0 24px 70px rgb(0 0 0 / 55%);
        --overlay: rgb(0 0 0 / 68%);
    }
}

* { box-sizing: border-box; }

body {
    margin: 0;
    background: var(--bg);
    color: var(--ink);
    font-family: var(--font);
    font-size: 15px;
    line-height: 1.5;
}

/* ---------------------------------------------------------------- page */

/*
 * ⚠️ **TROIS NIVEAUX QUI SE TOUCHAIENT.** La barre de navigation, le fil
 * d'Ariane et le titre s'enchaînaient sans respiration : le titre semblait
 * collé au menu, sur le wiki comme sur la fiche employé — donc c'était le
 * socle, pas les vues.
 *
 * ⚠️ **L'air se pose ICI, pas dans chaque écran.** Une marge ajoutée vue par
 * vue diverge à la première qu'on oublie, et personne ne sait plus laquelle
 * fait référence. Les deux modules en bénéficient du même coup.
 */
.page {
    width: min(var(--page), calc(100% - 36px));
    margin: auto;
    padding: 36px 0 48px;
}

.page--etroit { --page: 900px; }

/*
 * ⚠️ **TROIS NIVEAUX, TROIS RESPIRATIONS.** Le fil d'Ariane dit où l'on est,
 * le titre de qui il s'agit, le sous-titre ce qu'on regarde de lui. Serrés,
 * ils se lisent comme un seul bloc et on ne distingue plus lequel répond à
 * quelle question.
 */
.page__header {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 20px;
    margin-bottom: 26px;
}

.page__header h1 { margin: 0; font-size: 32px; line-height: 1.2; }
.page__sous-titre { margin: 9px 0 0; color: var(--muted); }

/* Fil d'Ariane — la position dans l'application, pas une décoration. */
.fil {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    /* Entre le fil et le titre : le fil dit OÙ, le titre dit QUOI. */
    margin: 0 0 26px;
    padding: 0;
    list-style: none;
    color: var(--muted);
    font-size: 13px;
}

.fil a { color: var(--muted); text-decoration: none; }

/*
 * ⚠️ **Les liens d'un tableau tombaient sur le DÉFAUT DU NAVIGATEUR** — bleu
 * souligné — parce que rien ne les habillait : la feuille ne portait qu'une
 * règle sur le fil d'Ariane. Ce n'était pas une couleur écrite en dur qu'il
 * fallait chasser, c'était une règle absente.
 *
 * ⚠️ **Le soulignement revient au survol.** L'enlever tout court laisserait un
 * texte coloré que rien ne désigne comme cliquable — et la couleur seule ne
 * suffit pas : elle ne se voit pas de la même façon selon les yeux.
 */
.tableau a {
    color: var(--brand);
    text-decoration: none;
}

.tableau a:hover,
.tableau a:focus-visible { text-decoration: underline; }
.fil a:hover { color: var(--ink); text-decoration: underline; }
.fil li + li::before { content: "›"; margin-right: 8px; color: var(--muted); }
.fil [aria-current="page"] { color: var(--ink); font-weight: 700; }

/* ---------------------------------------------------------------- carte */

/* ------------------------------------------------- menu lateral du module */

/*
 * ⚠️ **`display: contents` par defaut, et c'est ce qui rend l'ajout sur** :
 * l'enveloppe disparait de l'arbre de rendu tant qu'aucun menu lateral n'est
 * posé. Une page sans `$lateral` se dispose exactement comme avant.
 */
.enveloppe { display: contents; }

.enveloppe--lateral {
    display: flex;
    align-items: flex-start;
    gap: 0;
}

/*
 * ⚠️ **UN MENU SOMBRE, PAS UNE COLONNE PALE.** En gris clair sur blanc il se
 * confondait avec la page : rien ne séparait la navigation du contenu, et
 * l'œil devait lire pour savoir où il était. C'est le premier trait de la
 * référence Scriptcase (`Scriptcase/RH/50.png`) — un fond contrasté fait la
 * séparation avant qu'un seul mot soit lu.
 *
 * ⚠️ **Une teinte DISTINCTE de la barre du haut**, pas la même. Deux surfaces
 * sombres identiques donneraient un seul bloc en L dont on ne verrait plus la
 * charnière ; celle-ci est un cran plus claire.
 *
 * ⚠️ **272 px, pas 232.** « Administration du personnel » tenait à peine et
 * « Récapitulatif mensuel » frôlait le bord. Une entrée qui se coupe oblige à
 * deviner, et l'icône consomme sa part de largeur.
 */
/*
 * ⚠️ **`max-height` LAISSAIT LE FOND S'ARRÊTER SOUS LA DERNIÈRE ENTRÉE.** La
 * colonne prenait la hauteur de son contenu — l'enveloppe est en
 * `align-items: flex-start` — et le sombre s'interrompait à mi-page. Une
 * hauteur FIXE la fait couvrir la fenêtre entière, qu'il y ait cinq entrées ou
 * dix-huit.
 *
 * ⚠️ **`sticky`, PAS `fixed`.** Fixed la sortirait du flux : la colonne ne
 * réserverait plus sa largeur et le contenu passerait dessous. Sticky donne le
 * même résultat à l'œil — la bande ne bouge plus quand la page défile — en
 * gardant la place qu'elle occupe.
 *
 * ⚠️ **`top` vaut la hauteur de la barre du haut**, qui est elle-même sticky :
 * à zéro, le menu glisserait dessous. Les deux valeurs sont couplées, et la
 * barre vit dans `partials/app-menu.blade.php` — infrastructure partagée
 * (§0 quater) : d'où le jeton, pour qu'un seul endroit porte le nombre de ce
 * côté-ci.
 *
 * ⚠️ **`overscroll-behavior: contain`** : arrivé en bas du menu, la molette ne
 * doit pas emporter la page avec elle. Sans lui, dérouler la navigation fait
 * défiler l'écran qu'on regardait.
 */
.lateral {
    flex: 0 0 272px;
    position: sticky;
    top: var(--barre-haut, 58px);
    height: calc(100vh - var(--barre-haut, 58px));
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: 16px 12px 28px;
    background: var(--lateral-bg, #1e2b38);
    color: var(--lateral-ink, #c9d6e0);
}

/*
 * ⚠️ **La page reprend ses marges A L'INTERIEUR de l'enveloppe.** Hors flux,
 * elle se centrait avec `margin: auto` et son `width: min(...)` lui donnait
 * des marges laterales ; devenue element flex, elle les perd et le contenu
 * vient coller la colonne de gauche.
 */
/*
 * ⚠️ **`margin: auto` CENTRAIT LA PAGE VERTICALEMENT, et personne ne l'avait
 * demandé.** Sur un élément flex, une marge automatique absorbe l'espace libre
 * dans **les deux axes** — et elle l'emporte sur `align-items: flex-start`. La
 * règle `.page { margin: auto }` sert à centrer horizontalement hors flex ;
 * dans l'enveloppe elle centrait aussi de haut en bas.
 *
 * ⚠️ **Le défaut s'est RÉVÉLÉ en donnant sa hauteur au menu**, il n'est pas né
 * là : tant que la colonne mesurait la hauteur de son contenu, il n'y avait
 * pas d'espace libre à absorber. C'est pourquoi il se voit sur un écran court
 * — une liste vide flottait au milieu de la fenêtre.
 *
 * ⚠️ **C'est le SOCLE, pas un écran** : tous les écrans RH courts étaient
 * concernés. Une correction posée sur la vue des demandes aurait laissé les
 * vingt autres.
 */
.enveloppe--lateral .page {
    flex: 1;
    min-width: 0;
    margin-block: 0;
    align-self: flex-start;
    padding-inline: 28px;
}

@media (max-width: 900px) {
    .enveloppe--lateral .page { padding-inline: 18px; }
}

/* La barre du haut passe à 52 px sous 760 : le menu suit, sinon il glisse. */
@media (max-width: 760px) {
    :root { --barre-haut: 52px; }
}

.lateral__section { margin: 0 0 2px; }

/*
 * ⚠️ **Le titre de section est un `<summary>`, donc un BOUTON.** Il n'a plus
 * rien d'une étiquette : on clique dessus, il se déplie. À 11 px en capitales
 * grises il ressemblait à une légende — quelque chose qu'on ne clique pas.
 */
.lateral__titre {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 0;
    padding: 9px 10px;
    border-radius: 8px;
    color: var(--lateral-ink, #c9d6e0);
    font-size: 14.5px;
    font-weight: 700;
    cursor: pointer;
    list-style: none;
    user-select: none;
}

/* Le triangle natif de `<details>`, remplacé par notre chevron. */
.lateral__titre::-webkit-details-marker { display: none; }
.lateral__titre::marker { content: ''; }

.lateral__titre:hover { background: rgb(255 255 255 / 7%); color: #fff; }

.lateral__chevron {
    margin-left: auto;
    width: 7px;
    height: 7px;
    border-right: 2px solid currentcolor;
    border-bottom: 2px solid currentcolor;
    transform: rotate(-45deg);
    opacity: .6;
    transition: transform .15s ease;
}

/* Déplié : le chevron pointe vers le bas. */
.lateral__section[open] > .lateral__titre { color: #fff; }
.lateral__section[open] > .lateral__titre .lateral__chevron { transform: rotate(45deg); }

.lateral__liste { list-style: none; margin: 2px 0 8px; padding: 0 0 0 12px; }

/*
 * ⚠️ **Les libellés en taille NORMALE.** 13,5 px dans une colonne sombre se
 * lit moins bien que 13,5 px sur blanc — le contraste inversé demande un peu
 * plus de corps. La référence Scriptcase est à 14-15.
 */
.lateral__lien {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 7px 10px;
    border-radius: 8px;
    color: var(--lateral-ink, #c9d6e0);
    text-decoration: none;
    font-size: 14px;
    line-height: 1.3;
}

.lateral__lien--seul { font-weight: 700; margin-bottom: 2px; }

.lateral__lien:hover { background: rgb(255 255 255 / 8%); color: #fff; }

/*
 * ⚠️ **L'entrée active est un BLOC PLEIN**, pas une nuance de gris. Sur un fond
 * sombre, un simple changement de teinte ne se voit pas ; c'est le remplissage
 * qui dit « vous êtes ici », et il se voit avant qu'on lise.
 *
 * ⚠️ **Elle garde sa barre à gauche**, qui survit là où la couleur ne survit
 * pas — écran pâle, impression, daltonisme. La teinte seule ne suffit jamais.
 */
.lateral__lien.is-active {
    background: var(--brand);
    color: #fff;
    font-weight: 700;
    box-shadow: inset 3px 0 0 rgb(255 255 255 / 55%);
}

.lateral__lien.is-active .lateral__icone { opacity: 1; }

/*
 * ⚠️ **Une icône par entrée, et la MÊME pour un même sujet.** « Équipes » et
 * « Gestion équipe » partagent la leur : ce sont deux portes sur la même
 * matière, et deux dessins différents suggéreraient deux choses différentes.
 */
.lateral__icone {
    flex: 0 0 auto;
    width: 17px;
    height: 17px;
    opacity: .78;
}

/*
 * ⚠️ **Sous 900 px la colonne passe AU-DESSUS, elle ne disparait pas.** Un
 * menu qu'on replie derriere un bouton demande de savoir qu'il existe ; ici
 * il devient une bande horizontale defilante, et tout reste atteignable.
 *
 * ⚠️ **`height: auto` EST LE POINT DE CETTE RÈGLE, pas `max-height`.** La
 * colonne porte `height: calc(100vh - --barre-haut)` pour couvrir la fenêtre
 * quand elle est à gauche ; empilée au-dessus du contenu, cette hauteur
 * devient une bande sombre de la hauteur de l'écran — la bande de liens en
 * occupe le premier tiers, et **le contenu de la page part sous la ligne de
 * flottaison**. C'est ce qui rendait le module illisible sur téléphone : rien
 * ne manquait, tout était poussé dehors. La règle annulait `max-height`, qui
 * n'a jamais été posé.
 */
@media (max-width: 900px) {
    .enveloppe--lateral { flex-direction: column; }

    .lateral {
        flex: none;
        position: static;
        height: auto;
        max-height: none;
        width: 100%;
        border-right: 0;
        border-bottom: 1px solid var(--line);
        padding: 12px 14px;
    }

    .lateral__nav { display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: baseline; }
    .lateral__section { margin: 0; }
    .lateral__titre { margin: 0; padding: 4px 8px; }
    .lateral__liste { display: flex; flex-wrap: wrap; gap: 4px; padding-left: 0; }
    .lateral__lien.is-active { box-shadow: inset 0 -3px 0 rgb(255 255 255 / 55%); }
}

/* ------------------------------------------- le menu latéral sur téléphone */

/*
 * ⚠️ **SOUS 700 PX LE MENU SE REPLIE DERRIÈRE UN BOUTON, EN SUPERPOSITION.**
 * Empilée au-dessus du contenu, la bande de liens prenait trois lignes pleine
 * largeur avant qu'un seul mot de la page soit lisible : sur un téléphone, la
 * navigation d'un module coûtait le premier écran. En superposition elle ne
 * coûte plus rien — **elle ne pousse pas le contenu**, elle passe dessus le
 * temps qu'on choisisse.
 *
 * ⚠️ **La bande horizontale reste le bon choix entre 700 et 900 px** : il y a
 * la place d'y lire les libellés, et un menu visible épargne un geste. Le repli
 * ne se justifie que là où la place manque vraiment.
 *
 * ⚠️ **TOUT TIENT DANS LA FEUILLE, SANS SCRIPT.** Un menu de navigation doit
 * répondre même quand le JavaScript ne se charge pas — et le socle est partagé
 * (§0 quater) : un `<script>` de plus dans le gabarit atterrit sur les écrans
 * des autres modules, où il n'a rien à faire.
 */

/*
 * ⚠️ **HORS DE L'ÉCRAN, PAS `display: none`.** Un interrupteur retiré du rendu
 * sort de la tabulation : le menu deviendrait inatteignable au clavier sur
 * téléphone. Il reste donc là, invisible et focusable.
 */
.lateral__interrupteur {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    border: 0;
    overflow: hidden;
    clip-path: inset(50%);
}

/*
 * ⚠️ **UNE HAUTEUR DÉCLARÉE, PAS DÉDUITE D'UN PADDING.** Le tiroir s'ouvre
 * SOUS ce bouton — sans quoi il le recouvrirait et il n'y aurait plus rien à
 * toucher pour refermer. Les deux valeurs se calculent l'une sur l'autre, et
 * une hauteur écrite noir sur blanc est la seule qui ne dérive pas quand on
 * retouche le texte ou l'icône.
 */
.lateral__bascule {
    display: none;
    align-items: center;
    gap: 9px;
    width: 100%;
    height: var(--bascule-haut, 44px);
    padding: 0 14px;
    border-bottom: 1px solid var(--line);
    background: var(--lateral-bg, #1e2b38);
    color: #fff;
    font-size: 14px;
    font-weight: 700;
    cursor: pointer;
}

/* Les trois barres du bouton, dessinées plutôt qu'importées. */
.lateral__bascule-barres {
    flex: 0 0 auto;
    width: 17px;
    height: 2px;
    background: currentcolor;
    box-shadow: 0 -5px 0 currentcolor, 0 5px 0 currentcolor;
}

/*
 * ⚠️ **LE LIBELLÉ SUIT L'ÉTAT.** Le bouton reste au-dessus du tiroir ouvert :
 * c'est par lui qu'on referme, et un libellé qui continuerait d'annoncer
 * « Menu du module » proposerait d'ouvrir ce qui est ouvert.
 */
.lateral__bascule-fermer { display: none; }

/* Le focus clavier se voit sur le bouton, l'interrupteur étant hors de l'écran. */
.lateral__interrupteur:focus-visible + .lateral__bascule {
    outline: 2px solid var(--brand);
    outline-offset: -3px;
}

/* Le voile qui ferme le tiroir d'un geste hors de lui. */
.lateral__voile { display: none; }

@media (max-width: 700px) {
    .lateral__bascule { display: flex; }

    .lateral__interrupteur:checked ~ .lateral__bascule .lateral__bascule-ouvrir { display: none; }
    .lateral__interrupteur:checked ~ .lateral__bascule .lateral__bascule-fermer { display: inline; }

    /* Le bouton reste au-dessus du tiroir : c'est aussi lui qui le referme. */
    .lateral__bascule {
        position: sticky;
        top: var(--barre-haut, 52px);
        z-index: 41;
    }

    .lateral__interrupteur:checked ~ .lateral__voile {
        display: block;
        position: fixed;
        inset: 0;
        z-index: 39;
        background: rgb(0 0 0 / 45%);
    }

    /*
     * ⚠️ **`fixed`, ET C'EST L'INVERSE DU CHOIX FAIT SUR GRAND ÉCRAN.** À
     * gauche, `sticky` sert justement à ce que la colonne garde sa place dans
     * le flux et que le contenu ne passe pas dessous. En tiroir, on veut
     * exactement le contraire : **hors du flux, donc sans rien pousser**.
     */
    .enveloppe--lateral .lateral {
        position: fixed;
        top: calc(var(--barre-haut, 52px) + var(--bascule-haut, 44px));
        left: 0;
        z-index: 40;
        width: min(300px, 86vw);
        height: calc(100vh - var(--barre-haut, 52px) - var(--bascule-haut, 44px));
        overflow-y: auto;
        border-bottom: 0;
        padding: 14px 12px 28px;
        transform: translateX(-100%);
        transition: transform .18s ease, visibility .18s;
        visibility: hidden;
    }

    /*
     * ⚠️ **`visibility`, PAS SEULEMENT `transform`.** Un tiroir seulement
     * décalé hors de l'écran reste dans l'ordre de tabulation et dans l'arbre
     * d'accessibilité : la tabulation partirait dans dix-huit liens invisibles
     * avant d'atteindre la page, et un lecteur d'écran les annoncerait tous.
     */
    .lateral__interrupteur:checked ~ .lateral {
        transform: translateX(0);
        visibility: visible;
    }

    /* Ouvert, le menu redevient une liste verticale : c'est un tiroir, pas une bande. */
    .enveloppe--lateral .lateral__nav { display: block; }
    .enveloppe--lateral .lateral__titre { padding: 9px 10px; }
    .enveloppe--lateral .lateral__liste { display: block; padding-left: 12px; }
    .enveloppe--lateral .lateral__lien.is-active {
        box-shadow: inset 3px 0 0 rgb(255 255 255 / 55%);
    }

    /*
     * ⚠️ **LE CONTENU REPREND TOUTE LA LARGEUR.** Le `padding-inline` de 28 px
     * posé dans l'enveloppe existait pour écarter la page de la colonne sombre ;
     * le tiroir refermé, il ne l'écarte plus de rien et coûte 56 px sur 390.
     */
    .enveloppe--lateral .page { padding-inline: 0; }
}

@media (prefers-reduced-motion: reduce) {
    .enveloppe--lateral .lateral { transition: none; }
}

.carte {
    padding: 20px;
    border: 1px solid var(--line);
    border-radius: var(--radius-card);
    background: var(--card);
    box-shadow: var(--shadow-sm);
}

.carte + .carte { margin-top: var(--gap); }
/*
 * ⚠️ **LA MARGE D'EMPILEMENT NE VAUT QU'EN FLUX.** Dans une grille, `.carte + .carte`
 * atteignait les cartes 2 à N et pas la première : la rangée prenait la hauteur
 * de la plus haute — 171 px plus 14 de marge — et la première, étirée à cette
 * hauteur sans marge, gagnait 14 px de vide en bas. C'était le vide qu'on
 * chassait depuis le matin dans les textes et les marges internes (03/09/2026),
 * mesuré : 185,38 px contre 171,38. La grille a déjà son `gap`.
 */
.grille > .carte, .tuiles > .carte, .soldes-cote-a-cote > .carte { margin-top: 0; }
.carte__titre { margin: 0 0 4px; font-size: 19px; }
.carte__note { margin: 0 0 16px; color: var(--muted); font-size: 13.5px; }
.carte__titre + .carte__corps { margin-top: 16px; }

.grille {
    display: grid;
    grid-template-columns: repeat(var(--colonnes, 3), minmax(0, 1fr));
    gap: var(--gap);
}

/* ---------------------------------------------------------------- champs */

.champ { display: block; margin-bottom: 16px; }
.champ__label { display: block; margin-bottom: 6px; font-size: 14px; font-weight: 700; }

/*
 * ⚠️ **L'unité se lit DEUX fois, et ce n'est pas un doublon** : dans
 * l'étiquette quand on parcourt le formulaire, contre le champ quand on tape.
 * C'est au moment de la saisie qu'on se trompe.
 */
.champ__unite { font-weight: 500; color: var(--muted); }

.champ__avec-unite { display: flex; align-items: center; gap: 7px; }
.champ__avec-unite .champ__saisie { min-width: 0; }
.champ__suffixe { flex: 0 0 auto; color: var(--muted); font-size: 13px; }
.champ__requis { color: var(--danger); }

.champ__aide {
    display: block;
    margin-top: 6px;
    color: var(--muted);
    font-size: 12px;
}

.champ__saisie,
input[type="text"], input[type="email"], input[type="password"],
input[type="date"], input[type="time"], input[type="number"],
select, textarea {
    width: 100%;
    padding: 9px 11px;
    border: 1px solid var(--line);
    border-radius: var(--radius-control);
    background: var(--field-bg);
    color: var(--ink);
    font-family: inherit;
    font-size: 14px;
}

.champ__saisie:focus, input:focus, select:focus, textarea:focus {
    border-color: var(--brand);
    outline: 2px solid var(--focus-ring);
    outline-offset: 1px;
}

.champ__saisie:disabled, input:disabled, select:disabled, textarea:disabled {
    background: var(--field-disabled);
    color: var(--muted);
    cursor: not-allowed;
}

/*
 * ⚠️ Un champ verrouillé n'est pas un champ vide. Il porte sa valeur et dit
 * pourquoi on ne peut pas la changer — §2 : « un responsable voit une fiche
 * différente, pas un formulaire aux champs vides ».
 */
.champ--verrouille .champ__saisie { background: var(--field-disabled); color: var(--muted); }

.champ--erreur .champ__saisie,
.champ--erreur input, .champ--erreur select, .champ--erreur textarea {
    border-color: var(--danger);
}

.champ__erreur {
    display: block;
    margin-top: 6px;
    color: var(--danger);
    font-size: 12px;
    font-weight: 700;
}

/* ------------------------------------------------------ éditeur Markdown */

/* La barre est du confort : masquée tant que le script ne l'a pas armée, et le
   champ reste utilisable sans elle (décision 060). */
.editeur-markdown__outils {
    margin: 0 0 8px;
    padding: 0;
}

.editeur-markdown__outils .bouton { min-height: 34px; padding: 5px 11px; }

.editeur-markdown__saisie {
    min-height: 340px;
    /* Le Markdown s'écrit et se relit en chasse fixe : l'alignement des listes et
       des titres se voit, ce qui est la moitié de « lisible sans manuel ». */
    font-family: ui-monospace, "Cascadia Code", "Consolas", monospace;
    font-size: 14px;
    line-height: 1.7;
    resize: vertical;
    tab-size: 2;
}

/* L'aperçu à côté du texte — décision 060 §8. Côte à côte quand il y a la place,
   empilé sous 900 px ; la hauteur suit le champ, pas l'inverse. */
.editeur-markdown--apercu .editeur-markdown__corps { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 12px; align-items: start; }
.editeur-markdown__apercu { min-height: 340px; max-height: 70vh; overflow: auto; padding: 12px 16px; border: 1px solid var(--line); border-radius: 10px; background: var(--surface-muted); }
.editeur-markdown__apercu img { max-width: 100%; height: auto; }
.editeur-markdown__bascule-apercu[aria-pressed="true"] { background: var(--brand); color: #fff; border-color: var(--brand); }
@media (max-width: 900px) { .editeur-markdown--apercu .editeur-markdown__corps { grid-template-columns: minmax(0, 1fr); } }

.editeur-markdown .champ__aide code {
    padding: 1px 5px;
    border-radius: 4px;
    background: var(--surface-muted);
    font-size: 12.5px;
}

/* ---------------------------------------------------------------- boutons */

.bouton {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    padding: 9px 15px;
    border: 1px solid transparent;
    border-radius: var(--radius-control);
    background: var(--brand);
    color: var(--brand-ink);
    font-family: inherit;
    font-size: 14px;
    font-weight: 700;
    text-decoration: none;
    cursor: pointer;
}

.bouton:hover { filter: brightness(.94); }
.bouton:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.bouton:disabled { opacity: .55; cursor: not-allowed; filter: none; }

.bouton--secondaire {
    border-color: var(--line);
    background: var(--field-bg);
    color: var(--ink);
}

.bouton--discret { border-color: transparent; background: transparent; color: var(--muted); }
.bouton--danger { background: var(--danger); }

.barre-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    align-items: center;
    margin-top: 18px;
}

.barre-actions--fin { justify-content: flex-end; }

/* ---------------------------------------------------------------- messages */

.message {
    margin: 0 0 18px;
    padding: 12px 14px;
    border: 1px solid var(--line);
    border-left-width: 4px;
    border-radius: var(--radius-control);
    background: var(--field-bg);
    font-size: 14px;
}

.message__titre { margin: 0 0 4px; font-size: 14px; font-weight: 800; }
.message ul { margin: 8px 0 0; padding-left: 20px; }
.message--succes { border-left-color: var(--ok); background: var(--ok-bg); color: var(--ok); }
.message--erreur { border-left-color: var(--danger); background: var(--danger-bg); color: var(--danger); }
.message--alerte { border-left-color: var(--warn); background: var(--warn-bg); color: var(--warn); }
.message--info { border-left-color: var(--brand); background: var(--brand-bg); color: var(--brand); }

/* ---------------------------------------------------------------- tableau */

/*
 * ⚠️ **`container-type` EST LÀ POUR LA LÉGENDE, pas pour la mise en page.** Le
 * `<caption>` vit DANS le tableau : quand celui-ci est plus large que l'écran,
 * la légende prend la largeur du tableau et le conteneur défilant lui coupe la
 * fin — sur téléphone, « chaque ligne s'enregistre se… ». La légende se borne
 * donc à la largeur du CONTENEUR (`cqi`), pas à celle du tableau.
 */
.tableau { width: 100%; overflow-x: auto; container-type: inline-size; }

.tableau table {
    width: 100%;
    border-collapse: collapse;
    font-size: 14px;
}

.tableau th, .tableau td {
    padding: 10px 12px;
    border-bottom: 1px solid var(--line);
    text-align: left;
    vertical-align: top;
}

.tableau th {
    color: var(--muted);
    font-size: 12.5px;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: .04em;
    white-space: nowrap;
}

/*
 * ⚠️ **LES VALEURS SE LISENT, ELLES NE SE DEVINENT PAS.** Le corps des tableaux
 * héritait des 15 px du body mais les libellés voisins étaient à 12 : l'écart
 * donnait une page qui paraissait entièrement petite. Une taille explicite ici
 * évite qu'un composant parent la rabaisse sans qu'on s'en aperçoive.
 */
.tableau td { font-size: 14px; }

/*
 * Un tableau compact — demandé le 03/09/2026 pour la liste des administrateurs :
 * le GSM et les mandats passaient sur deux lignes et triplaient la hauteur
 * des rangées. Une police un peu plus petite et pas de retour à la ligne.
 */
.tableau--compact th, .tableau--compact td { font-size: 13px; white-space: nowrap; padding-top: 6px; padding-bottom: 6px; }
.tableau--compact td.mandat small { font-size: 12px; }

/*
 * Les boutons d'action d'un tableau compact (gouvernance, 03/09/2026) tiennent
 * sur la ligne : côte à côte, en taille réduite. Empilés et pleine taille, ils
 * doublaient la hauteur de chaque rangée quand tout le reste tenait sur une.
 */
.tableau--compact .barre-actions { margin-top: 0; flex-direction: row; flex-wrap: nowrap; gap: 6px; }
.tableau--compact .barre-actions form { display: contents; }
.tableau--compact .barre-actions .bouton { width: auto; padding: 4px 9px; font-size: 12.5px; font-weight: 600; }

/* ---------------------------------------------------------- sélecteur d'équipe */

.selecteur-equipe { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 0 0 14px; }
.selecteur-equipe label { font-size: 13.5px; font-weight: 700; color: var(--muted); }
.selecteur-equipe select { width: auto; min-width: 12em; padding: 6px 9px; }

.tableau tbody tr:last-child td { border-bottom: 0; }
.tableau tbody tr:hover { background: var(--surface-muted); }
.tableau .num { text-align: right; font-variant-numeric: tabular-nums; }
/*
 * ⚠️ **`sticky` EN PLUS de la largeur** : bornée sans être collée, la légende
 * partirait avec le tableau au premier défilement horizontal et sortirait de
 * l'écran. Elle décrit le tableau — elle doit rester lisible quelle que soit la
 * colonne qu'on regarde.
 */
.tableau caption {
    position: sticky;
    left: 0;
    width: 100cqi;
    margin-bottom: 10px;
    color: var(--muted);
    font-size: 13.5px;
    text-align: left;
}

.etiquette {
    display: inline-block;
    padding: 3px 9px;
    border-radius: var(--radius-pill);
    background: var(--field-disabled);
    color: var(--ink-soft);
    font-size: 12px;
    font-weight: 700;
    white-space: nowrap;
}

.etiquette--ok { background: var(--ok-bg); color: var(--ok); }
.etiquette--danger { background: var(--danger-bg); color: var(--danger); }

/* La pastille d'aide — décision 063. Un <details> en ligne : le rond est le <summary>,
   le panneau se pose par-dessus le contenu. Rien n'est rendu quand rien n'est accroché. */
.aide { display: inline-block; position: relative; vertical-align: middle; margin-left: 6px; }
.aide > summary { list-style: none; }
.aide > summary::-webkit-details-marker { display: none; }
.aide__pastille { display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; border-radius: 50%; border: 1.5px solid var(--brand); color: var(--brand); font-size: 12px; font-weight: 700; line-height: 1; cursor: pointer; background: var(--card); }
.aide__pastille:hover, .aide[open] > .aide__pastille { background: var(--brand); color: #fff; }
/* Le crayon de l'éditeur : discret, à côté de la pastille — ou seul, quand rien n'est écrit. */
.aide__crayon { display: inline-block; margin-left: 4px; color: var(--muted); font-size: 12px; text-decoration: none; vertical-align: middle; opacity: .55; }
.aide__crayon:hover { color: var(--brand); opacity: 1; }
.aide__panneau { position: absolute; z-index: 30; top: calc(100% + 6px); left: 0; min-width: 16rem; max-width: 24rem; padding: 10px 12px; border: 1px solid var(--line-strong); border-radius: 9px; background: var(--card); box-shadow: 0 6px 20px rgb(0 0 0 / 12%); font-weight: 400; text-align: left; }
.aide__rubrique { margin: 0 0 4px; color: var(--muted); font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; }
.aide__rubrique + .aide__liste + .aide__rubrique { margin-top: 9px; }
.aide__liste { margin: 0; padding: 0; list-style: none; }
.aide__liste li { display: grid; gap: 1px; padding: 3px 0; }
.aide__liste a { color: var(--brand); font-weight: 700; text-decoration: none; }
.aide__liste a:hover { text-decoration: underline; }
.etiquette--alerte { background: var(--warn-bg); color: var(--warn); }

/* Le trou explicite : une case délibérément vidée, pas une case sans donnée. */
.etiquette--vide { background: repeating-linear-gradient(45deg,var(--field-disabled),var(--field-disabled) 4px,var(--surface-strong) 4px,var(--surface-strong) 8px); color: var(--ink-soft); }

.vide {
    padding: 40px;
    border: 1px dashed var(--line-strong);
    border-radius: var(--radius-card);
    background: var(--field-bg);
    color: var(--muted);
    text-align: center;
}

.muet { color: var(--muted); }
.num { font-variant-numeric: tabular-nums; }

/*
 * ⚠️ Réservé aux lecteurs d'écran. Sert aux libellés que le regard déduit de
 * la mise en page mais qu'une synthèse vocale ne peut pas deviner.
 */
.sr-only {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0,0,0,0);
    white-space: nowrap;
    border: 0;
}

/* ------------------------------------------------------- compteurs RH */

/*
 * ⚠️ L'EN-TÊTE DE SEMAINE. Ses chiffres viennent du plan calculé sur la
 * grille affichée — jamais d'une lecture séparée en base. C'est un point de
 * conception, pas de style : l'écran Scriptcase montre 0 h 00 dans son
 * en-tête pendant que sa grille porte 38 h, parce que les deux lisent deux
 * sources. Le CSS ne peut pas empêcher ça, mais il ne doit pas le maquiller.
 */
/*
 * ⚠️ **UNE LIGNE DE VALEURS, PLUS DIX CARTES.** Le prévisionnel et les
 * prestations sont des écrans de SAISIE : les compteurs en cartes occupaient la
 * moitié de la hauteur avant qu'on n'atteigne la grille, alors qu'on vient
 * encoder un planning. Ils sont désormais **sous** la grille, et tiennent sur
 * une ligne.
 *
 * ⚠️ **Le point médian sépare, il ne décore pas.** Sans lui, « Presté 22h48
 * Congés 15h12 » se lit comme une phrase et il faut compter les mots pour
 * retrouver les paires.
 */
.compteurs__ligne {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 0;
    margin: 0;
}

.compteurs__ligne .compteur {
    display: flex;
    align-items: baseline;
    gap: 6px;
    padding: 0 14px;
    border: 0;
    border-radius: 0;
    background: none;
}

/* Le séparateur, entre les valeurs et jamais avant la première. */
.compteurs__ligne .compteur + .compteur { border-left: 1px solid var(--line); }

.compteurs__ligne dt {
    font-size: 12px;
    color: var(--muted);
    white-space: nowrap;
}

.compteurs__ligne dd {
    margin: 0;
    font-size: 15px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/*
 * ⚠️ **Les quatre compteurs qui touchent un SOLDE se distinguent des mesures.**
 * « Reconnu » et « Récupérables » engagent le compteur de l'employé ; « Presté »
 * et « Férié » ne font que décrire la semaine. La couleur dit lesquels comptent.
 */
.compteurs__ligne .compteur--fort dd { color: var(--brand); }

/*
 * ⚠️ Le manque n'est pas une erreur : c'est l'entrée de la chaîne du §14 —
 * case vidée, manque hebdomadaire, défalcation en récupération. Il se
 * remarque sans crier.
 */
.compteurs__ligne .compteur--manque dd { color: var(--warn); }

.compteurs__mention {
    margin: 0 0 10px;
    font-size: 13px;
    color: var(--muted);
}

.compteurs__mention:last-child { margin: 10px 0 0; }

/*
 * ⚠️ UNE PROJECTION S'AFFICHE EN JETON, pas en champ désactivé. Un champ grisé
 * invite à essayer ; un jeton ne promet rien. Pour changer une projection on
 * modifie la DEMANDE, qui repasse par le cycle de validation (§2).
 */
.jeton {
    display: inline-block;
    padding: 2px 9px;
    margin: 1px 3px 1px 0;
    border-radius: var(--radius-pill);
    background: var(--bg);
    border: 1px solid var(--line);
    font-size: 12px;
    white-space: nowrap;
}

/*
 * Les signaux d'une semaine à valider — §18, étape 6.
 *
 * ⚠️ **`white-space: normal` et pas de `nowrap`.** Un signal est une PHRASE,
 * pas une étiquette : « Une journée dépasse 11 h (11h40) » sur une seule ligne
 * pousserait la carte hors de l'écran sur un téléphone.
 */
.liste-signaux {
    list-style: none;
    margin: 8px 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
}

.liste-signaux .jeton {
    white-space: normal;
}

/*
 * ⚠️ **La variante grave se voit, elle ne refuse rien.** Sur les prestations on
 * signale, on ne bloque pas (§10) : la couleur dit « regardez ceci », pas
 * « corrigez avant de continuer ».
 */
.jeton--alerte {
    background: var(--warn-bg);
    border-color: var(--warn);
    color: var(--warn);
}

/*
 * ⚠️ **LES SIX FAMILLES DE TYPES — la palette du récapitulatif mensuel.** Les
 * blocs y étaient tous gris : un mois de trente lignes identiques ne se lit
 * pas, il se déchiffre. La grille de présence, elle, se balaye du regard parce
 * que la couleur y porte l'état.
 *
 * ⚠️ **LA COULEUR NE PORTE JAMAIS L'INFORMATION SEULE.** Chaque bloc écrit son
 * type en clair — « Congés », « Maladie » —, et le mot survit en niveaux de
 * gris comme il survit au daltonisme. La couleur ne fait qu'accélérer une
 * lecture qui reste possible sans elle.
 *
 * ⚠️ **Six familles, pas dix-sept teintes.** Ce qu'on lit d'un coup d'œil sur
 * un mois, c'est « travaillé / absent pourquoi » — pas la nuance entre un congé
 * de circonstance et une raison impérieuse. Le regroupement vit dans
 * `HoraireType::famille()`, jamais dans cette feuille : deux écrans ne doivent
 * pas pouvoir peindre la même journée de deux couleurs.
 *
 * ⚠️ **Aplats PÂLES et texte foncé, contrairement à la présence.** Un mois
 * affiche jusqu'à six blocs par jour sur une carte étroite : des aplats saturés
 * en feraient un damier. Ici la couleur situe, elle ne crie pas.
 */
.jeton--travail { background: #e7f7f0; border-color: #9ed8bd; color: #0a5c42; }
.jeton--conges { background: #e8f1fd; border-color: #a8c8ee; color: #1c4e8f; }
.jeton--recup { background: #efe9fb; border-color: #c3b2e8; color: #4a3287; }
.jeton--maladie { background: var(--warn-bg); border-color: var(--warn-line); color: var(--warn); }
.jeton--ferie { background: #fdeef5; border-color: #e8a9c0; color: #8f2350; }

/*
 * ⚠️ **« Sans solde » et « case vidée » RESTENT GRIS, et c'est le bon signal** :
 * ces cases ne valent rien — ni reconnu, ni compensatoire. Leur donner une
 * couleur les rendrait aussi présentes que les autres à la lecture, alors que
 * ce sont précisément les journées où il ne s'est rien passé.
 */
.jeton--sans-solde,
.jeton--vide,
.jeton--inconnu { background: var(--surface-strong); border-color: var(--line-strong); color: var(--muted); }

/*
 * ⚠️ « PROPOSÉ » N'EST PAS « ENREGISTRÉ ». La case porte l'horaire de base et
 * rien n'est écrit en base. Sans cette distinction, un employé croirait sa
 * semaine encodée alors qu'aucune ligne n'existe — et la première sauvegarde
 * la remplirait de trous.
 */
.ligne--proposee > * { background: color-mix(in srgb, var(--warn-bg) 45%, transparent); }

/* --------------------------------------------------- grille de semaine */

/*
 * ⚠️ HORIZONTALE : sept lignes de jours, trois périodes en colonnes. C'est la
 * disposition de Scriptcase, et ce n'est pas qu'une habitude — vingt-et-une
 * lignes empilées obligent à défiler pour saisir une semaine, et on ne voit
 * jamais sa semaine entière. Ici elle tient sur un écran.
 */
.grille-semaine table { table-layout: fixed; }

.grille-semaine th[scope="col"] { width: auto; }
.grille-semaine th[scope="row"] { width: 108px; white-space: nowrap; }

/*
 * ⚠️ **LA COLONNE TOTAL PRENAIT LA LARGEUR D'UNE PÉRIODE.** Avec
 * `table-layout: fixed`, ce sont les cellules de l'EN-TÊTE qui fixent les
 * colonnes, et `width: 1%` posé sur les `td.num` n'y pouvait rien : « 7h36 »
 * occupait autant de place que trois champs de saisie. Les largeurs se posent
 * donc sur les `th` de l'en-tête — Jour, puis les trois périodes qui se
 * partagent le reste, Total, Note.
 */
.grille-semaine thead th:first-child { width: 108px; }
.grille-semaine thead th:last-child { width: 72px; }

/*
 * ⚠️ **LA MÊME GRILLE N'A PAS LE MÊME NOMBRE DE COLONNES PARTOUT.** Le
 * prévisionnel et les prestations portent une colonne Note après le Total ;
 * l'horaire de base s'arrête au Total. Des largeurs comptées depuis la droite
 * (« avant-dernière = Total ») écrasaient Soirée à 72 px sur l'horaire de base
 * et donnaient 170 px au Total — les sélecteurs de type s'étiraient, le Total
 * sortait du cadre, un ascenseur apparaissait. Mesuré le 02/09/2026.
 *
 * ➡️ La grille AVEC note le dit par une classe ; la position ne le devine pas.
 */
.grille-semaine--notes thead th:nth-last-child(2) { width: 72px; }
.grille-semaine--notes thead th:last-child { width: 170px; }

.grille-semaine td { vertical-align: top; }

/*
 * ⚠️ **TROIS PÉRIODES, TROIS BLOCS — pas neuf champs alignés.** Matin,
 * Après-Midi et Soirée se confondaient : trois fois « type, début, fin » sans
 * démarcation, et l'œil ne savait plus dans quelle période il saisissait. Un
 * filet à gauche de chaque période, et un fond à peine plus sombre sur celle
 * du milieu : la période se lit avant le champ, sur l'en-tête comme sur les
 * lignes.
 *
 * ⚠️ Le fond est posé sur la CELLULE ; la teinte des cases proposées
 * (`.ligne--proposee > *`) vit sur son contenu. Les deux se superposent sans
 * s'effacer.
 */
.grille-semaine thead th:nth-child(n+2):nth-child(-n+4),
.grille-semaine td[data-periode]:not([data-periode="Total"]) {
    border-left: 2px solid var(--line);
}

.grille-semaine thead th:nth-child(3),
.grille-semaine td[data-periode="Apres-Midi"] {
    background: color-mix(in srgb, var(--bg) 70%, transparent);
}

.grille-semaine td[data-periode="Total"] { border-left: 2px solid var(--line); }

/*
 * ⚠️ LA DATE SOUS LE NOM DU JOUR — reprise de la grille Scriptcase, et pas
 * décorative : le férié se détecte sur la DATE et non sur le type (§6), et
 * c'est lui qui déclenche le taux 3.
 */
.grille-semaine__date { display: block; font-size: 12px; font-weight: 400; }
.grille-semaine__pointage { display: block; font-size: 12px; font-weight: 400; white-space: nowrap; cursor: help; }

/*
 * ⚠️ **La flèche de copie tient sur la LIGNE du jour.** Sous la date, elle
 * ajoutait une ligne à chacune des sept rangées — et cette grille a justement
 * été refaite pour tenir sur un écran.
 */
.jour-entete {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 6px;
}

/*
 * ⚠️ LE TOTAL DE LA SEMAINE compte les HEURES ENCODÉES, pas le reconnu : une
 * absence valorisée depuis l'horaire de base ne porte pas d'heures sur sa
 * ligne. L'écart avec l'en-tête est légitime, et la note de la ligne le dit —
 * un total qui aurait l'air faux serait pire que pas de total du tout.
 */
.grille-semaine__total > * {
    background: var(--bg);
    font-weight: 650;
}

.grille-semaine__total th[scope="row"] {
    width: auto;
    white-space: normal;
    text-align: right;
}

.grille-semaine__total small { display: block; font-weight: 400; }

/* Les trois contrôles d'une case s'empilent dans leur colonne. */
/*
 * ⚠️ **UNE LIGNE PAR JOUR, PAS TROIS.** La cellule empilait le type au-dessus
 * des deux heures : sept jours donnaient vingt-et-une lignes visuelles, plus
 * les notes, et une semaine ne tenait plus sur un écran. Le type et les deux
 * heures vivent maintenant côte à côte — c'est la forme de Scriptcase, et
 * elle tient.
 */
.case-saisie {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 4px;
}

/*
 * ⚠️ **`min-width: 0` RÉDUISAIT LE SÉLECTEUR À SA FLÈCHE.** Avec
 * `table-layout: fixed`, la colonne se partage entre trois contrôles ; les deux
 * champs d'heures ont une largeur fixe, et le type absorbait tout le manque.
 * À zéro de minimum, il tombait à la largeur du chevron — le libellé
 * « Present » était bien rendu dans le HTML, il n'y avait plus de place pour
 * l'afficher.
 *
 * ➡️ **Un plancher qui contient le plus long libellé** — « Télétravail »,
 * « Compensatoires » — plutôt qu'une largeur fixe : il grandit encore quand la
 * colonne le permet.
 */
.case-saisie > .champ__saisie {
    flex: 1 1 auto;
    min-width: 8.6em;
}

.case-heures { display: flex; gap: 3px; flex: 0 0 auto; }
.case-heures .champ__saisie { min-width: 0; width: 5.9em; padding-inline: 3px; }

.grille-semaine .champ__saisie { font-size: 12.5px; padding-block: 4px; }

/*
 * ⚠️ **LA CELLULE REVIENT À DEUX LIGNES quand la GRILLE manque de place**, plutôt
 * que d'imposer un défilement horizontal. Une grille qu'on fait glisser cache
 * toujours la colonne qu'on regarde : mieux vaut deux lignes lisibles qu'une
 * seule inaccessible.
 *
 * ⚠️ **LA MÉDIA QUERY MESURAIT LA MAUVAISE CHOSE — corrigé le 07/09/2026, sur
 * capture d'écran.** Le seuil portait sur la LARGEUR DE FENÊTRE ; or la grille
 * n'en reçoit qu'une part, et la chaîne se compte :
 *
 *     1440 (fenêtre) − 272 (menu) − 56 (marges de page) − 40 (marge de carte)
 *     = 1072 px pour la table
 *
 * Elle en réclame **1208**. Il manquait donc **136 px**, soit environ 45 par
 * période — exactement ce que la capture montrait : le second champ d'heures
 * **débordait de sa cellule**, l'icône d'horloge coupée par la bordure. Et la
 * média query, elle, ne se déclenchait pas : 1440 est bien au-dessus de 1200.
 *
 * ➡️ **Une ligne ne revient qu'au-delà de ~1576 px de fenêtre** (1208 + 272 + 56
 * + 40). En dessous, deux lignes — c'est le comportement voulu, pas un pis-aller.
 *
 * ➡️ **Le compte, tout en `border-box`** : Jour 108 + Total 72 + Note 170, plus
 * trois périodes à 286 px — sélecteur 8,6 em (107,5 px) + 4 px + deux champs de
 * 5,9 em et leur écart (150,5 px) + 24 px de marge intérieure. Soit **1208 px de
 * table**, jamais de fenêtre.
 *
 * ➡️ **`@container`, pas `@media`** : `.tableau` porte déjà
 * `container-type: inline-size`, donc la règle peut mesurer **l'espace réel de la
 * grille**. C'est la seule mesure qui dise la vérité quel que soit le menu.
 *
 * ⚠️ **La média query RESTE**, en filet pour un navigateur sans requêtes de
 * conteneur : empiler deux fois revient à empiler une fois.
 */
@container (max-width: 1210px) {
    .case-saisie { flex-direction: column; align-items: stretch; }
    .case-saisie > .champ__saisie { width: 100%; }
    .case-heures .champ__saisie { width: 100%; }
}

@media (max-width: 1200px) {
    .case-saisie { flex-direction: column; align-items: stretch; }
    .case-saisie > .champ__saisie { width: 100%; }
    .case-heures .champ__saisie { width: 100%; }
}

/*
 * ⚠️ ORANGE, ET RIEN D'AUTRE (§9). Un type renseigné sans heures ne bloque pas,
 * ne déclenche aucune boîte de dialogue, n'empêche pas la validation : vérifier
 * ses prestations relève de la responsabilité de l'employé. Une alerte
 * hebdomadaire finit cliquée sans être lue et érode la crédibilité des vraies
 * alertes — le signal reste visuel.
 */
.total--sans-heures { color: var(--warn); font-weight: 650; }

/*
 * La flèche qui recopie une journée sur la suivante. Geste quotidien de la
 * version Scriptcase : une semaine ordinaire se saisit une fois puis se
 * propage.
 */
.copie-jour {
    border: 1px solid var(--line);
    background: var(--card);
    color: var(--muted);
    border-radius: var(--radius-control);
    padding: 1px 7px;
    cursor: pointer;
    font-size: 13px;
    line-height: 1.5;
}

.copie-jour:hover { color: var(--brand); border-color: var(--brand); }

/* Le sélecteur de semaine, ses deux flèches de part et d'autre. */
.semaine-nav { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.semaine-nav form { display: flex; align-items: center; gap: 8px; }

/* ---------------------------------------------------------------- < 700px */

/*
 * ⚠️ UNE COLONNE sous 700px. La grille RH fait sept jours de large : sur un
 * téléphone elle déborde, et un tableau qui déborde se lit de travers ou pas
 * du tout. Les tableaux gardent leur défilement horizontal propre, dans leur
 * conteneur, plutôt que de faire glisser la page entière.
 */
@media (max-width: 700px) {
    .page { width: min(calc(100% - 20px), var(--page)); padding-top: 20px; }
    .page__header { flex-direction: column; align-items: flex-start; gap: 10px; }
    .page__header h1 { font-size: 26px; }
    .grille { grid-template-columns: 1fr; }
    .carte { padding: 16px; }
    .barre-actions { flex-direction: column; align-items: stretch; }
    .barre-actions .bouton { width: 100%; }
}

@media (max-width: 700px) {
    .compteurs__ligne .compteur { padding-inline: 10px; }
}

/*
 * ⚠️ LA GRILLE BASCULE EN CARTES, une par jour. Sept colonnes ne tiennent pas
 * sur un téléphone, et un tableau qui déborde se lit de travers ou pas du tout.
 *
 * ⚠️ Le coût est réel : `display: block` détruit la sémantique de tableau, donc
 * une synthèse vocale n'annonce plus « Mardi, Après-Midi ». C'est pourquoi
 * chaque case répète sa période en `data-periode` — le libellé que l'en-tête
 * portait revient dans la cellule.
 */
@media (max-width: 700px) {
    .grille-semaine table,
    .grille-semaine tbody,
    .grille-semaine tr,
    .grille-semaine th,
    .grille-semaine td { display: block; width: auto; }

    .grille-semaine thead { display: none; }

    .grille-semaine tr {
        border: 1px solid var(--line);
        border-radius: var(--radius-card);
        background: var(--card);
        margin-bottom: 12px;
        padding: 10px 12px;
    }

    .grille-semaine th[scope="row"] {
        border: 0;
        padding: 0 0 8px;
        font-size: 15px;
        font-weight: 700;
    }

    .grille-semaine td { border: 0; padding: 6px 0; }

    /*
     * ⚠️ En cartes, la ligne de total n'a plus de colonne où s'aligner : elle
     * redevient une simple ligne de texte. Sans ça, son `colspan` la ferait
     * flotter à droite d'une carte vide.
     */
    .grille-semaine__total th[scope="row"] { text-align: left; }
    .grille-semaine__total td { font-size: 18px; }

    .grille-semaine td[data-periode]::before {
        content: attr(data-periode);
        display: block;
        margin-bottom: 3px;
        font-size: 12px;
        text-transform: uppercase;
        letter-spacing: .04em;
        color: var(--muted);
    }
}

/* ----------------------------------------------- récapitulatif mensuel */

/*
 * ⚠️ UN CALENDRIER, PAS UN TABLEAU DE SAISIE. Cet écran RELIT un mois pour le
 * vérifier avant transmission : rien n'y est modifiable, et il ne porte ni code
 * paie ni récupérable — les seconds sont hebdomadaires, un mois n'a pas de
 * seuil.
 */
.mois {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
    gap: 10px;
}

.mois__jour {
    border: 1px solid var(--line);
    border-radius: var(--radius-control);
    padding: 9px 10px;
    background: var(--bg);
}

.mois__jour--weekend { background: color-mix(in srgb, var(--line) 22%, transparent); }
.mois__jour--vide { opacity: .55; }

.mois__date {
    display: flex;
    align-items: baseline;
    gap: 6px;
    margin: 0 0 6px;
    font-size: 13px;
}

.mois__date strong { font-size: 17px; }

.mois__total {
    margin-left: auto;
    font-weight: 650;
    font-variant-numeric: tabular-nums;
}

/* ------------------------------------- note du jour et dérogations */

/*
 * ⚠️ LA NOTE EST UNE LIGNE DE TABLEAU À ELLE SEULE, sous les trois périodes :
 * c'est une note sur la JOURNÉE, pas sur une case. La glisser dans une cellule
 * la rattacherait à une période et obligerait à choisir laquelle.
 *
 * ⚠️ Elle est MODIFIABLE, toujours — et c'est ce qui interdit d'y loger la
 * justification d'une dérogation, qui se fige à la validation (§10).
 */
/*
 * ⚠️ **La note est une COLONNE, plus une ligne.** Elle prenait toute la largeur
 * sous chaque jour et doublait la hauteur de la grille. À droite, elle reste
 * sous les yeux sans peser.
 */
td.note-jour { min-width: 150px; }
.note-jour__saisie { width: 100%; font-size: 12.5px; padding-block: 4px; }

/* Une journée sans note s'efface : la case reste, elle ne réclame rien. */
.note-jour--vide .note-jour__saisie { background: transparent; border-style: dashed; }

/*
 * ⚠️ **Les explications descendent sous le tableau.** Elles ne se lisent pas à
 * chaque consultation — on vient chercher un chiffre — mais elles doivent
 * rester à portée : un renvoi coûte un coup d'œil, une colonne de texte coûte
 * la largeur de tout le tableau.
 */
/*
 * ⚠️ **DES VALEURS COURTES N'ONT PAS BESOIN DE LA LARGEUR ENTIÈRE.** Six
 * champs de quelques caractères occupaient chacun une ligne pleine, avec un
 * vide des deux tiers à droite. `auto-fit` place ce qui tient : Nom, Prénom et
 * Date sur une ligne, Fonction et Régime sur la suivante — sans largeur écrite
 * en dur, donc sans casse quand un libellé s'allonge.
 */
.fiche-champs,
.grille-champs {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 4px 22px;
    margin: 0;
}

.fiche-champs__item { padding: 6px 0; border-bottom: 1px solid var(--line); }

.fiche-champs dt {
    font-size: 12px;
    font-weight: 650;
    color: var(--muted);
}

.fiche-champs dd { margin: 2px 0 0; }

/*
 * ⚠️ UNE NOTE QU'ON LIT UNE FOIS VIT EN INFOBULLE — demandé le 02/09/2026.
 * Les notes de bas de tableau des soldes ajoutaient quatre lignes à un écran de
 * saisie. Le pointillé dit qu'il y a quelque chose à survoler ; le `title`
 * porte le texte, que le navigateur affiche sans une ligne de script.
 */
.infobulle { text-decoration: underline dotted; text-underline-offset: 3px; cursor: help; }

/*
 * Les deux tableaux du bloc « D'où vient votre solde » côte à côte — congés à
 * gauche, heures à droite. Quatre et cinq colonnes de chiffres courts : empilés
 * ils doublaient la hauteur du bloc au-dessus du formulaire de demande.
 * Sous 900 px, une colonne : les chiffres n'auraient plus la place.
 */
.soldes-cote-a-cote { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 24px; align-items: start; }
.soldes-cote-a-cote h3 { margin-top: 0; }
@media (max-width: 900px) { .soldes-cote-a-cote { grid-template-columns: 1fr; } }

/*
 * ⚠️ LE FORMULAIRE DE DEMANDE NE MONTRE QUE CE QUI COMPTE — demandé le
 * 02/09/2026. Chaque bloc porte en `data-pour` les natures (ou le mode) pour
 * lesquelles il existe ; `:has()` lit la valeur choisie dans le `<select>` et
 * masque le reste. Sans `:has()`, rien n'est masqué : tout reste utilisable,
 * et le serveur ignore ce qui ne s'applique pas. Aucun script.
 *
 * ⚠️ UNE RÈGLE PAR NATURE, et une nature oubliée ne masque RIEN : mission et
 * formation (décision 064) sont arrivées sans les leurs — « prélever sur »,
 * « compter en » et les champs de mission s'affichaient tous sur une formation,
 * signalé le 11/09/2026. Un test lit cette feuille et exige une règle pour chaque
 * nature que `DemandeController` sait encoder.
 */
form:has(select[name="type"] option[value="CONGE"]:checked) .selon-nature:not([data-pour~="CONGE"]),
form:has(select[name="type"] option[value="TELETRAVAIL"]:checked) .selon-nature:not([data-pour~="TELETRAVAIL"]),
form:has(select[name="type"] option[value="RECUPERATION"]:checked) .selon-nature:not([data-pour~="RECUPERATION"]),
form:has(select[name="type"] option[value="MISSION"]:checked) .selon-nature:not([data-pour~="MISSION"]),
form:has(select[name="type"] option[value="FORMATION"]:checked) .selon-nature:not([data-pour~="FORMATION"]),
form:has(select[name="mode"] option[value="demi_journee"]:checked) .selon-mode[data-pour="heures"],
form:has(select[name="mode"] option[value="heures"]:checked) .selon-mode[data-pour="demi_journee"] {
    display: none;
}

/* Des chiffres courts n'ont pas besoin de colonnes larges. */
.tableau td.num { white-space: nowrap; width: 1%; }

.violations { margin: 0; padding-left: 18px; }
.violations li { margin-bottom: 10px; }
.violations small { display: block; }

/*
 * ⚠️ LE CONTOURNEMENT DOIT ÊTRE PLUS LOURD QUE LE BLOCAGE (§10). Le motif est
 * un champ obligatoire, pas une case à cocher : demander une dérogation coûte
 * une phrase, et c'est délibéré.
 */
.derogation-demande {
    display: flex;
    gap: 6px;
    align-items: center;
    margin-top: 6px;
}

.derogation-demande .champ__saisie { flex: 1; min-width: 0; }

/* --------------------------------------------------------------- équipes */

/*
 * ⚠️ UNE ÉQUIPE DÉSACTIVÉE RESTE LISIBLE. On désactive, on ne supprime pas :
 * l'historique d'appartenance se conserve, et une équipe dissoute reste
 * lisible sur les décisions qu'elle a portées. Elle s'efface visuellement,
 * elle ne disparaît pas.
 */
.equipe--inactive { opacity: .62; }

.liste-nue { list-style: none; margin: 0; padding: 0; }

.liste-nue li {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 3px 0;
}

.liste-nue form { display: contents; }

/* ------------------------------------------------- gestion des équipes (35.png) */

/*
 * Trois colonnes comme l'écran Scriptcase : les équipes, l'équipe choisie, le
 * personnel disponible. Les zones de dépôt sont des listes ordinaires ; le
 * pointillé dit « on peut déposer ici » quand le script tourne.
 */
.equipes-plan {
    display: grid;
    grid-template-columns: 200px minmax(0, 1fr) 300px;
    gap: 16px;
    align-items: start;
}

.equipes-plan > .carte { margin-top: 0; }

.equipes-liste { list-style: none; margin: 0 0 12px; padding: 0; }

.equipes-liste a {
    display: block;
    padding: 8px 10px;
    border-radius: 8px;
    color: inherit;
    text-decoration: none;
}

.equipes-liste a:hover { background: var(--fond-doux, #f3f5f4); }

.equipes-liste a[aria-current] {
    background: var(--ok-bg);
    color: var(--ok);
    font-weight: 600;
}

.equipes-nouvelle summary,
.equipe-reglages summary { cursor: pointer; color: var(--muted); font-size: .92rem; }

.equipe-reglages { margin: 8px 0 18px; }

.equipe-titre { margin: 0 0 4px; }

.zone-titre {
    margin: 18px 0 6px;
    font-size: .8rem;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--muted);
}

.zone {
    list-style: none;
    margin: 0;
    padding: 8px;
    min-height: 56px;
    border: 2px dashed var(--bordure, #d5dad8);
    border-radius: 10px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.zone--survol { border-color: var(--ok); background: var(--ok-bg); }

.zone__vide { padding: 8px 4px; font-size: .92rem; }

.zone--retrait {
    margin-top: 14px;
    justify-content: center;
    align-items: center;
    text-align: center;
    color: var(--muted);
    font-size: .92rem;
}

.carte-personne {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "nom gestes" "role gestes";
    column-gap: 10px;
    padding: 8px 12px;
    background: #fff;
    border: 1px solid var(--bordure, #d5dad8);
    border-radius: 8px;
}

.glissable .carte-personne { cursor: grab; }

.carte-personne.en-vol { opacity: .5; }

.carte-personne__nom { grid-area: nom; font-weight: 600; }

.carte-personne__role { grid-area: role; font-size: .85rem; }

.carte-personne > form,
.carte-personne__gestes { grid-area: gestes; display: flex; gap: 6px; align-items: center; }

.carte-personne .bouton { padding: 4px 8px; font-size: .82rem; white-space: nowrap; }

/*
 * ⚠️ Dans la colonne du personnel disponible, 280 px de large, deux boutons à
 * côté du nom l'écrasaient sur une lettre (capture 52). Là, les gestes passent
 * SOUS le nom, sur leur propre ligne.
 */
.zone--disponible .carte-personne {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "nom" "role" "gestes";
}

.zone--disponible .carte-personne__gestes { margin-top: 6px; flex-wrap: wrap; }

.carte-personne__nom { overflow-wrap: anywhere; }

@media (max-width: 1100px) {
    .equipes-plan { grid-template-columns: 1fr; }
}

/* ------------------------------------------------- tuiles du tableau de bord */

.tuiles {
    display: grid;

    /*
     * ⚠️ **UNE SEULE LIGNE, quel que soit le NOMBRE de tuiles.** `auto-fit`
     * plaçait autant de colonnes que la largeur en permettait, et la septième
     * tombait seule à la ligne — un bloc orphelin, et un grand vide à droite.
     *
     * ⚠️ **`grid-auto-flow: column` s'adapte au rôle**, là où `repeat(7, ...)`
     * aurait laissé deux colonnes vides à un chef d'équipe : il n'en voit que
     * cinq, ni la maladie ni les sorties (§2). Le nombre de tuiles dépend de
     * qui regarde ; la mise en page ne doit pas le supposer.
     */
    /*
     * ⚠️ **COLONNES EXPLICITES, ÉGALES — corrigé le 03/09/2026.** Les pistes
     * implicites de `grid-auto-flow: column` ne se partageaient PAS la largeur
     * à parts égales : la première tuile prenait celle de son contenu le plus
     * long, et « Demandes d'absence » débordait nettement sur les autres. Deux
     * retouches de texte n'y avaient rien changé — le défaut était dans la
     * grille, pas dans les mots. `repeat(N, minmax(0, 1fr))` règle ça d'un
     * coup ; N vient de la vue (`--tuiles`), puisqu'il dépend du rôle.
     */
    grid-template-columns: repeat(var(--tuiles, 7), minmax(0, 1fr));
    gap: 10px;
    margin-bottom: 20px;
}

/*
 * ⚠️ **`grid-auto-rows: 1fr` A ÉTÉ RETIRÉ.** Il étirait la rangée à une hauteur
 * que rien ne réclamait, et les cartes gagnaient une trentaine de pixels vides
 * en bas. Deux corrections successives ont déplacé ce vide — sous le bouton,
 * puis de part et d'autre — sans jamais le supprimer : **la question n'était
 * pas où le placer, c'était pourquoi il existait.**
 *
 * ➡️ **Des cartes de même contenu ont naturellement la même hauteur.** L'étirement
 * par défaut d'une grille suffit à les égaliser dans leur rangée ; il n'ajoute
 * rien tant que les contenus se ressemblent. Si un écart d'une ligne
 * réapparaît, **c'est la carte qu'il faut corriger** — un titre ou une note
 * trop longue —, pas la grille qu'il faut étirer.
 */

/*
 * ⚠️ **Sous 1100 px, sept colonnes deviennent illisibles** — une centaine de
 * pixels par tuile, moins le padding : il ne reste pas de quoi écrire un mot.
 * On repasse alors au retour à la ligne, où l'orphelin coûte moins cher qu'un
 * texte coupé.
 */
@media (max-width: 1200px) {
    /* deux rangées, colonnes toujours égales — la moitié haute des tuiles par rangée */
    .tuiles { grid-template-columns: repeat(var(--tuiles-demi, 4), minmax(0, 1fr)); }
}

@media (max-width: 700px) {
    .tuiles { grid-template-columns: minmax(0, 1fr); }
}

/*
 * ⚠️ **Une tuile est plus SERRÉE qu'une carte ordinaire.** Le padding de 20 px
 * du socle vaut pour une carte qui porte un tableau ou un formulaire ; sur sept
 * tuiles alignées il se paie six fois en largeur et deux fois en hauteur, pour
 * entourer trois lignes de texte.
 */
.tuile { padding: 12px 10px 10px; text-align: center; }

/*
 * ⚠️ **NI MARGE AUTOMATIQUE, NI ÉTIREMENT.** `margin-top: auto` calait le
 * bouton en bas ; mais une marge automatique n'espace pas, elle **absorbe** —
 * elle ramassait tout l'espace libre en un seul trou. `flex: 1` sur le corps
 * faisait la même chose autrement : il réclamait une hauteur que le contenu ne
 * demandait pas.
 *
 * ➡️ **Quatre écarts fixes, du chiffre au bouton.** La carte fait exactement la
 * hauteur de ce qu'elle contient, et les boutons s'alignent parce que les
 * cartes se ressemblent — pas parce qu'on les y pousse.
 *
 * ⚠️ Le flex vit sur `.carte__corps`, pas sur `.tuile` : le composant enveloppe
 * son contenu dans ce `div`.
 */
.tuile,
.tuile .carte__corps {
    display: flex;
    flex-direction: column;
}

.tuile .carte__corps { align-items: center; }

.tuile__nombre {
    margin: 0;
    font-size: 26px;
    font-weight: 750;
    font-variant-numeric: tabular-nums;
    line-height: 1.05;
}

/*
 * ⚠️ **Le chiffre et son titre sont UNE seule information**, pas deux : c'est
 * « 4 demandes d'absence » qu'on lit, en un coup d'œil. Les espacer les
 * détache l'un de l'autre et oblige à recoller les deux moitiés.
 */
.tuile__titre {
    min-height: 2.4em;
    margin: 4px 0 0;
    font-size: 13px;
    line-height: 1.2;
}

/*
 * ⚠️ **SEUL LE TITRE GARDE SA RÉSERVE DE DEUX LIGNES**, et c'est la carte qu'on
 * corrige, pas la grille qu'on étire : dans une colonne de cette largeur,
 * « Maladie à traiter » tient sur une ligne là où « Prestations à valider » en
 * prend deux. Réserver la boîte du texte le plus long est le seul moyen de les
 * égaliser sans renommer des tuiles dont le nom est l'identité.
 *
 * ➡️ Si l'un des sept titres se raccourcit un jour au point que TOUS tiennent
 * sur une ligne, cette réserve devient inutile et doit sauter avec eux.
 */

/*
 * ⚠️ **PLUS DE HAUTEUR RÉSERVÉE ICI.** Les sept notes tiennent sur une ligne
 * depuis qu'elles sont bornées à 30 signes — un test le garantit —, donc elles
 * ont déjà la même hauteur. En réserver deux ajoutait une ligne vide à chacune
 * des sept.
 */
.tuile__note {
    margin: 4px 0 10px;
    font-size: 11.5px;
    line-height: 1.3;
}

/*
 * ⚠️ « RIEN À TRAITER » EST L'ÉTAT NORMAL, pas une réussite à célébrer.
 * Scriptcase teinte ses tuiles en vert quand la file est vide et en rouge
 * sinon ; on garde la distinction, qui se lit d'un coup d'œil, mais le rouge
 * dit « il y a du travail » — jamais « il y a un problème ». D'où une bordure
 * accentuée plutôt qu'un fond d'alerte.
 */
.tuile--travail { border-color: var(--brand); }
.tuile--travail .tuile__nombre { color: var(--brand); }

/* ------------------------------------------------------- écran de présence */

/*
 * ⚠️ TROIS ÉTATS, ET AUCUN DÉTAIL (§2). C'est l'absence de détail qui rend cet
 * écran ouvert à tout le personnel : un écran distinguant « maladie »
 * d'« absent » diffuserait une donnée de santé à tout le monde. Il n'y a donc
 * que trois couleurs ici, et il ne doit jamais y en avoir une quatrième — une
 * nuance de plus, c'est une donnée personnelle de plus.
 */
/*
 * ⚠️ LES TROIS ÉTATS SONT DES JETONS, pas des littéraux — pour que la palette
 * ait UN endroit où les retoucher, et un seul. Le socle existe précisément pour
 * qu'aucune couleur ne soit réécrite ailleurs.
 *
 * ⚠️ Ils gardent trois TEINTES DISTINCTES, et ce n'est pas décoratif : le §2
 * pose que confondre « hors murs » et « absent » ferait renoncer à joindre
 * quelqu'un qui travaille et répond. Une palette qui les rapprocherait casserait
 * la seule chose que cet écran sert à dire.
 */
:root {
    --presence-present: #10b981;
    --presence-hors-murs: #38a3c9;
    --presence-teletravail: #bfdbfe;
    --presence-absent: #414c58;
}

/*
 * ⚠️ **LA COULEUR NE PORTE JAMAIS L'INFORMATION SEULE.** Un daltonien ne
 * distingue pas le vert du bleu : chaque état porte donc une FORME qui le
 * différencie en niveaux de gris — pastille pleine et arrondie, pastille
 * pleine et carrée, contour pointillé. La couleur ne fait que confirmer.
 *
 * ⚠️ **La lettre, elle, porte la PÉRIODE** — M, AM, S. Deux informations par
 * pastille, aucune redondante : sans elle, il faudrait compter les positions
 * pour savoir de quelle demi-journée on parle.
 */
/*
 * ⚠️ **DES BLOCS PLEINS, PAS DES ÉTIQUETTES GRISES.** C'est ce qui fait la
 * lisibilité de la grille Scriptcase (`Scriptcase/RH/49.png`) : on lit une
 * semaine d'un balayage du regard, sans lire un seul mot. Une pastille discrète
 * oblige à déchiffrer case par case — sur vingt employés et sept jours, c'est
 * cent quarante déchiffrages.
 *
 * ⚠️ **LA COULEUR NE PORTE JAMAIS L'INFORMATION SEULE.** Un daltonien ne
 * distingue pas le vert du bleu : chaque bloc porte donc son **mot** en clair —
 * « Présent », « Hors murs », « Absent » — et le mot survit en niveaux de gris.
 * La couleur ne fait qu'accélérer une lecture qui reste possible sans elle.
 *
 * ⚠️ **UNE SEULE LIGNE : « MATIN Présent ».** Scriptcase les empile sur deux,
 * ce qui double la hauteur de chaque case ; à vingt employés il faut défiler
 * pour lire une semaine. Le `<b>` porte la période, le reste porte l'état.
 */
.creneau {
    display: block;
    margin: 0 0 3px;
    padding: 3px 7px;
    border: 1.5px solid transparent;
    border-radius: 5px;
    background: var(--presence-absent, #414c58);
    color: #fff;
    font-size: 11.5px;
    font-weight: 600;
    line-height: 1.35;
    white-space: nowrap;
}

.creneau:last-child { margin-bottom: 0; }

/*
 * ⚠️ **La période est ÉCRASÉE, pas mise en gras.** Elle sert de repère, pas
 * d'information principale : « MATIN » en petites capitales translucides se
 * saute à la lecture quand on cherche l'état, et se retrouve quand on cherche
 * la demi-journée.
 */
.creneau b {
    margin-right: 4px;
    font-size: 9.5px;
    font-weight: 800;
    letter-spacing: .4px;
    opacity: .72;
}

.creneau small { margin-left: 5px; font-size: 10.5px; opacity: .8; }

/*
 * ⚠️ UNE PÉRIODE À DEUX ÉTATS — demandé le 02/09/2026. Une récup de 9 h à
 * 11 h suivie d'un complément présent affichait « Présent » tout court : on
 * appelait à 10 h quelqu'un qui n'était pas là. La pastille garde sa forme, et
 * chaque segment porte SA couleur et SES heures. Le motif reste absent (§2).
 */
.creneau--mixte { background: var(--bg); color: var(--fg); border: 1px solid var(--line); padding-right: 4px; }
.creneau__segment {
    display: inline-block;
    margin-left: 4px;
    padding: 1px 6px;
    border-radius: 999px;
    color: #fff;
    font-weight: 600;
}
.creneau__segment--present { background: var(--presence-present, #10b981); }
/* ⚠️ Bleu PÂLE, texte sombre : le télétravail est pleinement joignable, il ne
   doit ressembler ni au vert du poste ni au bleu soutenu des missions et
   formations, où l'on l'est peu — arbitré le 02/09/2026. */
.creneau__segment--teletravail { background: var(--presence-teletravail, #bfdbfe); color: #0c2a4a; }
.creneau--teletravail { background: var(--presence-teletravail, #bfdbfe); color: #0c2a4a; }
.creneau__segment--hors_murs { background: var(--presence-hors-murs, #38a3c9); }
.creneau__segment--absent { background: var(--presence-absent, #414c58); }
.creneau__segment small { margin-left: 4px; font-size: 10.5px; opacity: .85; }

.creneau--present { background: var(--presence-present, #10b981); }
.creneau--hors_murs { background: var(--presence-hors-murs, #38a3c9); }

/*
 * ⚠️ « Absent » est SOURD, pas alarmant : il ne signale aucun problème et ne
 * porte aucun jugement — la personne peut être en congé, en formation, ou
 * n'avoir simplement rien à faire là ce jour-là. Le rouge en ferait une alerte.
 */
.creneau--absent { background: var(--presence-absent, #414c58); }

/*
 * ⚠️ **LE POINTILLÉ CHANGE CE QUE LA CASE AFFIRME**, il ne la décore pas. Plein
 * = constaté, aux prestations ; pointillé = prévu, encore au prévisionnel et
 * que la semaine peut démentir. Un « Absent » planifié et un « Absent » constaté
 * n'engagent pas la même chose, et la légende le dit — sans elle, les deux se
 * lisent comme un fait.
 *
 * ⚠️ **Ce n'est PAS la distinction de Scriptcase**, qui oppose l'horaire de base
 * au prévisionnel : il affiche la semaine type quand rien n'est encodé. Notre
 * service ne lit jamais l'horaire de base — une case inconnue reste vide et le
 * dit.
 */
.creneau--prevu {
    border-style: dashed;
    border-color: rgb(255 255 255 / 65%);
    background-image: linear-gradient(rgb(255 255 255 / 22%), rgb(255 255 255 / 22%));
}

/*
 * ⚠️ **L'objectif tenait en une phrase : huit personnes et sept jours sur un
 * portable, sans ascenseur.** C'est un écran de consultation rapide — on
 * l'ouvre pour savoir si un collègue est là. Il doit répondre d'un coup d'œil.
 */
.presence__legende {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 14px;
    margin: 0;
    font-size: 12.5px;
    color: var(--muted);
}

.presence__legende .creneau { display: inline-block; margin: 0 2px 0 0; }

.presence__reserve { margin: 8px 0 0; font-size: 12px; }

/*
 * ⚠️ **Le nom du jour ET sa date, sur deux lignes** — l'en-tête de Scriptcase.
 * Le nom seul ne dit pas laquelle des cinquante-deux semaines on regarde ; la
 * date seule oblige à compter pour savoir si le 03/09 est un jeudi.
 */
.presence thead th {
    text-align: center;
    line-height: 1.2;
    white-space: nowrap;
}

.presence thead th:first-child { text-align: left; }
.presence__jour { display: block; font-size: 12.5px; }
.presence__date { display: block; font-size: 11px; font-weight: 600; opacity: .72; }

/*
 * ⚠️ **Le jour courant se repère AVANT de se lire.** C'est la colonne qu'on
 * vient chercher neuf fois sur dix — « qui est là aujourd'hui ». Sans repère,
 * il faut d'abord retrouver la date, puis descendre.
 */
.presence thead th.est-aujourdhui {
    background: var(--brand);
    color: var(--brand-ink);
    border-radius: 6px 6px 0 0;
}

.presence td.est-aujourdhui { background: var(--brand-bg); }

/*
 * ⚠️ **Le week-end grisé n'est pas décoratif** : les centres culturels n'ont
 * pas de week-end structurel (§7), donc aucune case n'y est attendue — et
 * quand il y en a une, elle déclenche les taux 2 et 3 (§6). Le fond dit
 * « ici, c'est l'exception ».
 */
.presence thead th.est-weekend { background: var(--surface-strong); }
.presence td.est-weekend { background: var(--surface-muted); }

.presence th[scope="row"] { white-space: normal; min-width: 120px; font-size: 13px; }
.presence td { vertical-align: middle; }
.presence th, .presence td { padding-block: 5px; }

/*
 * ⚠️ **LE WEEK-END EST GRISÉ SUR TOUTES LES GRILLES DE SEMAINE**, et ce n'est
 * pas de la décoration : les centres culturels n'ont **pas de week-end
 * structurel** (§7) — l'horaire de base n'y porte jamais de case. Une ligne
 * samedi ou dimanche est donc toujours une exception, et elle coûte cher :
 * c'est elle qui déclenche le **taux 2** et le **taux 3** (§6), et le dimanche
 * sort du cumul des compensatoires.
 *
 * ➡️ Celui qui encode doit voir qu'il change de régime **avant** de saisir, pas
 * en relisant son solde le mois suivant.
 *
 * ⚠️ **Une seule règle, trois grilles** — encodage des prestations, du
 * prévisionnel, et les deux écrans de lecture seule. Elles portent toutes
 * `data-jour` : une teinte posée grille par grille divergerait à la première
 * qu'on oublie.
 */
[data-jour="Samedi"],
[data-jour="Dimanche"] { background: var(--surface-muted); }

[data-jour="Samedi"] > th[scope="row"],
[data-jour="Dimanche"] > th[scope="row"] { background: var(--surface-strong); }

/* -------------------------------------------------------------- onglets */

/*
 * La barre d'onglets de la fiche employé.
 *
 * ⚠️ **Ce sont des LIENS, pas des boutons.** Chaque onglet est une URL propre :
 * on l'ouvre dans un onglet du navigateur, on le met en favori, on y revient
 * par le bouton Précédent. Un basculement JavaScript aurait perdu les trois.
 *
 * ⚠️ **Elle défile horizontalement plutôt que de passer à la ligne.** Sept
 * onglets sur un téléphone tiendraient sur trois lignes et pousseraient le
 * contenu sous la ligne de flottaison ; le défilement garde la grille visible.
 */
/*
 * ⚠️ **ILS PASSENT À LA LIGNE, ILS NE SE COUPENT PAS.** `overflow-x: auto`
 * seul ne suffisait pas : une zone défilante à l'intérieur d'une page se
 * remarque mal, et sept onglets sortaient du cadre à droite sans que rien ne
 * l'indique. Le retour à la ligne garantit qu'aucun onglet n'est hors
 * d'atteinte, quelle que soit la largeur.
 *
 * ⚠️ Éprouvé aux deux largeurs qui comptent ici : 1280 px et 720p.
 */
.onglets {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 2px;
    margin: 0 0 var(--gap);
    padding: 0 0 2px;
    border-bottom: 1px solid var(--line);
}

.onglets__lien {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    flex: 0 1 auto;
    min-width: 0;
    padding: 9px 14px;
    border: 1px solid var(--line);
    border-bottom: 0;
    border-radius: 9px 9px 0 0;
    background: var(--surface-strong, #eef2f5);
    color: var(--ink-soft);
    text-decoration: none;
    font-size: 14px;
    font-weight: 600;
    white-space: nowrap;
}

.onglets__icone { width: 15px; height: 15px; flex: 0 0 auto; opacity: .7; }

/*
 * ⚠️ `var(--jeton, littéral)` PARTOUT OÙ UNE COULEUR RESTE EN DUR. Le token
 * n'existe pas encore sur cette branche ; le repli garde l'apparence actuelle.
 */
.onglets__lien:hover { color: var(--ink); background: var(--card); }

/*
 * ⚠️ **L'ONGLET ACTIF EST RATTACHÉ À SA PAGE**, il n'est pas seulement plus
 * foncé. Fond blanc, filet de marque au-dessus, et un pixel qui recouvre la
 * ligne du bas : la languette se raccorde visuellement au contenu, comme dans
 * la référence (`Scriptcase/RH/51.png`). Des onglets tous plats et gris se
 * lisent comme une liste de liens, pas comme une position.
 */
.onglets__lien.is-active {
    position: relative;
    bottom: -1px;
    border-color: var(--line);
    border-top: 3px solid var(--brand);
    padding-top: 7px;
    background: var(--card);
    color: var(--ink);
    font-weight: 800;
}

.onglets__lien.is-active .onglets__icone { opacity: 1; color: var(--brand); }

/* --- Postes d'animation (décision 043) -------------------------------------
   ⚠️ La couleur d'un rôle est DÉCORATIVE : la pastille accompagne le libellé,
   elle ne le remplace jamais. Rien ne se lit dans la couleur seule. */
.liste-roles { display: flex; flex-wrap: wrap; gap: 8px 18px; list-style: none; margin: 0; padding: 0; }
.liste-roles li { display: inline-flex; align-items: center; gap: 7px; }
.pastille-role {
    display: inline-block; width: 11px; height: 11px; border-radius: 50%;
    background: var(--couleur, #64748b); flex: 0 0 auto;
}
.occurrence { padding: 14px 0; border-top: 1px solid var(--line); }
.occurrence:first-of-type { border-top: 0; padding-top: 0; }
.occurrence h3 { margin: 0 0 10px; font-size: 15px; }
.occurrence h3 small { font-weight: 400; margin-left: 6px; }

/* ------------------------------------- remises du certificat (dossier maladie)
   ⚠️ **CINQ COLONNES, PAS DE BARRE DE DÉFILEMENT.** Le tableau des remises
   débordait de sa carte pour deux raisons cumulées : le nom de fichier d'un
   certificat (`1LC0875_20260731_Fiche de paie_0000003.pdf`) n'a presque pas de
   coupure naturelle, et le `<select>` des motifs de refus héritait du
   `width: 100%` commun aux champs — en cellule, un `select` ainsi dimensionné
   se calcule sur son option la plus longue et pousse la colonne d'autant.
   `table-layout: fixed` donne les largeurs, le contenu revient à la ligne
   dedans. Le motif complet reste lisible à l'ouverture de la liste : la
   cellule le borne, elle ne le tronque pas. */
.tableau--remises table { table-layout: fixed; }
.tableau--remises th { white-space: normal; }

.tableau--remises th:nth-child(1), .tableau--remises td:nth-child(1) { width: 15%; }
.tableau--remises th:nth-child(2), .tableau--remises td:nth-child(2) { width: 33%; }
.tableau--remises th:nth-child(3), .tableau--remises td:nth-child(3) { width: 16%; }
.tableau--remises th:nth-child(4), .tableau--remises td:nth-child(4) { width: 20%; }
.tableau--remises th:nth-child(5), .tableau--remises td:nth-child(5) { width: 16%; }

.remise__document { overflow-wrap: anywhere; }

.remise__refus { display: flex; flex-wrap: wrap; gap: 6px; }
.remise__refus select { width: 100%; min-width: 0; padding: 6px 8px; font-size: 13px; }
.remise__refus .bouton { width: 100%; padding: 6px 10px; font-size: 13px; }

/* ------------------------------------ sélection des catégories du mur (047)
   ⚠️ Un repli plutôt qu'une liste dépliée : la tuile du planning mural porte
   déjà deux boutons et un avertissement, et vingt cases à cocher la feraient
   dominer un écran qui compte sept tuiles. */
.repli-mur { margin-top: 12px; border-top: 1px solid var(--line); }
.repli-mur > summary { padding: 9px 0 6px; cursor: pointer; font-weight: 700; }
.repli-mur .liste-simple { max-height: 260px; overflow-y: auto; margin: 0 0 8px; }

/* « + Ajouter » à côté d'une liste — décision 053.
   ⚠️ Discret : c'est un raccourci, pas une action de l'écran. Le vrai formulaire
   reste dans Paramètres · Listes, et le bouton n'apparaît qu'à qui peut créer. */
.ajout-rapide { margin-top: 4px; }
.ajout-rapide__ouvrir { font-size: 12px; color: var(--muted); text-decoration: none; }
.ajout-rapide__ouvrir:hover { color: var(--marque); text-decoration: underline; }
.ajout-rapide__boite { display: grid; gap: 6px; margin-top: 6px; padding: 9px 10px;
  border: 1px solid var(--line-strong); border-radius: 8px; background: var(--surface-muted); }
.ajout-rapide__boite label { font-size: 12px; font-weight: 700; color: var(--muted); }
.ajout-rapide__message:empty { display: none; }

/* Choisir plusieurs dans une longue liste — décision 053.
   ⚠️ Le <select multiple> reste dans la page : c'est lui qui poste. Le script le
   masque et pose les pastilles ; sans script, il s'affiche et suffit. */
.choix-pastilles { display: grid; gap: 6px; }
.choix-pastilles__etiquette { font-size: 12px; font-weight: 700; color: var(--muted); }
.choix-pastilles__barre { display: flex; gap: 8px; align-items: center; }
.choix-pastilles__ajout { max-width: 22em; }
.choix-pastilles__liste { display: flex; flex-wrap: wrap; gap: 6px; margin: 0; padding: 0; list-style: none; }
.choix-pastilles__pastille { display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 6px 3px 10px; border: 1px solid var(--line-strong); border-radius: 999px;
  background: var(--card); font-size: 13px; }
.choix-pastilles__retirer { border: 0; background: transparent; cursor: pointer;
  font-size: 15px; line-height: 1; color: var(--muted); padding: 0 3px; }
.choix-pastilles__retirer:hover { color: var(--danger); }
.choix-pastilles__vide { font-size: 13px; }

/* Le numéro de semaine, toujours lisible — 07/09/2026.
   ⚠️ `type="week"` porte les numéros dans son calendrier sur Chrome et Edge ;
   Firefox et Safari retombent sur un champ texte. Celui-ci ne dépend de rien. */
/* --------------------------------------------------------------------------
   Proposer un point en réunion — décision 049

   ⚠️ Le bloc n'avait AUCUN style : il portait `card` et `section-head`, classes
   des pages matériel, absentes du socle. D'où six champs nus sur trois fiches.

   ⚠️ `<details>` plutôt qu'une modale : le repli est natif, donc il fonctionne
   script coupé — le bouton d'une `<dialog>` ne ferait rien sans JavaScript.
-------------------------------------------------------------------------- */

.proposer-point > summary {
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 14px;
    border: 1px solid var(--line);
    border-radius: var(--radius-btn, 8px);
    background: var(--bg, #f7f7f8);
    font-weight: 600;
    list-style: none;
}

/* La flèche par défaut de Safari, que `list-style: none` ne suffit pas à retirer. */
.proposer-point > summary::-webkit-details-marker { display: none; }

.proposer-point > summary::before {
    content: "＋";
    font-weight: 400;
    line-height: 1;
}

.proposer-point[open] > summary::before { content: "−"; }

.proposer-point > summary:hover,
.proposer-point > summary:focus-visible {
    border-color: var(--marque, #444);
}

.proposer-point[open] > summary { margin-bottom: 14px; }

.proposer-point__ligne {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    align-items: center;
}

.proposer-point__ligne + .proposer-point__ligne { margin-top: 10px; }

/* ⚠️ L'intitulé prend la place qui reste : c'est le champ qu'on remplit vraiment. */
.proposer-point__ligne input[name="libelle"] { flex: 1 1 260px; }
.proposer-point__ligne input[name="duree_minutes"] { width: 6em; }
.proposer-point__ligne select { flex: 0 1 auto; }

/* ------------------------------------------------------------------
   Un texte rédigé, converti depuis le Markdown (`x-texte-redige`).
   ⚠️ Ces règles vivaient dans le document de l'iframe du wiki, retirée le
   14/09/2026 : sans elles, une capture d'écran déborde de la carte et un
   tableau perd ses filets. Elles valent pour toute sortie du composant.
   ------------------------------------------------------------------ */
.texte-redige { overflow-wrap: anywhere; }
.texte-redige img, .texte-redige video { max-width: 100%; height: auto; }
.texte-redige table { max-width: 100%; border-collapse: collapse; }
.texte-redige td, .texte-redige th { padding: 6px 8px; border: 1px solid var(--line); }
.texte-redige pre { white-space: pre-wrap; }

/* ------------------------------------------------------------------
   Administration du personnel — treize colonnes de champs (capture 53, 14/09/2026).
   ⚠️ Les nombres tiennent en six caractères, les noms et les listes non : sans
   ces largeurs, chaque cellule recevait la même part et « Berckenbosch »
   devenait « Berckenb ». Le conteneur `.tableau` défile s'il manque de place.
   ------------------------------------------------------------------ */
.tableau--administration table { table-layout: auto; }
.tableau--administration .champ__saisie { min-width: 0; }
.tableau--administration input[name="nom"],
.tableau--administration input[name="prenom"] { min-width: 11em; }
.tableau--administration select[name="statut_id"] { min-width: 8.5em; }
.tableau--administration select[name="fonction_id"] { min-width: 12em; }
.tableau--administration select[name="regime_id"] { min-width: 8em; }
.tableau--administration td.num input[type="number"] { width: 6.2em; padding-inline: 7px; }
