/* =============================================================================
   apparence.css — apparence et ergonomie du calendrier
   Chantier « calendrier-plhair » · PROPRIÉTAIRE : lot D (CONTRAT §1)
   -----------------------------------------------------------------------------
   RÔLE
   Donner couleur, forme, typographie et états à ce que produisent les autres
   lots : la page publique et le layout du noyau (S), la vue du calendrier (V).
   Les écrans d'administration (A) sont habillés par la feuille sœur admin.css,
   pour que la page publique et l'administration se ressemblent.

   ORIGINE DE LA COULEUR ET DE LA FORME (consigne règle 1, CONTRAT §6)
   --cal-couleur et --cal-rayon sont les SEULES sources de la couleur et du
   rayon ; elles sont posées par /apparence.css du noyau, à partir des réglages
   enregistrés dans l'administration. Aucune couleur de marque n'est écrite en
   dur ici : les valeurs neutres déclarées au §1 sont justifiées une par une.
   Les teintes dérivées (fonds très clairs, encres foncées) sont fabriquées à
   partir de --cal-couleur avec color-mix(), donc elles suivent le réglage : il
   suffit de comparer les captures AVANT/APRÈS réglage pour le voir.

   ORDRE DE CHARGEMENT — ET UN ÉCART AVEC LA CONSIGNE, À CONNAÎTRE
   La consigne annonce que cette feuille est chargée APRÈS calendrier.css du
   lot V. Mesure faite sur le layout du noyau (resources/views/layouts/
   calendrier.blade.php) : la boucle @foreach des ressources de modules charge
   assets/apparence.css PUIS assets/calendrier.css. Cette feuille arrive donc
   AVANT celle de V. Conséquence assumée : les règles qui doivent gagner sont
   écrites avec une spécificité supérieure (#calendrier .cal-pastille et non
   .cal-pastille) plutôt que de compter sur l'ordre. Écart signalé dans
   docs/DEMANDES-LOT-D.md (le layout appartient au noyau, lot S).

   SÉPARATION APPAREIL / MISE EN PAGE
   V (mise en page du calendrier) et D (apparence) se partagent la même page.
   Règle suivie ici : les propriétés de MISE EN PAGE (display, position, taille,
   marges, gabarit de grille) ne sont jamais réécrites par cette feuille — elles
   appartiennent à V. Ne sont écrites ici que la couleur, la forme, la
   typographie, les ombres et les états.

   DÉGRADATION (consigne règle 4)
   Sans cette feuille, les pages restent utilisables : aucune règle ci-dessous ne
   masque un élément porteur de sens, et rien n'y dépend d'une autre feuille.

   SECTIONS AJOUTÉES PAR LE LOT D2 (§10 à §13) : roue d'administration
   (F24), fenêtre modale d'édition (F20, F21), états de focus clavier de
   ces deux composants, et leurs règles responsive aux deux largeurs
   obligatoires (≥ 1200 px et ≤ 420 px). Les sections 1 à 9 sont celles du
   lot D : elles n'ont pas été modifiées.

   Dépendances de bibliothèque : AUCUNE (aucun import, aucune police externe) —
   la typographie est la pile système, conformément à N6 (aucune compilation).
   ============================================================================= */

/* ==== 1. Jetons neutres =====================================================
   Les seules couleurs littérales de la feuille, toutes NEUTRES (gris, noir et
   blanc, sans identité de marque), justifiées une par une :
     - elles ne varient pas avec le réglage, car elles portent le texte, les
       séparations et les fonds — pas l'identité ;
     - les teintes qui DOIVENT suivre le réglage sont fabriquées avec color-mix()
       à partir de --cal-couleur (voir §2 et §5).
   ============================================================================= */
:root {
    --app-encre:         #221f2b; /* neutre : texte principal (gris ardoise très foncé, 16,2:1 sur blanc) */
    --app-encre-douce:   #5c5668; /* neutre : texte secondaire, en-têtes de jours, pied de page (7,0:1 sur blanc) */
    --app-bordure:       #ddd9e4; /* neutre : bordure des cases, des champs et des séparateurs */
    --app-bordure-forte: #c2bccb; /* neutre : bordure au survol, contour des commandes */
    --app-fond:          #ffffff; /* neutre : fond des surfaces (fiche agrandie, tableaux, champs) */
    --app-fond-doux:     #f7f6fa; /* neutre : fond en retrait (en-tête du tableau, week-ends, hors mois) */
    --app-ombre:         rgba(0, 0, 0, .16); /* neutre : ombre portée des surfaces flottantes */
    /* VOILE NEUTRE — POURQUOI IL EXISTE : la pastille porte du texte blanc sur
       --cal-couleur. Le nuancier de l'administration (PE-03) contient des
       couleurs claires (« Pierre » #8f8f8f, « Sauge » #4a8e6f) sur lesquelles le
       blanc ne tient que 3,2:1. Un voile noir à 20 % posé en dégradé au-dessus
       de la couleur ramène le pire cas du nuancier à 4,81:1, soit WCAG AA pour
       le texte (mesure : preuves/PREUVES-LOT-D.md §5). Le dégradé plutôt qu'une
       couleur opaque laisse --cal-couleur intact : getComputedStyle().backgroundColor
       de la pastille rend exactement la couleur réglée. */
    --app-voile:         rgba(0, 0, 0, .20);
    /* Pile système : aucune police téléchargée (aucune compilation, aucun CDN). */
    --app-typo: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}

/* ==== 2. Base et page publique =============================================
   Le chrome de la page publique (en-tête, contenu, pied) est produit par le
   layout du noyau avec les classes .cal-page / .cal-entete / .cal-contenu /
   .cal-pied. Ces classes ne sont réclamées par aucun module : elles reçoivent
   ici leur typographie et leurs séparations, dans la limite du sobre.
   ============================================================================= */
html {
    -webkit-text-size-adjust: 100%; /* le texte ne grossit pas tout seul sur téléphone */
}

body.cal-page {
    margin: 0;
    background: var(--app-fond-doux);
    color: var(--app-encre);
    font-family: var(--app-typo);
    font-size: 16px;
    line-height: 1.5;
}

/* Page « intégrée » (/integre, F18) : elle vit dans le cadre d'une page hôte,
   donc pas de fond opaque ni de largeur imposée — le calendrier s'adapte. */
body.cal-page--integree {
    background: transparent;
}

.cal-entete,
.cal-contenu,
.cal-pied {
    max-width: 1180px;      /* largeur de lecture confortable sur ordinateur */
    margin: 0 auto;
    padding-left: 16px;
    padding-right: 16px;
}

.cal-entete {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px 16px;
    padding-top: 18px;
    padding-bottom: 18px;
    border-bottom: 1px solid var(--app-bordure);
}

.cal-entete-titre {
    margin: 0;
    font-size: 1.25rem;
    font-weight: 600;
    letter-spacing: -.01em;
}

.cal-entete-titre a,
.cal-entete-lien,
.cal-pied-lien {
    color: var(--app-encre);
    text-decoration: none;
    border-bottom: 2px solid transparent;
}

.cal-entete-titre a:hover,
.cal-entete-lien:hover,
.cal-pied-lien:hover {
    border-bottom-color: var(--cal-couleur);
}

/* Lien d'administration : discret, il n'est pas l'action principale du visiteur. */
.cal-entete-lien {
    color: var(--app-encre-douce);
    font-size: .9375rem;
}

.cal-contenu {
    padding-top: 22px;
    padding-bottom: 34px;
}

/* Vue intégrée : aucun chrome, aucune largeur de lecture (F18). */
body.cal-page--integree .cal-contenu {
    max-width: none;
    padding: 10px;
}

.cal-pied {
    padding-top: 14px;
    padding-bottom: 22px;
    border-top: 1px solid var(--app-bordure);
    color: var(--app-encre-douce);
    font-size: .875rem;
}

.cal-pied-lien {
    color: var(--app-encre-douce);
}

/* Ancrage des couleurs de FullCalendar sur le réglage (voir aussi §4) : la vue
   du lot V s'appuie sur FullCalendar, dont le thème se règle par ces variables.
   Les poser ici évite toute couleur de marque en dur dans la vue de V. */
#calendrier {
    --fc-border-color: var(--app-bordure);
    --fc-today-bg-color: transparent;       /* l'aujourd'hui est traité au §4 */
    --fc-event-bg-color: var(--cal-couleur);
    --fc-event-border-color: var(--cal-couleur);
    --fc-event-text-color: #fff;            /* neutre : texte sur la pastille colorée */
    --fc-page-bg-color: var(--app-fond);
    --fc-neutral-bg-color: var(--app-fond-doux);
    --fc-small-font-size: .8125rem;
}

/* ==== 3. Sélecteur de vue (Mois / Année) ===================================
   Marqueurs figés .cal-vue-mois et .cal-vue-annee (CONTRAT §5). La vue active
   est reconnue sur le conteneur (#calendrier[data-vue="mois"|"annee"]) ET sur
   les états ARIA : ainsi le bouton actif garde son habillage quelle que soit la
   façon dont le lot V marque l'état, sans qu'un marqueur lui soit imposé.
   ============================================================================= */
.cal-vue-mois,
.cal-vue-annee {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .4em;
    min-height: 40px;                 /* cible tactile confortable au doigt */
    padding: 8px 18px;
    box-sizing: border-box;           /* min-height et padding ne se cumulent pas */
    /* Commandes DANS la pastille grise de .cal-selecteur (mise en page du lot V) :
       le fond reste transparent, c'est la commande active qui porte la couleur
       réglée. Une commande inactive se lit malgré tout (texte foncé sur gris clair),
       donc le sélecteur reste utilisable même sans cette feuille. */
    border: 1px solid transparent;
    border-radius: var(--cal-rayon);
    background: transparent;
    color: var(--app-encre-douce);
    font: inherit;
    font-weight: 600;
    line-height: 1.2;
    text-decoration: none;
    cursor: pointer;
}

.cal-vue-mois:hover,
.cal-vue-annee:hover {
    color: var(--app-encre);
    background: rgba(255, 255, 255, .55); /* neutre : éclaircit la pastille au survol */
}

#calendrier[data-vue="mois"] .cal-vue-mois,
#calendrier[data-vue="annee"] .cal-vue-annee,
.cal-vue-mois[aria-pressed="true"],
.cal-vue-annee[aria-pressed="true"] {
    background-color: var(--cal-couleur);
    background-image: linear-gradient(var(--app-voile), var(--app-voile));
    border-color: transparent;
    color: #fff;                      /* neutre : texte sur la couleur réglée (4,8:1 au pire) */
}

/* ==== 4. Grille du calendrier ==============================================
   La vue est celle du lot V. Le contrat ne fige aucun marqueur pour les cases :
   l'habillage vise donc les classes de FullCalendar (.fc-*), que V utilise pour
   poser la grille. Les règles ne touchent jamais au gabarit (colonnes, hauteurs),
   seulement aux couleurs, bordures et à la forme : la mise en page reste à V.
   ============================================================================= */
#calendrier .fc {
    font-family: var(--app-typo);
    color: var(--app-encre);
}

#calendrier .fc-theme-standard .fc-scrollgrid,
#calendrier .fc-theme-standard td,
#calendrier .fc-theme-standard th {
    border-color: var(--app-bordure);
}

#calendrier .fc-scrollgrid {
    border-radius: var(--cal-rayon);
    overflow: hidden;                 /* la grille épouse le rayon réglé */
}

/* En-têtes de jours : bandeau en retrait, libellés doux et lisibles. */
#calendrier .fc-col-header-cell {
    background: var(--app-fond-doux);
}

#calendrier .fc-col-header-cell-cushion {
    padding: 8px 4px;
    color: var(--app-encre-douce);
    font-size: .8125rem;
    font-weight: 600;
    letter-spacing: .02em;
    text-decoration: none;
    text-transform: capitalize;
}

#calendrier .fc-daygrid-day {
    background: var(--app-fond);
    /* FORME RÉGLÉE SUR LES CASES (F16) : le rayon des cases suit le réglage,
       comme celui des pastilles. À « angle » la valeur est 0 : les deux formes
       se distinguent donc à la mesure, pas seulement à l'œil. */
    border-radius: var(--cal-rayon);
}

#calendrier .fc-daygrid-day-number {
    color: var(--app-encre);
    font-size: .8125rem;
    font-weight: 500;
    text-decoration: none;
}

/* Cases du mois précédent ou suivant : présentes mais en retrait. */
#calendrier .fc-day-other {
    background: var(--app-fond-doux);
}

#calendrier .fc-day-other .fc-daygrid-day-number,
#calendrier .fc-day-other .fc-daygrid-day-top {
    color: var(--app-encre-douce);
    opacity: .6;
}

/* Aujourd'hui : la couleur réglée marque la case, sans crier. Le contraste du
   texte de la pastille tient grâce au même voile neutre qu'au §5. */
#calendrier .fc-day-today {
    background-color: color-mix(in srgb, var(--cal-couleur) 7%, var(--app-fond));
}

#calendrier .fc-day-today .fc-daygrid-day-number {
    display: inline-block;
    min-width: 1.9em;
    padding: 1px 6px;
    border-radius: var(--cal-rayon);
    background-color: var(--cal-couleur);
    background-image: linear-gradient(var(--app-voile), var(--app-voile));
    color: #fff;                      /* neutre : texte sur la couleur réglée */
    font-weight: 700;
    text-align: center;
}

/* Boutons de navigation de la grille (« ‹ », « › », « Aujourd'hui »). La couleur
   réglée est posée par la vue du lot V au moyen des variables de FullCalendar
   (--fc-button-bg-color) : ici on n'ajoute que le voile neutre, pour que le
   texte blanc tienne 4,5:1 quel que soit le réglage, et le rayon vient de V. */
#calendrier .fc-button-primary {
    background-image: linear-gradient(var(--app-voile), var(--app-voile));
}

/* Vue annuelle : chaque mois est une carte de la couleur réglée à ses bordures. */
#calendrier .fc-multimonth,
#calendrier .fc-multimonth-month {
    border-color: var(--app-bordure);
}

#calendrier .fc-multimonth-header,
#calendrier .fc-multimonth-title {
    background: var(--app-fond);
    color: var(--app-encre);
    font-weight: 600;
    font-size: .9375rem;
    letter-spacing: -.01em;
    text-transform: capitalize;
}

#calendrier .fc-multimonth .fc-daygrid-day.fc-day-today {
    background-color: color-mix(in srgb, var(--cal-couleur) 10%, var(--app-fond));
}

/* ==== 5. Pastilles d'évènement (.cal-pastille) =============================
   Marqueur figé (CONTRAT §5). backgroundColor rend EXACTEMENT la couleur réglée
   (c'est la mesure D1) ; le voile neutre posé en dégradé au-dessus garantit la
   lisibilité du texte blanc pour tout le nuancier.
   ============================================================================= */
#calendrier .cal-pastille,
.cal-pastille {
    background-color: var(--cal-couleur);
    background-image: linear-gradient(var(--app-voile), var(--app-voile));
    color: #fff;                      /* neutre : texte sur la couleur réglée */
    border-radius: var(--cal-rayon);
    box-shadow: 0 1px 2px var(--app-ombre);
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
}

#calendrier .cal-pastille:hover,
.cal-pastille:hover {
    box-shadow: 0 3px 10px var(--app-ombre);
}

/* La pastille tirée au clavier (déplacement, F8) se voit. */
#calendrier .cal-pastille:active {
    box-shadow: 0 1px 2px var(--app-ombre), inset 0 2px 6px rgba(0, 0, 0, .22);
}

/* ==== 6. Fiche agrandie (.cal-fiche et ses parties) ========================
   Marqueurs figés (CONTRAT §5). Le fond est opaque (elle se pose par-dessus la
   grille), l'ombre la détache, et l'image est bornée pour ne pas dévorer la
   fiche quand la photo est haute. L'apparition (animation/transition) reste au
   lot V : cette feuille n'y touche pas, pour ne pas mesurer à sa place (F6).
   ============================================================================= */
.cal-fiche,
#calendrier .cal-fiche {
    /* HAUTEUR DE L'IMAGE DE LA FICHE — LE POINT UNIQUE (LOT W4, 06/10/2026).
       Une SEULE valeur pour toute l'application : l'image la lit ci-dessous
       (« height »), et la réserve de place du module V la lit aussi
       (/assets/calendrier.css, règle [data-image-en-attente]). Deux valeurs
       écrites à deux endroits divergeraient tôt ou tard, et la fiche se
       remettrait alors à bouger sous le curseur — c'est exactement le défaut
       décrit par Norbert (« la fiche est là, puis elle saute »). Sur téléphone
       la valeur est réduite plus bas, dans le même bloc média. */
    --cal-fiche-image-hauteur: 190px;

    /* APPAREIL SEULEMENT : la position, la taille et les marges de la fiche
       appartiennent à la mise en page du lot V (elle est positionnée en fixed
       près de la pastille). Ici : fond opaque (la fiche se pose par-dessus la
       grille), ombre qui la détache, forme réglée, typographie lisible. */
    border: 1px solid var(--app-bordure);
    border-radius: var(--cal-rayon);
    background: var(--app-fond);
    box-shadow: 0 18px 40px -12px rgba(0, 0, 0, .38);
    color: var(--app-encre);
    font-family: var(--app-typo);
    font-size: .9375rem;
}

/* TYPOGRAPHIE INTERNE DE LA FICHE : elle appartient au lot V (tailles, marges,
   filet de gauche du titre). Cette feuille n'y touche pas — à une exception
   près : la COULEUR DU TEXTE. V pose `background: Canvas; color: CanvasText`
   (couleurs système) ; comme le fond de la fiche est forcé au blanc ici, un
   poste en thème sombre donnerait du texte clair sur fond blanc. La couleur est
   donc fixée sur la fiche elle-même (.cal-fiche ci-dessus) : ses enfants en
   héritent, sans qu'aucune règle de V ne soit réécrite. */

/* L'image de la fiche : elle ne doit jamais étirer la fiche ni déformer la
   photo. Le cadre et le rayon viennent de V ; ici on fixe sa HAUTEUR et on
   demande un recadrage centré (lisibilité de l'image, règle 2 de la consigne).

   POURQUOI UNE HAUTEUR FIXE, ET NON « max-height » (LOT W4, 06/10/2026) :
   mesuré sur le banc — avec « max-height », une image déclarée mais pas encore
   chargée occupe ZÉRO pixel ; sa hauteur définitive (190 px pour une photo en
   portrait, 143 px pour la place 3/2 réservée par le module V) n'est connue
   qu'à l'arrivée, et la fiche se replace alors de 46,5 px sous le curseur
   (production : 48,5 px, contrôle P7-C §3.1). Fixer la hauteur rend la boîte de
   l'image IDENTIQUE avant et après le chargement : la fiche ne bouge plus du
   tout. « object-fit: cover » recadre les images plus larges que la boîte,
   comme il le faisait déjà pour les images trop hautes. */
.cal-fiche-image,
#calendrier .cal-fiche-image {
    height: var(--cal-fiche-image-hauteur, 190px);
    object-fit: cover;
    background: var(--app-fond-doux); /* neutre : cadre d'attente avant chargement */
}

/* Le lien vers l'article : couleur réglée, assombrie pour tenir 4,5:1 sur fond
   blanc quel que soit le réglage (pire cas du nuancier : 4,8:1). */
.cal-fiche-lien,
#calendrier .cal-fiche-lien {
    color: var(--cal-couleur);
    color: color-mix(in srgb, var(--cal-couleur) 80%, black);
    text-decoration: underline;
    text-underline-offset: 2px;
}

/* La classe d'affichage n'est pas habillée ici : elle porte l'animation du lot V. */

/* ==== 7. Messages ==========================================================
   Messages d'état de la page publique (F9 par exemple : déplacement refusé).
   La vue du lot V nomme ses messages .cal-message et sa variante de refus
   .cal-message--refus ; elles reçoivent ici le filet à la couleur réglée et le
   fond teinté qui les distinguent du reste de la page.
   ============================================================================= */
.cal-message {
    margin: 0 0 14px;
    padding: 10px 14px;
    border: 1px solid var(--app-bordure);
    border-left: 4px solid var(--cal-couleur);
    border-radius: var(--cal-rayon);
    background: var(--app-fond);
    color: var(--app-encre);
    font-size: .9375rem;
}

.cal-message--erreur,
.cal-message--refus {
    border-left-color: var(--cal-couleur);
    background: color-mix(in srgb, var(--cal-couleur) 8%, var(--app-fond));
    font-weight: 600;
}

/* ==== 8. États : survol, actif, focus visible ==============================
   L'accessibilité n'est pas une option : un anneau de focus est posé partout où
   le clavier peut aller. Le contour est opaque et coloré (3 px) plutôt qu'un
   simple changement de teinte : il reste visible même sur la pastille colorée,
   et il ne dépend d'aucune transition.
   ============================================================================= */
.cal-page a:focus-visible,
.cal-page button:focus-visible,
.cal-page input:focus-visible,
.cal-page select:focus-visible,
.cal-page textarea:focus-visible,
.cal-page [tabindex]:focus-visible,
#calendrier .cal-pastille:focus-visible,
#calendrier .cal-vue-mois:focus-visible,
#calendrier .cal-vue-annee:focus-visible,
#calendrier .fc-button-primary:focus-visible {
    outline: 3px solid var(--cal-couleur);
    outline-offset: 2px;
}

/* Survol des cases : un fond très légèrement teinté, repère de lecture. */
#calendrier .fc-daygrid-day:hover {
    background-color: color-mix(in srgb, var(--cal-couleur) 5%, var(--app-fond));
}

/* ==== 9. Responsive ========================================================
   Deux largeurs obligatoires (consigne règle 3) : ordinateur ≥ 1200 px et
   téléphone ≤ 420 px. Les paliers sont posés en max-width/min-width, jamais en
   width fixe, pour que la page reste fluide entre les deux.
   ============================================================================= */

/* --- Téléphone (≤ 420 px) --- */
@media (max-width: 420px) {
    body.cal-page {
        font-size: 15px;
    }

    .cal-entete,
    .cal-contenu,
    .cal-pied {
        padding-left: 10px;
        padding-right: 10px;
    }

    .cal-entete {
        padding-top: 12px;
        padding-bottom: 12px;
    }

    .cal-contenu {
        padding-top: 14px;
        padding-bottom: 24px;
    }

    /* Commandes du sélecteur : elles remplissent la largeur, deux cibles larges
       valent mieux que deux petits boutons difficiles à viser au doigt. */
    .cal-vue-mois,
    .cal-vue-annee {
        flex: 1 1 0;
        min-height: 44px;
        padding: 8px 10px;
    }

    /* La fiche agrandie : sa largeur est bornée par le lot V (max-width: 92vw) ;
       ici seule la hauteur de l'image est réduite pour que la fiche reste courte.
       LOT W4 : la réduction passe par le POINT UNIQUE (la même variable que le
       module V lit pour réserver la place) — jamais par une seconde valeur. */
    .cal-fiche,
    #calendrier .cal-fiche {
        --cal-fiche-image-hauteur: 150px;
    }

    /* Grille du mois : les libellés de jours et les numéros rétrécissent plutôt
       que de forcer un défilement horizontal. */
    #calendrier .fc-col-header-cell-cushion,
    #calendrier .fc-daygrid-day-number {
        font-size: .75rem;
    }

    /* Rien ne doit dépasser en largeur : la mesure D4 vérifie scrollWidth <= innerWidth. */
    #calendrier {
        max-width: 100%;
    }

    /* VUE ANNUELLE : sur un téléphone, six mois de front deviennent illisibles.
       Les mois sont donc empilés en colonne (un mois par ligne, pleine largeur),
       ce qui supprime tout défilement horizontal et rend chaque mois lisible.
       Les règles visent la grille de FullCalendar utilisée par V. */
    #calendrier[data-vue="annee"] .fc-multimonth {
        display: block;
    }

    #calendrier[data-vue="annee"] .fc-multimonth-month {
        width: 100%;
        min-width: 0;
        margin: 0 0 12px;
    }

    #calendrier[data-vue="annee"] .fc-multimonth-month:last-child {
        margin-bottom: 0;
    }

    /* Repli, si la vue annuelle du lot V n'est pas bâtie sur FullCalendar :
       toute grille de mois empile ses enfants au lieu de les aligner. */
    #calendrier[data-vue="annee"] > * > .cal-mois,
    #calendrier[data-vue="annee"] > * > .cal-annee-mois {
        display: block;
        width: 100%;
        min-width: 0;
    }
}

/* --- Ordinateur (≥ 1200 px) --- */
@media (min-width: 1200px) {
    .cal-entete,
    .cal-contenu,
    .cal-pied {
        max-width: 1240px;
        padding-left: 24px;
        padding-right: 24px;
    }

    /* Plus d'air dans les cases : la grille mensuelle respire sur grand écran.
       La hauteur des cases et le gabarit de la grille restent au lot V et à
       FullCalendar (mise en page) : ici, seule la typographie est ajustée. */
    #calendrier .fc-daygrid-day-number,
    #calendrier .fc-col-header-cell-cushion {
        font-size: .875rem;
    }

    /* La fiche agrandie garde la largeur que lui donne le lot V (max 22rem). */
}

/* ==== 10. Roue d'administration (.roue-admin, F24) =========================
   Marqueur figé du CONTRAT §5, produit par le layout du noyau. C'est le SEUL
   point d'entrée vers l'administration : la consigne la veut donc DISCRÈTE au
   repos, franche au survol et nette au focus clavier.

   Deux états, décidés par la session (le noyau pose data-administration) :
     - session fermée : le bouton est un lien vers la connexion ;
     - session ouverte : c'est un <summary> qui ouvre un menu de service.
   Les deux portent la même classe .roue-admin-bouton : le dessin ci-dessous
   vaut donc pour l'un comme pour l'autre.
   ============================================================================= */
.roue-admin {
    position: fixed;                  /* hors du flux : elle ne pousse rien */
    right: 14px;
    bottom: 14px;
    z-index: 30;                      /* au-dessus de la page (1), SOUS la fiche
                                         agrandie du lot V (40) et sous la
                                         fenêtre modale (70) */
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    font-family: var(--app-typo);
    /* DISCRÉTION VOULUE (F24) : au repos la commande s'efface presque (45 %).
       Elle reprend toute son opacité au survol ET dès qu'un de ses éléments a
       le focus — sans quoi le contour de focus serait lui aussi atténué, ce qui
       est exactement le défaut d'accessibilité à éviter (règle 4). */
    opacity: .45;
    transition: opacity .15s ease;
}

.roue-admin:hover,
.roue-admin:focus-within {
    opacity: 1;
}

.roue-admin-bouton {
    box-sizing: border-box;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;                      /* petite cible : voir règle 3 (discretion) */
    height: 40px;
    padding: 0;
    border: 1px solid var(--app-bordure-forte);
    border-radius: var(--cal-rayon);
    background: var(--app-fond);
    color: var(--app-encre-douce);
    box-shadow: 0 2px 8px -2px var(--app-ombre); /* neutre : détache la commande du calendrier */
    cursor: pointer;
    text-decoration: none;
    list-style: none;                 /* retire le triangle natif du <summary> */
}

/* Le marqueur natif de <summary> ne s'enlève pas partout par list-style seul. */
.roue-admin-bouton::-webkit-details-marker {
    display: none;
}

.roue-admin-bouton::marker {
    content: "";
}

.roue-admin-bouton:hover {
    border-color: var(--cal-couleur);
    color: var(--app-encre);
}

.roue-admin-icone {
    display: block;                   /* l'icône suit currentColor : elle prend
                                         donc la couleur du bouton dans chaque état */
}

/* Menu de service : il s'ouvre AU-DESSUS de la roue. Elle est collée au bas de
   la fenêtre ; un menu ouvert vers le bas sortirait de l'écran. */
.roue-admin-menu {
    position: relative;
}

.roue-admin-menu[open] .roue-admin-bouton {
    border-color: var(--cal-couleur);
    color: var(--app-encre);
}

.roue-admin-liste {
    position: absolute;
    right: 0;
    bottom: calc(100% + 8px);
    z-index: 31;
    min-width: 11rem;
    margin: 0;
    padding: 6px;
    list-style: none;
    border: 1px solid var(--app-bordure);
    border-radius: var(--cal-rayon);
    background: var(--app-fond);
    box-shadow: 0 12px 28px -12px rgba(0, 0, 0, .45); /* neutre : ombre de la surface flottante */
}

/* Repli : si un navigateur laissait un enfant de <details> fermé visible une
   fois son display posé par cette feuille, on le cache explicitement. */
.roue-admin-menu:not([open]) .roue-admin-liste {
    display: none;
}

.roue-admin-liste li + li {
    margin-top: 2px;
}

.roue-admin-lien {
    box-sizing: border-box;
    display: block;
    width: 100%;
    padding: 8px 10px;
    border: 0;
    border-radius: var(--cal-rayon);
    background: transparent;
    color: var(--app-encre);
    font: inherit;
    font-size: .9375rem;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}

.roue-admin-lien:hover {
    background: color-mix(in srgb, var(--cal-couleur) 8%, var(--app-fond));
}

.roue-admin-formulaire {
    margin: 0;                        /* « Déconnexion » est un bouton, pas un bloc */
}

/* ==== 11. Fenêtre modale d'édition (#modale-evenement, F20 et F21) =========
   Marqueurs figés du CONTRAT §5, produits par le lot A2 et présents dans le
   document SEULEMENT quand la session d'administration est ouverte (F23).

   CE QUI FAIT QU'UNE MODALE EST UTILISABLE (règle 2 de la consigne) :
     - un fond assombri qui couvre toute la page : aucune ambiguïté sur ce qui
       est actif, et le clic dessus ferme la fenêtre ;
     - une boîte centrée, à largeur confortable sur ordinateur, pleine largeur
       utilisable sur téléphone ;
     - des champs pleine largeur, lisibles ;
     - un bouton « Supprimer » visiblement différent de « Enregistrer ».
   ============================================================================= */
.modale {
    position: fixed;
    inset: 0;                         /* couvre la fenêtre entière */
    z-index: 70;                      /* au-dessus de la roue (30) et de la fiche (40) */
    display: flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    padding: 18px;
}

/* PIÈGE MESURÉ, À NE PAS OUBLIER : le script du module A ouvre et ferme la
   fenêtre par l'attribut `hidden` (modale.hidden = true/false). Or une règle
   d'auteur — ici « display: flex » ci-dessus — l'emporte TOUJOURS sur la règle
   du navigateur qui masque [hidden]. Sans les quatre règles qui suivent, la
   fenêtre resterait affichée en permanence, en clair comme en modification.
   Elles sont plus spécifiques (classe + attribut) : aucun !important n'est
   nécessaire. */
.modale[hidden],
.modale-message[hidden],
.modale-confirmation[hidden],
.modale-supprimer[hidden] {
    display: none;
}

.modale-fond {
    position: absolute;
    inset: 0;
    /* VOILE ASSOMBRI — valeur neutre, justifiée : c'est du noir translucide, il
       n'exprime aucune identité et n'entre donc pas en concurrence avec
       --cal-couleur. 55 % suffisent à rendre la page derrière manifestement
       inactive tout en la laissant deviner (on sait d'où l'on vient). */
    background: rgba(0, 0, 0, .55);
}

.modale-boite {
    position: relative;               /* passe AU-DESSUS du fond assombri */
    box-sizing: border-box;           /* max-width et marges intérieures ne se cumulent pas */
    width: 100%;
    max-width: 560px;
    max-height: calc(100vh - 36px);
    max-height: calc(100dvh - 36px);
    overflow-y: auto;                 /* formulaire long : il défile DANS la boîte */
    padding: 22px 24px 20px;
    border: 1px solid var(--app-bordure);
    border-top: 4px solid var(--cal-couleur);  /* rappel de la couleur réglée */
    border-radius: var(--cal-rayon);
    background: var(--app-fond);      /* opaque : la page ne se lit plus à travers */
    color: var(--app-encre);
    font-family: var(--app-typo);
    font-size: 1rem;
    box-shadow: 0 24px 60px -18px rgba(0, 0, 0, .55); /* neutre : ombre de la surface flottante */
}

/* Empêche la page de défiler derrière la fenêtre ouverte. Amélioration : un
   navigateur qui ne connaît pas :has() ignore simplement la règle. */
body.cal-page:has(#modale-evenement:not([hidden])) {
    overflow: hidden;
}

.modale-titre {
    margin: 0 0 14px;
    font-size: 1.25rem;
    font-weight: 600;
    letter-spacing: -.01em;
}

/* Messages de la fenêtre (succès ET erreur — F21). Mêmes jetons que la page
   publique (.cal-message) et que l'administration (.adm-message) : le module A
   bascule les classes --erreur / --succes lui-même. */
.modale-message {
    margin: 0 0 14px;
    padding: 10px 14px;
    border: 1px solid var(--app-bordure);
    border-left: 4px solid var(--cal-couleur);
    border-radius: var(--cal-rayon);
    background: var(--app-fond);
    font-size: .9375rem;
}

.modale-message--erreur {
    background: color-mix(in srgb, var(--cal-couleur) 9%, var(--app-fond));
    font-weight: 600;
}

.modale-message--succes {
    background: color-mix(in srgb, var(--cal-couleur) 6%, var(--app-fond));
}

.modale-formulaire {
    margin: 0;
}

.modale-etiquette {
    display: block;
    margin-top: 14px;
    color: var(--app-encre);
    font-size: .875rem;
    font-weight: 600;
}

/* La mention « (obligatoire) » informe sans crier : elle est dans le libellé,
   pas dans un astérisque coloré. */
.modale-obligatoire {
    font-weight: 400;
    color: var(--app-encre-douce);
}

input.modale-champ,
select.modale-champ,
textarea.modale-champ {
    box-sizing: border-box;
    display: block;
    width: 100%;                      /* champ pleine largeur : lisible au téléphone */
    min-height: 42px;                 /* hauteur de saisie confortable */
    margin-top: 5px;
    padding: 9px 12px;
    border: 1px solid var(--app-bordure-forte);
    border-radius: var(--cal-rayon);
    background: var(--app-fond);
    color: var(--app-encre);
    font: inherit;
    font-size: .9375rem;
}

textarea.modale-champ {
    min-height: 98px;
    line-height: 1.45;
    resize: vertical;
}

input.modale-champ[type="file"] {
    padding: 6px 8px;                 /* le bouton natif du sélecteur de fichier respire */
}

/* La case « Publié » porte AUSSI .modale-champ (contrat §5) mais n'est pas une
   surface de saisie : elle reprend ses dimensions natives. Écrite APRÈS la
   règle ci-dessus et à spécificité égale (élément + classe), elle l'emporte. */
input.modale-champ--case {
    width: auto;
    min-height: 0;
    margin: 0;
    padding: 0;
}

.modale-case {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 16px;
    font-size: .9375rem;
    cursor: pointer;
}

input.modale-champ:hover,
select.modale-champ:hover,
textarea.modale-champ:hover {
    border-color: var(--cal-couleur);
}

/* ==== 11 bis. Commandes de la fenêtre =====================================
   Trois commandes, trois rôles : ENREGISTRER (l'action courante, à la couleur
   réglée), SUPPRIMER (l'action destructive) et FERMER (le retrait, sans effet).

   COMMENT « SUPPRIMER » SE DISTINGUE SANS SECONDE COULEUR D'IDENTITÉ : la
   règle 1 de la consigne n'autorise que --cal-couleur, et un rouge destructive
   écrit en dur serait une couleur de marque. La distinction est donc portée par
   la VALEUR et le POIDS : aplat neutre très foncé (l'encre du texte) contre
   aplat coloré pour Enregistrer. Les deux ne peuvent pas être confondus (mesuré
   en D2-3 : couleurs de fond calculées différentes), et la vraie protection
   reste la confirmation en deux temps du module A.
   ============================================================================= */
.modale-enregistrer,
.modale-supprimer,
.modale-fermer {
    box-sizing: border-box;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 42px;
    padding: 9px 18px;
    border: 1px solid var(--app-bordure-forte);
    border-radius: var(--cal-rayon);
    background: var(--app-fond);
    color: var(--app-encre);
    font: inherit;
    font-size: .9375rem;
    font-weight: 600;
    line-height: 1.2;
    cursor: pointer;
}

.modale-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin-top: 20px;
}

/* L'action courante prend la place disponible, les deux autres restent à droite. */
.modale-enregistrer {
    flex: 1 1 12rem;
    background-color: var(--cal-couleur);
    background-image: linear-gradient(var(--app-voile), var(--app-voile));
    border-color: transparent;
    color: #fff;                      /* neutre : texte sur la couleur réglée (4,8:1 au pire du nuancier) */
}

.modale-supprimer {
    background: var(--app-encre);
    border-color: var(--app-encre);
    color: #fff;
}

.modale-fermer {
    background: var(--app-fond);
    color: var(--app-encre-douce);
}

.modale-enregistrer:hover {
    box-shadow: 0 3px 12px var(--app-ombre);
}

.modale-supprimer:hover {
    box-shadow: 0 3px 12px var(--app-ombre);
}

.modale-fermer:hover {
    border-color: var(--app-encre);
    color: var(--app-encre);
}

.modale-enregistrer:active,
.modale-supprimer:active,
.modale-fermer:active {
    box-shadow: inset 0 2px 5px rgba(0, 0, 0, .20); /* neutre : enfoncement de la commande */
}

/* Bloc de confirmation de suppression (F21) : il apparaît dans la fenêtre, il
   ne remplace pas la page. Filet neutre foncé, comme le bouton « Supprimer »
   qu'il double — aucune seconde couleur d'identité n'est introduite. */
.modale-confirmation {
    margin-top: 18px;
    padding: 12px 14px 14px;
    border: 1px solid var(--app-bordure);
    border-left: 4px solid var(--app-encre);
    border-radius: var(--cal-rayon);
    background: var(--app-fond-doux);
}

.modale-confirmation-texte {
    margin: 0;
    font-size: .9375rem;
    font-weight: 600;
}

.modale-confirmation .modale-actions {
    margin-top: 12px;
}

/* ==== 12. Focus clavier de la roue et de la fenêtre ========================
   Règle 4 de la consigne : un focus qui disparaît est un défaut. Le contour
   est opaque, large de 3 px et à la couleur réglée — visible sur le fond clair
   comme sur un aplat coloré. Le <summary> de la roue n'est ni un <a> ni un
   <button> : il faut donc le nommer ici, sinon il garde le contour du
   navigateur (mesuré : 1 px « auto »).
   ============================================================================= */
/* Au focus clavier, la roue est aussi NETTE qu'au survol (règle 3 de la
   consigne) : le contour seul suffirait à la repérer, mais l'icône doit être
   franche — mesuré : sans cette règle, l'icône restait en gris doux au focus
   alors qu'elle passe à l'encre au survol. */
.roue-admin-bouton:focus-visible {
    border-color: var(--cal-couleur);
    color: var(--app-encre);
}

/* Idem pour les entrées du menu : la ligne active se voit, pas seulement son
   contour. */
.roue-admin-lien:focus-visible {
    background: color-mix(in srgb, var(--cal-couleur) 10%, var(--app-fond));
}

.roue-admin-bouton:focus-visible,
.roue-admin-lien:focus-visible,
.modale-etiquette:focus-visible,
input.modale-champ:focus-visible,
select.modale-champ:focus-visible,
textarea.modale-champ:focus-visible,
.modale-case:focus-visible,
.modale-enregistrer:focus-visible,
.modale-supprimer:focus-visible,
.modale-fermer:focus-visible {
    outline: 3px solid var(--cal-couleur);
    outline-offset: 2px;
}

.modale-case:focus-within {
    color: var(--app-encre);
}

/* ==== 13. Responsive de la roue et de la fenêtre ===========================
   Deux largeurs obligatoires (règle 5) : ordinateur ≥ 1200 px et téléphone
   ≤ 420 px. Paliers en max-width/min-width, jamais en largeur fixe.
   ============================================================================= */

/* --- Téléphone (≤ 420 px) --- */
@media (max-width: 420px) {
    /* La fenêtre occupe l'écran : c'est la seule façon d'avoir des champs
       pleine largeur utilisables et un clavier tactile qui ne recouvre pas les
       commandes. Le fond assombri reste, mais la boîte le couvre. */
    .modale {
        padding: 0;
        align-items: flex-start;
    }

    .modale-boite {
        width: 100%;
        max-width: none;
        max-height: 100vh;
        max-height: 100dvh;
        padding: 16px 14px 20px;
        border-left: 0;
        border-right: 0;
        border-bottom: 0;
        border-radius: 0;             /* la boîte EST l'écran : aucun coin à arrondir */
    }

    /* Les commandes s'empilent en pleine largeur : viser au doigt, sans rater. */
    .modale-actions > .modale-enregistrer,
    .modale-actions > .modale-supprimer,
    .modale-actions > .modale-fermer {
        flex: 1 1 100%;
    }

    /* La roue reste discrète mais devient une cible tactile franche. */
    .roue-admin {
        right: 10px;
        bottom: 10px;
    }

    .roue-admin-bouton {
        width: 44px;
        height: 44px;
    }
}

/* --- Ordinateur (≥ 1200 px) --- */
@media (min-width: 1200px) {
    .roue-admin {
        right: 22px;
        bottom: 22px;
    }

    /* Largeur confortable : le formulaire à sept champs se lit d'un seul tenant. */
    .modale-boite {
        max-width: 600px;
        padding: 26px 28px 22px;
    }
}
