/* =============================================================================
 * Hello Cécile — feuille de style unique
 *
 * Parti pris et justification des valeurs : docs/identite-visuelle.md
 * Ne pas introduire de couleur, de taille de texte ou d'espacement en dur :
 * tout passe par les variables de la section 1. Si une valeur manque, on
 * l'ajoute au système plutôt qu'on l'écrit sur place.
 *
 * Sommaire
 *   1. Variables
 *   2. Réinitialisation et base
 *   3. Typographie
 *   4. Motif « accent »
 *   5. Liens
 *   6. Boutons
 *   7. Cartes
 *   8. Badges
 *   9. Tableaux
 *  10. Fiches (listes de définition)
 *  11. Listes
 *  12. Formulaires
 *  13. Messages (alertes, flash)
 *  14. Illustrations
 *  15. Vitrine publique
 *  16. Écrans connectés (back-office et espace famille)
 *  17. Préférences utilisateur et impression
 *  18. Marqueurs de relecture éditoriale (temporaire)
 * ========================================================================== */

/* ---------------------------------------------------------------------------
 * 1. Variables
 * ------------------------------------------------------------------------ */

:root {
    /* --- Couleurs : 6 teintes de base ---------------------------------- */

    /* Fond général. Papier chaud, jamais de blanc pur en pleine page. */
    --c-papier: #fbf8f3;
    /* Surfaces posées sur le papier : cartes, champs, corps de tableau. */
    --c-surface: #ffffff;
    /* Texte principal. Noir chaud, pas #000. 15,3:1 sur papier. */
    --c-encre: #23201d;
    /* Texte secondaire : méta, légendes, libellés. 6,9:1 sur papier. */
    --c-encre-doux: #5c554e;
    /* Accent de marque : « Hello », liens, action principale, offre ANGLAIS.
       5,6:1 sur papier, 5,9:1 en blanc sur aplat. */
    --c-brique: #a8452c;
    /* Second accent : offre SOUTIEN SCOLAIRE et état de succès.
       5,8:1 sur papier, 6,1:1 en blanc sur aplat. */
    --c-sauge: #3c6b55;

    /* --- Dérivées : jamais utilisées seules, toujours en appui ---------- */

    /* Filets décoratifs, séparateurs, bordures de cartes. */
    --c-trait: #e5ded4;
    /* Bordures de composants interactifs (champs). 3,2:1 sur papier — WCAG 1.4.11. */
    --c-trait-fort: #968a78;
    /* Textes sur voile brique / sauge. 7,6:1 et 8,2:1 sur papier. */
    --c-brique-fonce: #8a3520;
    --c-sauge-fonce: #2c5744;
    /* Sur l'aplat encre du pied de page, la brique tombe à 2,7:1 — sous le seuil
       de 3:1 exigé d'un indicateur non textuel (WCAG 1.4.11), donc sous celui de
       l'anneau de focus. Ces deux teintes sont sa seule dérogation ; elles ne
       s'emploient nulle part ailleurs. 7,6:1 et 9,7:1 sur --c-encre. */
    --c-brique-clair: #e9a08a;
    --c-papier-doux: #cfc7ba;
    /* Voiles : fonds de message, survols, lignes alternées. */
    --c-voile-brique: #fbede8;
    --c-voile-sauge: #edf3ef;
    --c-voile-papier: #f4efe6;

    /* --- Polices ------------------------------------------------------- */

    /* Titres : serif système. New York (Apple), Charter, Georgia (Windows),
       Noto Serif (Android/Linux). Aucune requête réseau, aucun FOUT. */
    --police-titre: ui-serif, 'New York', Charter, 'Bitstream Charter',
        Georgia, 'Noto Serif', 'Liberation Serif', serif;
    /* Texte courant : sans-serif système, humaniste sur toutes les plateformes. */
    --police-texte: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
        'Helvetica Neue', 'Noto Sans', Arial, sans-serif;
    /* Chiffres alignés (montants, tableaux) et code. */
    --police-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas,
        'Liberation Mono', monospace;

    /* --- Échelle typographique ------------------------------------------
       Ratio ~1,2. Le corps de texte est à 17 px : le public lit sur
       téléphone, souvent dehors, parfois sans lunettes. */
    --pas-xs: 0.8125rem;   /* 13 px — mentions, badges */
    --pas-s: 0.9375rem;    /* 15 px — méta, légendes, tableaux denses */
    --pas-m: 1.0625rem;    /* 17 px — texte courant */
    --pas-l: 1.1875rem;    /* 19 px — chapô */
    --pas-xl: 1.4375rem;   /* 23 px — h3, titre de carte */
    --pas-2xl: 1.75rem;    /* 28 px — h2 */
    /* Planchers abaissés en juillet 2026 (identite-visuelle.md, § 3) : à 390 px
       le h1 de l'accueil montait à 41 px sur cinq lignes, soit 226 px de haut.
       À 34 px il reste un titre d'affiche, et les 50 à 65 px récupérés vont aux
       repères. Les paliers hauts ne bougent pas : rien n'était cassé au-delà. */
    --pas-3xl: clamp(1.75rem, 1.15rem + 2.6vw, 2.875rem);   /* h1 */
    --pas-4xl: clamp(2.125rem, 1.2rem + 4.2vw, 4rem);       /* display accueil */

    --interligne-serre: 1.1;
    --interligne-titre: 1.22;
    --interligne-texte: 1.65;

    --graisse-normale: 400;
    --graisse-moyenne: 500;
    --graisse-forte: 600;

    /* Une seule valeur d'interlettrage négatif, pour les titres. */
    --interlettre-titre: -0.015em;
    /* Une seule valeur positive, pour les surtitres en capitales. */
    --interlettre-surtitre: 0.11em;

    /* --- Espacements : base 4 px ---------------------------------------- */
    --espace-1: 0.25rem;
    --espace-2: 0.5rem;
    --espace-3: 0.75rem;
    --espace-4: 1rem;
    --espace-5: 1.5rem;
    --espace-6: 2rem;
    --espace-7: 3rem;
    --espace-8: 4rem;
    --espace-9: 6rem;

    /* --- Formes --------------------------------------------------------- */
    --rayon-s: 4px;
    --rayon-m: 8px;
    --rayon-l: 14px;
    --rayon-plein: 999px;

    /* Ombres teintées chaud, très basses : on suggère le papier posé,
       pas le calque flottant. */
    --ombre-1: 0 1px 2px rgba(35, 32, 29, 0.05), 0 1px 1px rgba(35, 32, 29, 0.04);
    --ombre-2: 0 2px 4px rgba(35, 32, 29, 0.05), 0 10px 24px -10px rgba(35, 32, 29, 0.13);

    /* --- Gabarit -------------------------------------------------------- */
    --largeur-page: 68rem;
    --largeur-texte: 36rem;
    --largeur-etroite: 26rem;
    --gouttiere: var(--espace-5);

    /* --- Interaction ---------------------------------------------------- */
    --transition: 140ms ease-out;
    --anneau-focus: 0 0 0 3px rgba(168, 69, 44, 0.32);
}

/* ---------------------------------------------------------------------------
 * 2. Réinitialisation et base
 * ------------------------------------------------------------------------ */

*,
*::before,
*::after {
    box-sizing: border-box;
}

html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
}

body {
    margin: 0;
    font-family: var(--police-texte);
    font-size: var(--pas-m);
    font-weight: var(--graisse-normale);
    line-height: var(--interligne-texte);
    color: var(--c-encre);
    background: var(--c-papier);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

img,
svg {
    max-width: 100%;
}

/* Un seul style de focus dans tout le site : anneau brique, toujours visible,
   jamais supprimé. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
    outline: 2px solid var(--c-brique);
    outline-offset: 2px;
    border-radius: var(--rayon-s);
}

::selection {
    background: var(--c-voile-brique);
    color: var(--c-brique-fonce);
}

/* Lien d'évitement — premier élément focusable de chaque page. */
.evitement {
    position: absolute;
    left: var(--espace-3);
    top: var(--espace-3);
    z-index: 10;
    padding: var(--espace-2) var(--espace-4);
    background: var(--c-encre);
    color: var(--c-papier);
    border-radius: var(--rayon-s);
    text-decoration: none;
    transform: translateY(-250%);
    transition: transform var(--transition);
}

.evitement:focus-visible {
    transform: translateY(0);
    outline-color: var(--c-papier);
}

/* ---------------------------------------------------------------------------
 * 3. Typographie
 * ------------------------------------------------------------------------ */

h1,
h2,
h3,
h4 {
    font-family: var(--police-titre);
    font-weight: var(--graisse-forte);
    line-height: var(--interligne-titre);
    letter-spacing: var(--interlettre-titre);
    color: var(--c-encre);
    margin: 0;
    text-wrap: balance;
}

h1 { font-size: var(--pas-3xl); }
h2 { font-size: var(--pas-2xl); }
h3 { font-size: var(--pas-xl); }
h4 { font-size: var(--pas-l); }

p,
ul,
ol,
dl {
    margin: 0 0 var(--espace-4);
}

p:last-child,
ul:last-child,
dl:last-child {
    margin-bottom: 0;
}

strong,
b {
    font-weight: var(--graisse-forte);
}

/* Surtitre : capitales espacées, précédées de l'accent. Annonce une section
   sans consommer un niveau de titre. */
/* `display: block` et non `flex` (correction de juillet 2026, § 11.5) : en
   conteneur flex, un surtitre qui passe sur deux lignes voyait sa puce se
   détacher à l'extrême gauche pendant que le texte restait centré. En bloc, la
   puce redevient un caractère parmi les autres — elle suit l'alignement du
   texte et ne se détache plus. */
.surtitre {
    display: block;
    font-size: var(--pas-xs);
    font-weight: var(--graisse-forte);
    text-transform: uppercase;
    letter-spacing: var(--interlettre-surtitre);
    color: var(--c-brique);
    margin: 0 0 var(--espace-3);
    text-wrap: balance;
}

.surtitre::before {
    content: '';
    display: inline-block;
    width: 0.8rem;
    height: 2.5px;
    margin-right: var(--espace-2);
    vertical-align: 0.25em;
    background: currentColor;
    border-radius: var(--rayon-plein);
    transform: rotate(-35deg);
}

.surtitre--sauge {
    color: var(--c-sauge);
}

.surtitre--neutre {
    color: var(--c-encre-doux);
}

/* Chapô : le paragraphe d'accroche sous un h1. */
.chapo {
    font-size: var(--pas-l);
    color: var(--c-encre-doux);
    max-width: var(--largeur-texte);
    text-wrap: pretty;
}

/* Texte de service : notes, précisions, mentions. */
.note {
    font-size: var(--pas-s);
    color: var(--c-encre-doux);
}

code {
    font-family: var(--police-mono);
    font-size: 0.9em;
    background: var(--c-voile-papier);
    padding: 0.1em 0.35em;
    border-radius: var(--rayon-s);
}

/* Fil d'Ariane. */
.fil {
    font-size: var(--pas-s);
    color: var(--c-encre-doux);
    margin: 0 0 var(--espace-4);
}

.fil a {
    color: var(--c-encre-doux);
}

.fil a:hover {
    color: var(--c-brique);
}

/* Bloc de texte long : largeur limitée pour rester lisible. */
.prose {
    max-width: var(--largeur-texte);
    text-wrap: pretty;
}

/* Titre intermédiaire dans le flux d'un texte long. Le système n'espaçait que
   les titres de section (`.section-entete`, `.page h2`), qui portent leur
   marge sur le conteneur ; un titre posé au milieu d'un `.prose` arriverait
   collé au paragraphe précédent — les pages vitrine en ont besoin dès qu'une
   section a des sous-parties (« Où ont lieu les cours ? », la FAQ). */
.prose h2,
.prose h3,
.prose h4 {
    margin-top: var(--espace-6);
    margin-bottom: var(--espace-3);
}

.prose > :first-child {
    margin-top: 0;
}

/* ---------------------------------------------------------------------------
 * 4. Motif « accent »
 *
 * L'unique signe graphique de la marque : le trait oblique de l'accent aigu
 * de « Cécile ». On le retrouve en puce de liste, en surtitre, en marqueur de
 * rubrique active, en favicon. Il ne sert jamais de décoration gratuite.
 * ------------------------------------------------------------------------ */

/* Angle unique du motif : -35°, celui des accents tracés dans les SVG
   (templates/partials/illustrations.html.twig). Ne pas en introduire d'autre. */
.accent {
    display: inline-block;
    width: 0.8rem;
    height: 2.5px;
    background: var(--c-brique);
    border-radius: var(--rayon-plein);
    transform: rotate(-35deg);
    vertical-align: middle;
}

/* Séparateur de section. */
.separateur {
    border: 0;
    height: 1px;
    background: var(--c-trait);
    margin: var(--espace-7) 0;
}

/* ---------------------------------------------------------------------------
 * 5. Liens
 * ------------------------------------------------------------------------ */

a {
    color: var(--c-brique);
    text-decoration-thickness: 1px;
    text-underline-offset: 0.18em;
    transition: color var(--transition), text-decoration-thickness var(--transition);
}

a:hover {
    color: var(--c-brique-fonce);
    text-decoration-thickness: 2px;
}

/* Lien sans soulignement permanent : navigation, listes de fiches. */
.lien-discret {
    text-decoration: none;
    color: var(--c-encre);
    font-weight: var(--graisse-moyenne);
}

.lien-discret:hover {
    color: var(--c-brique);
    text-decoration: underline;
    text-decoration-thickness: 2px;
}

/* Lien fléché, pour repartir vers une page parente. */
.retour {
    display: inline-flex;
    align-items: center;
    gap: var(--espace-2);
    font-size: var(--pas-s);
    text-decoration: none;
    margin-top: var(--espace-6);
}

.retour::before {
    content: '←';
    transition: transform var(--transition);
}

.retour:hover {
    text-decoration: underline;
}

.retour:hover::before {
    transform: translateX(-3px);
}

/* ---------------------------------------------------------------------------
 * 6. Boutons
 *
 * Trois variantes, pas davantage : principal (une seule par écran),
 * secondaire, discret. Hauteur minimale 44 px — cible tactile.
 * ------------------------------------------------------------------------ */

.bouton {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--espace-2);
    min-height: 2.75rem;
    padding: var(--espace-3) var(--espace-5);
    font-family: var(--police-texte);
    font-size: var(--pas-m);
    font-weight: var(--graisse-forte);
    line-height: 1.2;
    text-align: center;
    text-decoration: none;
    border: 1px solid transparent;
    border-radius: var(--rayon-m);
    cursor: pointer;
    transition: background-color var(--transition), border-color var(--transition),
        color var(--transition), box-shadow var(--transition), transform var(--transition);
}

.bouton--principal {
    background: var(--c-brique);
    color: #fff;
    box-shadow: var(--ombre-1);
}

.bouton--principal:hover {
    background: var(--c-brique-fonce);
    color: #fff;
}

.bouton--principal:active {
    transform: translateY(1px);
    box-shadow: none;
}

.bouton--secondaire {
    background: var(--c-surface);
    color: var(--c-brique);
    border-color: var(--c-trait);
    box-shadow: var(--ombre-1);
}

.bouton--secondaire:hover {
    background: var(--c-voile-brique);
    border-color: var(--c-brique);
    color: var(--c-brique-fonce);
}

.bouton--discret {
    background: transparent;
    color: var(--c-encre-doux);
    padding-inline: var(--espace-3);
}

.bouton--discret:hover {
    background: var(--c-voile-papier);
    color: var(--c-encre);
}

.bouton[disabled],
.bouton[aria-disabled='true'] {
    opacity: 0.5;
    cursor: not-allowed;
    box-shadow: none;
}

/* Barre d'actions. */
.actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--espace-3);
    align-items: center;
}

/* Bouton de formulaire grimé en lien.
   ⚠️ Utilisé par la déconnexion, qui DOIT rester un <form method="post"> avec
   jeton CSRF. Ne pas supprimer cette classe, ne pas retransformer en <a>. */
.lien-bouton {
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    color: var(--c-brique);
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 0.18em;
    cursor: pointer;
    transition: color var(--transition);
}

.lien-bouton:hover {
    color: var(--c-brique-fonce);
    text-decoration-thickness: 2px;
}

/* ---------------------------------------------------------------------------
 * 7. Cartes
 * ------------------------------------------------------------------------ */

.carte {
    background: var(--c-surface);
    border: 1px solid var(--c-trait);
    border-radius: var(--rayon-l);
    padding: var(--espace-5);
    box-shadow: var(--ombre-1);
}

.carte > :last-child {
    margin-bottom: 0;
}

/* Carte cliquable dans son ensemble. */
.carte--lien {
    display: block;
    text-decoration: none;
    color: inherit;
    transition: border-color var(--transition), box-shadow var(--transition);
}

.carte--lien:hover {
    border-color: var(--c-trait-fort);
    box-shadow: var(--ombre-2);
    color: inherit;
}

/* Carte d'offre : le filet supérieur porte la couleur de l'offre. */
.carte--offre {
    display: flex;
    flex-direction: column;
    gap: var(--espace-3);
    padding-top: var(--espace-6);
    position: relative;
    overflow: hidden;
}

.carte--offre::before {
    content: '';
    position: absolute;
    inset: 0 0 auto;
    height: 4px;
    background: var(--c-brique);
}

.carte--offre.est-sauge::before {
    background: var(--c-sauge);
}

/* Carte de service (à domicile, tarifs, crédit d'impôt) : filet neutre.
   La brique et la sauge restent réservées aux deux offres — sinon la lecture
   « deux offres distinctes » se brouille dès la troisième carte. */
.carte--offre.est-neutre::before {
    background: var(--c-trait-fort);
}

.carte-titre {
    font-size: var(--pas-xl);
    margin: 0;
}

.grille {
    display: grid;
    gap: var(--espace-5);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
}

/* ---------------------------------------------------------------------------
 * 8. Badges
 *
 * Étiquettes courtes : matière, niveau, statut. Toujours accompagnées d'un
 * libellé lisible — la couleur seule ne porte jamais l'information.
 * ------------------------------------------------------------------------ */

.badge {
    display: inline-flex;
    align-items: center;
    gap: var(--espace-1);
    padding: 0.15rem var(--espace-2);
    font-size: var(--pas-xs);
    font-weight: var(--graisse-forte);
    line-height: 1.5;
    border-radius: var(--rayon-s);
    background: var(--c-voile-papier);
    color: var(--c-encre-doux);
    white-space: nowrap;
}

.badge--anglais {
    background: var(--c-voile-brique);
    color: var(--c-brique-fonce);
}

.badge--soutien {
    background: var(--c-voile-sauge);
    color: var(--c-sauge-fonce);
}

/* --- Statuts de séance ---------------------------------------------------
   Un statut ne prend jamais la brique ni la sauge : elles sont prises par les
   deux offres, et un badge « Effectuée » en sauge à côté d'un badge « Anglais »
   en brique se lirait « soutien scolaire ». Les statuts se distinguent donc par
   LA FORME — pastille creuse, pastille pleine — dans les seules neutres.

   Exception unique : « Annulée tardivement » et « Absence non prévenue », les
   seuls statuts qui coûtent de l'argent sans qu'une séance ait eu lieu. C'est
   l'usage « alerte » de la brique, et c'est la seule ligne d'une facture qu'un
   parent doit pouvoir repérer sans lire. */
.badge--statut {
    background: var(--c-surface);
    border: 1px solid var(--c-trait-fort);
    color: var(--c-encre);
}

.badge--statut::before {
    content: '';
    flex: none;
    width: 0.5rem;
    height: 0.5rem;
    border-radius: var(--rayon-plein);
    border: 1.5px solid currentColor;
}

.badge--prevue::before {
    background: transparent;
}

.badge--effectuee::before {
    background: currentColor;
}

.badge--annulee {
    background: var(--c-voile-brique);
    border-color: var(--c-brique-fonce);
    color: var(--c-brique-fonce);
}

.badge--annulee::before {
    background: currentColor;
}

.badges {
    display: flex;
    flex-wrap: wrap;
    gap: var(--espace-1) var(--espace-2);
    list-style: none;
    padding: 0;
    margin: 0;
}

/* ---------------------------------------------------------------------------
 * 9. Tableaux
 *
 * Sur mobile, .tableau-defilant fait défiler horizontalement plutôt que
 * d'écraser les colonnes.
 * ------------------------------------------------------------------------ */

.tableau-defilant {
    overflow-x: auto;
    /* Un tableau suivi de texte a besoin de sa respiration — c'est le cas du
       tableau des tarifs, qui est commenté juste en dessous. En back-office il
       termine généralement sa section : la marge y passe inaperçue. */
    margin-bottom: var(--espace-5);
    border: 1px solid var(--c-trait);
    border-radius: var(--rayon-l);
    background: var(--c-surface);
    box-shadow: var(--ombre-1);
}

.tableau {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--pas-s);
}

.tableau th,
.tableau td {
    text-align: left;
    padding: var(--espace-3) var(--espace-4);
    vertical-align: top;
    border-bottom: 1px solid var(--c-trait);
}

/* ⚠️ `white-space: nowrap` a été SUPPRIMÉ ici en juillet 2026, et ne doit pas
   revenir. Cette seule déclaration coupait la colonne « Tarif » de la page
   tarifs à 390 px — on lisait « 40 ( », « 35 ( », « 25 ( ». L'en-tête « Ce que
   ça vous coûte au bout du compte » occupait à lui seul 384 px incompressibles.
   Seul un montant ne se coupe jamais : c'est `.col-montant` qui porte le
   `nowrap`, pas l'en-tête. */
.tableau thead th {
    font-size: var(--pas-xs);
    font-weight: var(--graisse-forte);
    text-transform: uppercase;
    letter-spacing: var(--interlettre-surtitre);
    color: var(--c-encre-doux);
    background: var(--c-voile-papier);
    text-wrap: balance;
    border-bottom-color: var(--c-trait-fort);
}

.tableau tbody tr:last-child td,
.tableau tbody tr:last-child th {
    border-bottom: 0;
}

.tableau tbody tr:hover {
    background: var(--c-voile-papier);
}

/* Colonne de montants : chiffres à chasse fixe, alignés à droite. */
.col-montant {
    text-align: right;
    font-family: var(--police-mono);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* Un montant peut porter une précision sous lui (le crédit d'impôt en
   back-office) : elle reprend la police de texte et la puce d'accent. */
.col-montant .note {
    display: block;
    font-family: var(--police-texte);
    white-space: normal;
}

/* --- .tableau--cartes : un tableau qui devient une pile ------------------
   Un seul balisage, deux rendus. Au-delà de 48 rem le tableau dense revient à
   l'identique ; en dessous, chaque ligne devient un bloc lisible sans un seul
   geste horizontal. C'est le même mécanisme sur la vitrine et en back-office —
   pas un second système.

   ⚠️ `display: block` sur des éléments de tableau supprime les rôles implicites
   dans WebKit : VoiceOver cesse de lire la structure. Le balisage DOIT donc
   porter `role="table" / rowgroup / row / columnheader / rowheader / cell`
   explicitement, sinon cette correction en casse une autre. */
@media (max-width: 48rem) {
    .tableau--cartes thead {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
    }

    .tableau--cartes,
    .tableau--cartes tbody,
    .tableau--cartes tr,
    .tableau--cartes th,
    .tableau--cartes td {
        display: block;
        width: auto;
    }

    .tableau--cartes tr {
        padding: var(--espace-4) var(--espace-5);
        border-bottom: 1px solid var(--c-trait);
    }

    .tableau--cartes tr:last-child {
        border-bottom: 0;
    }

    .tableau--cartes tbody th[scope='row'] {
        font-size: var(--pas-m);
        font-weight: var(--graisse-forte);
        border: 0;
        padding: 0 0 var(--espace-2);
    }

    .tableau--cartes td {
        border: 0;
        padding: 0 0 var(--espace-2);
    }

    .tableau--cartes td:last-child {
        padding-bottom: 0;
    }

    /* Le libellé de colonne suit sa valeur au lieu de la surplomber. */
    .tableau--cartes td::before {
        content: attr(data-libelle);
        display: block;
        /* Réaffirmée : sans elle, le libellé de la cellule de montant hériterait
           de la serif et de la taille du chiffre. */
        font-family: var(--police-texte);
        font-size: var(--pas-xs);
        font-weight: var(--graisse-forte);
        text-transform: uppercase;
        letter-spacing: var(--interlettre-surtitre);
        color: var(--c-encre-doux);
    }

    /* Le prix cesse d'être une cellule et devient le chiffre de la carte. */
    .tableau--cartes .col-montant {
        text-align: left;
        font-family: var(--police-titre);
        font-size: var(--pas-2xl);
        font-weight: var(--graisse-forte);
        line-height: var(--interligne-serre);
        color: var(--c-brique);
    }

    .tableau--cartes .col-montant .note {
        font-size: var(--pas-s);
        font-weight: var(--graisse-normale);
        line-height: var(--interligne-texte);
        color: var(--c-encre-doux);
    }
}

/* ---------------------------------------------------------------------------
 * 10. Fiches (listes de définition)
 * ------------------------------------------------------------------------ */

.fiche {
    display: grid;
    grid-template-columns: minmax(10rem, auto) 1fr;
    gap: var(--espace-3) var(--espace-5);
    margin: 0 0 var(--espace-5);
}

.fiche dt {
    color: var(--c-encre-doux);
    font-size: var(--pas-s);
    line-height: var(--interligne-texte);
}

.fiche dd {
    margin: 0;
}

@media (max-width: 34rem) {
    .fiche {
        grid-template-columns: 1fr;
        gap: 0;
    }

    .fiche dt {
        margin-top: var(--espace-4);
    }

    .fiche dt:first-child {
        margin-top: 0;
    }
}

/* ---------------------------------------------------------------------------
 * 11. Listes
 * ------------------------------------------------------------------------ */

/* Liste de navigation : une entrée par ligne, séparées par un filet. */
.liste {
    list-style: none;
    padding: 0;
    margin: 0 0 var(--espace-5);
    border-top: 1px solid var(--c-trait);
}

.liste > li {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--espace-2) var(--espace-3);
    padding: var(--espace-3) 0;
    border-bottom: 1px solid var(--c-trait);
}

/* Le montant est le chiffre que le parent vient vérifier : il forme une colonne
   alignée à droite, comme dans les tableaux du back-office. Le `text-align:
   right` de `.col-montant` n'a aucun effet sur un élément flex — chaque montant
   flottait à la suite d'un contenu de longueur variable, et les cinq montants de
   `/espace-eleve/seances` s'échelonnaient sur 218 px. C'est la marge automatique
   qui le pousse au bord droit de sa ligne. */
.liste .col-montant {
    margin-left: auto;
}

/* Liste à puces « accent ». */
.liste-accent {
    list-style: none;
    padding: 0;
}

.liste-accent > li {
    position: relative;
    padding-left: var(--espace-5);
    margin-bottom: var(--espace-2);
}

.liste-accent > li::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0.7em;
    width: 0.8rem;
    height: 2.5px;
    background: var(--c-brique);
    border-radius: var(--rayon-plein);
    transform: rotate(-35deg);
}

.liste-accent--sauge > li::before {
    background: var(--c-sauge);
}

.liste-accent--neutre > li::before {
    background: var(--c-trait-fort);
}

/* Chiffres clés du tableau de bord. */
/* `auto-fill` et non `auto-fit` : avec deux chiffres seulement, `auto-fit`
   étirerait deux cartes sur toute la largeur. Les tuiles gardent leur taille
   et la ligne se complète au fur et à mesure que des indicateurs s'ajoutent. */
/* 9rem et non 12rem depuis juillet 2026 : à 327 px de large, 12rem ne laissait
   qu'une tuile par ligne — trois nombres occupaient 300 px de haut sur l'écran
   d'accueil du back-office. À 9rem : deux tuiles dès 360 px, trois dès 560 px. */
.chiffres {
    display: grid;
    gap: var(--espace-4);
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 9rem), 1fr));
    list-style: none;
    padding: 0;
    margin: 0 0 var(--espace-6);
}

/* Le plancher de 9 rem est un réglage MOBILE, et il n'avait jamais été relevé
   au-delà : à 1440 px la grille se résolvait en six colonnes de 146,66 px pour
   trois tuiles — ~480 px de grille vide à droite — et « séances à venir »
   passait sur deux lignes, ce qui poussait les trois tuiles à 111 px de haut.
   13 rem donne quatre colonnes de 228 px à la largeur de page du back-office :
   le libellé tient sur une ligne, et la quatrième colonne accueillera
   l'indicateur que la facturation apportera sans redistribuer la grille.
   `auto-fill` reste indispensable (voir ci-dessus). */
@media (min-width: 40rem) {
    .chiffres {
        grid-template-columns: repeat(auto-fill, minmax(min(100%, 13rem), 1fr));
    }
}

.chiffre {
    display: block;
    height: 100%;
    padding: var(--espace-4) var(--espace-5);
    background: var(--c-surface);
    border: 1px solid var(--c-trait);
    border-radius: var(--rayon-l);
    text-decoration: none;
    color: inherit;
    box-shadow: var(--ombre-1);
    transition: border-color var(--transition), box-shadow var(--transition);
}

a.chiffre:hover {
    border-color: var(--c-trait-fort);
    box-shadow: var(--ombre-2);
    color: inherit;
}

.chiffre-valeur {
    display: block;
    font-family: var(--police-titre);
    font-size: var(--pas-2xl);
    font-weight: var(--graisse-forte);
    line-height: var(--interligne-serre);
    letter-spacing: var(--interlettre-titre);
    font-variant-numeric: tabular-nums;
}

.chiffre-libelle {
    display: block;
    font-size: var(--pas-s);
    color: var(--c-encre-doux);
    margin-top: var(--espace-1);
}

/* État vide : ce n'est pas une erreur, c'est une page qui attend. */
.vide {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--espace-3);
    padding: var(--espace-6) var(--espace-5);
    text-align: center;
    color: var(--c-encre-doux);
    font-size: var(--pas-s);
    background: var(--c-surface);
    border: 1px dashed var(--c-trait-fort);
    border-radius: var(--rayon-l);
}

.vide p {
    margin: 0;
    max-width: 28rem;
}

/* ---------------------------------------------------------------------------
 * 12. Formulaires
 * ------------------------------------------------------------------------ */

.formulaire {
    margin: 0;
}

.champ {
    display: flex;
    flex-direction: column;
    gap: var(--espace-2);
    margin: 0 0 var(--espace-5);
}

.formulaire label {
    font-size: var(--pas-s);
    font-weight: var(--graisse-moyenne);
    color: var(--c-encre-doux);
}

.champ-aide {
    font-size: var(--pas-xs);
    color: var(--c-encre-doux);
}

.formulaire input,
.formulaire select,
.formulaire textarea {
    font: inherit;
    /* 16 px minimum sur mobile : en dessous, iOS zoome au focus. */
    font-size: var(--pas-m);
    color: var(--c-encre);
    min-height: 2.75rem;
    padding: var(--espace-2) var(--espace-3);
    background: var(--c-surface);
    border: 1px solid var(--c-trait-fort);
    border-radius: var(--rayon-m);
    transition: border-color var(--transition), box-shadow var(--transition);
}

.formulaire textarea {
    min-height: 7rem;
    line-height: var(--interligne-texte);
    resize: vertical;
}

.formulaire input:hover,
.formulaire select:hover,
.formulaire textarea:hover {
    border-color: var(--c-encre-doux);
}

.formulaire input:focus,
.formulaire select:focus,
.formulaire textarea:focus {
    border-color: var(--c-brique);
    box-shadow: var(--anneau-focus);
    outline: none;
}

.formulaire input[aria-invalid='true'],
.champ--erreur input {
    border-color: var(--c-brique-fonce);
}

/* Même règle que `.alerte` : un composant de message n'est jamais un conteneur
   flex — un message qui contiendrait un `<strong>` y serait éclaté en colonnes.
   Le ⚠ est un glyphe de chasse variable selon la plateforme : il reste dans le
   flux du texte (marge à droite), il n'est pas positionné en absolu. */
.champ-erreur {
    display: block;
    font-size: var(--pas-s);
    font-weight: var(--graisse-moyenne);
    color: var(--c-brique-fonce);
}

.champ-erreur::before {
    content: '⚠';
    margin-right: var(--espace-2);
}

/* Bouton de soumission nu : il prend l'apparence du bouton principal sans
   qu'on ait à ajouter la classe sur chaque formulaire. */
.formulaire button[type='submit']:not(.lien-bouton):not(.bouton) {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 2.75rem;
    padding: var(--espace-3) var(--espace-5);
    font: inherit;
    font-weight: var(--graisse-forte);
    background: var(--c-brique);
    color: #fff;
    border: 0;
    border-radius: var(--rayon-m);
    cursor: pointer;
    box-shadow: var(--ombre-1);
    transition: background-color var(--transition);
}

.formulaire button[type='submit']:not(.lien-bouton):not(.bouton):hover {
    background: var(--c-brique-fonce);
}

/* Action pleine largeur — le SEUL moyen déclaré d'étendre un bouton sur toute la
   largeur du formulaire (aujourd'hui : le « Se connecter » de la page de
   connexion). Elle fait doublon avec l'étirement que `.champ` impose à ses
   enfants, et c'est justement pour ça qu'elle reste : cet étirement est un effet
   de bord, pas une intention — il donne aussi des boutons « Enregistrer » de
   960 px sur les formulaires du back-office.

   Une barre d'actions se balise `<p class="actions">`, qui ne s'étire pas.
   Un bouton pleine largeur se DEMANDE, ici, et nulle part ailleurs.

   `>` et non un descendant : la classe habille le champ qui porte le bouton,
   elle ne déborde pas sur un bouton imbriqué plus bas. */
.champ--pleine-largeur > button,
.champ--pleine-largeur > .bouton {
    width: 100%;
}

/* Groupe de cases à cocher — les matières suivies d'un élève.
   Seul ajout de la saisie des foyers et des élèves : aucun composant existant
   ne couvre le choix multiple, et `.formulaire input` habille indistinctement
   tous les champs (2,75 rem, bordure, fond), ce qui donnait des cases de la
   taille d'un champ de texte.

   Cases plutôt qu'un `<select multiple>` : Cécile saisit depuis son téléphone
   le soir, et une sélection multiple native y demande un appui long puis des
   appuis précis — exactement le geste que le § 14.1 reproche aux tableaux.

   La grille à deux colonnes apparie chaque `<input>` avec son `<label>` : le
   thème Symfony les rend à plat (input, label, input, label…), sans conteneur
   par option. Le libellé porte la hauteur de cible tactile, pas la case. */
.champ--cases > div {
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: center;
    column-gap: var(--espace-3);
}

.champ--cases input[type='checkbox'] {
    width: 1.125rem;
    height: 1.125rem;
    min-height: 0;
    padding: 0;
    accent-color: var(--c-brique);
}

/* `> div label` et non `label` : le libellé du CHAMP lui-même (« Matières
   suivies ») est un frère de la grille, pas un de ses enfants. La règle le
   visait aussi, et il ressortait à 17 px en `--c-encre` quand « Prénom » et
   « Nom » du même formulaire sont à 15 px en `--c-encre-doux` — un seul champ du
   formulaire avait un libellé plus gros et plus foncé que les autres. Seules les
   OPTIONS portent la cible tactile. */
.champ--cases > div label {
    display: flex;
    align-items: center;
    min-height: 2.75rem;
    font-size: var(--pas-m);
    color: var(--c-encre);
    cursor: pointer;
}

/* ---------------------------------------------------------------------------
 * 13. Messages (alertes, flash)
 *
 * L'information n'est jamais portée par la seule couleur : chaque message
 * a un pictogramme et une barre latérale.
 * ------------------------------------------------------------------------ */

/* ⚠️ `display: block`, et JAMAIS `flex` (correction de juillet 2026, § 8).
   En conteneur flex, chaque nœud de texte nu devient un élément flex anonyme :
   dès qu'un message mêlait du texte et un `<strong>`, la phrase était découpée
   en autant de colonnes séparées par le `gap`. « Annuler la séance » se lisait
   en cinq colonnes de 113 / 57 / 14 / 39 / 4 px à 390 px, point final compris —
   pire sur téléphone, là où Cécile saisit le soir.

   Le composant doit rester insensible au balisage qu'on met dedans, sans quoi le
   piège resservira au prochain message : la pastille est posée en absolu et la
   place qu'elle occupe est réservée par le padding gauche. Même raison qu'au
   § 11.5 pour `.surtitre`. */
.alerte {
    display: block;
    position: relative;
    padding: var(--espace-3) var(--espace-4);
    /* Réserve de la pastille : gouttière + 1,25 rem de pastille + gouttière. */
    padding-left: calc(var(--espace-4) + 1.25rem + var(--espace-3));
    margin: 0 0 var(--espace-5);
    font-size: var(--pas-s);
    background: var(--c-voile-papier);
    color: var(--c-encre);
    border-left: 4px solid var(--c-trait-fort);
    border-radius: 0 var(--rayon-m) var(--rayon-m) 0;
}

.alerte::before {
    content: 'i';
    position: absolute;
    left: var(--espace-4);
    /* La pastille s'aligne sur la PREMIÈRE LIGNE de texte, pas sur le haut de la
       boîte : 0,15em reprend exactement l'ancien décalage optique. */
    top: calc(var(--espace-3) + 0.15em);
    display: grid;
    place-items: center;
    width: 1.25rem;
    height: 1.25rem;
    font-family: var(--police-titre);
    font-size: var(--pas-xs);
    font-weight: var(--graisse-forte);
    line-height: 1;
    color: var(--c-surface);
    background: var(--c-trait-fort);
    border-radius: var(--rayon-plein);
}

.alerte--erreur,
.alerte--error,
.alerte--danger {
    background: var(--c-voile-brique);
    color: var(--c-brique-fonce);
    border-left-color: var(--c-brique-fonce);
}

.alerte--erreur::before,
.alerte--error::before,
.alerte--danger::before {
    content: '!';
    background: var(--c-brique-fonce);
}

.alerte--success,
.alerte--succes {
    background: var(--c-voile-sauge);
    color: var(--c-sauge-fonce);
    border-left-color: var(--c-sauge);
}

.alerte--success::before,
.alerte--succes::before {
    content: '✓';
    background: var(--c-sauge);
}

.alerte a {
    color: inherit;
}

/* ---------------------------------------------------------------------------
 * 14. Illustrations
 *
 * Dessins au trait maison, tous sur la même grille 48×48, même graisse de
 * trait (1,75), mêmes extrémités arrondies. La couleur vient de currentColor :
 * on colore le conteneur, jamais le SVG.
 * ------------------------------------------------------------------------ */

.illu {
    display: block;
    width: 3rem;
    height: 3rem;
    color: var(--c-brique);
}

.illu--sauge { color: var(--c-sauge); }
.illu--encre { color: var(--c-encre-doux); }
.illu--l { width: 4.5rem; height: 4.5rem; }

/* Pictogramme en ligne, à la hauteur d'un caractère : c'est ce qui remplace les
   glyphes ☎ et ✉, de chasse imprévisible selon la plateforme (parfois rendus en
   emoji couleur), qui cassaient l'alignement de la puce d'accent. */
.illu--s {
    display: inline-block;
    width: 1.15rem;
    height: 1.15rem;
    vertical-align: -0.2em;
}

/* ---------------------------------------------------------------------------
 * 15. Vitrine publique
 * ------------------------------------------------------------------------ */

.espace-public {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

/* Palier mobile — le padding vertical de section passe d'un cran.
   Mesuré : le bandeau d'accueil faisait 886 px de haut à 390 px, dont 160 px de
   padding pur ; l'accueil compte six sections, la FAQ seize. Le padding vertical
   représentait à lui seul entre 800 et 2 000 px par page. Quarante pixels de
   respiration suffisent sur un écran de 390 px de large.
   La gouttière, elle, ne bouge pas : le texte a besoin de sa marge. Ce sont les
   tableaux qui passent en pleine largeur (§ 12.2 b). */
@media (max-width: 40rem) {
    .espace-public {
        --espace-8: 2.5rem;   /* 64 → 40 px */
        --espace-9: 3rem;     /* 96 → 48 px */
    }
}

/* --- Logotype ------------------------------------------------------------
   « Hello » en italique brique, « Cécile » en romain encre. Le clin d'œil
   bilingue tient à ce seul contraste : un mot anglais, un prénom accentué. */
.logotype {
    display: inline-flex;
    align-items: baseline;
    gap: 0.28em;
    font-family: var(--police-titre);
    font-size: var(--pas-xl);
    font-weight: var(--graisse-forte);
    line-height: 1.1;
    letter-spacing: var(--interlettre-titre);
    text-decoration: none;
    color: var(--c-encre);
    white-space: nowrap;
}

.logotype:hover {
    color: var(--c-encre);
}

.logotype-hello {
    font-style: italic;
    color: var(--c-brique);
}

.logotype--grand {
    font-size: var(--pas-2xl);
}

/* --- En-tête vitrine --------------------------------------------------- */
.entete {
    position: sticky;
    top: 0;
    z-index: 5;
    background: rgba(251, 248, 243, 0.93);
    backdrop-filter: blur(8px);
    border-bottom: 1px solid var(--c-trait);
}

.entete-interieur {
    max-width: var(--largeur-page);
    margin: 0 auto;
    padding: var(--espace-3) var(--gouttiere);
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--espace-2) var(--espace-5);
}

.entete-nav {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--espace-2) var(--espace-5);
    font-size: var(--pas-s);
}

.entete-nav a {
    color: var(--c-encre-doux);
    text-decoration: none;
    font-weight: var(--graisse-moyenne);
    padding: var(--espace-1) 0;
    border-bottom: 2px solid transparent;
}

.entete-nav a:hover {
    color: var(--c-brique);
    text-decoration: underline;
    text-decoration-thickness: 2px;
}

/* Rubrique courante — même traitement que `.bandeau-nav` des espaces
   connectés : le trait accent, jamais la couleur seule, et une bordure
   transparente au repos pour que l'état actif ne décale rien. */
.entete-nav a[aria-current='page'] {
    color: var(--c-encre);
    border-bottom-color: var(--c-brique);
}

/* Sous 40rem, le bandeau passe sur deux lignes : collant, il mangerait un
   dixième de la hauteur utile. On le laisse défiler — et c'est `.barre-appel`
   qui prend le relais de la persistance. Deux barres collantes sur un écran de
   740 px, c'en est une de trop. */
@media (max-width: 40rem) {
    .entete {
        position: static;
    }

    .entete-interieur {
        gap: var(--espace-2);
    }

    /* Les rubriques tiennent sur une ligne et défilent horizontalement.
       Pas de menu déroulant : six entrées ne justifient pas un composant à
       état, et un ruban reste lisible sans interaction. Mesuré : 179 px de
       hauteur d'en-tête (six rubriques sur trois lignes) → ~86 px. */
    .entete-nav {
        flex-wrap: nowrap;
        overflow-x: auto;
        scrollbar-width: none;
        margin-inline: calc(-1 * var(--gouttiere));
        padding-inline: var(--gouttiere);
        scroll-snap-type: x proximity;
    }

    .entete-nav::-webkit-scrollbar {
        display: none;
    }

    .entete-nav a {
        white-space: nowrap;
        scroll-snap-align: start;
    }
}

/* --- Sections ----------------------------------------------------------- */
.section {
    padding: var(--espace-8) var(--gouttiere);
}

.section--voile {
    background: var(--c-voile-papier);
}

.section-interieur {
    max-width: var(--largeur-page);
    margin: 0 auto;
}

.section-entete {
    max-width: var(--largeur-texte);
    margin: 0 0 var(--espace-6);
}

.section-entete p {
    color: var(--c-encre-doux);
    margin: var(--espace-3) 0 0;
}

/* Les tableaux passent en pleine largeur sur mobile : 24 px de gouttière de
   chaque côté sont récupérés, et le tableau touche les bords comme un tableau
   imprimé sur une page étroite. Avec la suppression du `nowrap` d'en-tête, le
   tableau des tarifs (trois colonnes) tient alors sans coupure dès 375 px. */
@media (max-width: 40rem) {
    .section-interieur > .tableau-defilant,
    .prose > .tableau-defilant {
        margin-inline: calc(-1 * var(--gouttiere));
        border-inline: 0;
        border-radius: 0;
    }
}

/* Débordement horizontal résiduel : un marqueur en ligne contient parfois une
   chaîne non sécable (une adresse, un intitulé), et la page débordait de 5 px
   sur `/tarifs-et-zone-d-intervention`. */
.prose,
.marqueur,
.fiche dd {
    overflow-wrap: anywhere;
}

/* --- Bandeau d'accueil -------------------------------------------------- */
.heros {
    padding: var(--espace-8) var(--gouttiere) var(--espace-9);
    text-align: center;
}

.heros-interieur {
    max-width: 46rem;
    margin: 0 auto;
}

.heros h1 {
    font-size: var(--pas-4xl);
    line-height: var(--interligne-serre);
    margin: 0 0 var(--espace-5);
}

.heros .chapo {
    margin: 0 auto var(--espace-6);
}

.heros .actions {
    justify-content: center;
}

/* `.surtitre` n'est plus un conteneur flex (§ 11.5) : c'est l'alignement du
   texte qui centre la puce avec lui. */
.heros .surtitre {
    text-align: center;
}

/* L'illustration du bandeau. La règle vivait sous `.heros-illu`, une classe
   qu'AUCUN gabarit ne porte : `templates/vitrine/accueil.html.twig` rend un
   `<svg class="illu">` tout court. Règle morte, donc SVG en `display: block`
   sans marge — collé au bord gauche d'un bandeau centré (x=345 pour un bandeau
   de 736 px), et son bas touchant le surtitre à 0 px au lieu des 24 prévus.
   Elle est rattachée à ce que le gabarit rend réellement.

   `> .illu` et non `.heros .illu` : `/contact` porte des pictogrammes `.illu--s`
   EN LIGNE dans son bandeau (téléphone, enveloppe). Une marge basse de 24 px sur
   un `inline-block` gonfle la boîte de ligne — la règle ne vise que
   l'illustration qui ouvre le bandeau, seule enfant directe du conteneur. */
.heros-interieur > .illu {
    margin: 0 auto var(--espace-5);
}

/* --- Bandeau de page intérieure ------------------------------------------
   Le centrage est réservé au bandeau de la page d'accueil, et à lui seul. Sur
   téléphone, un chapô de cinq lignes centré en drapeau des deux côtés est le
   mode de lecture le plus coûteux — et on le payait sur les pages où le lecteur
   est le plus pressé. Les bandeaux intérieurs sont alignés à gauche, sans
   illustration : le motif de l'accent reste porté par le surtitre. */
.heros--page {
    text-align: left;
    padding-bottom: var(--espace-7);
}

.heros--page .heros-interieur {
    max-width: var(--largeur-page);
}

.heros--page .chapo,
.heros--page .actions,
.heros--page .surtitre,
.heros--page .heros-interieur > .illu {
    margin-inline: 0;
    justify-content: flex-start;
    text-align: left;
}

/* --- .reperes : le bloc de réponse ---------------------------------------
   Deux à quatre faits courts, chiffrés ou géographiques, posés sous le chapô
   dans le premier écran. C'est ce qui répond « à qui / où / combien » avant que
   le visiteur ait fait un geste. Si le premier écran mobile (740 px) ne les
   contient pas, c'est le haut de page qu'on raccourcit — pas les repères qu'on
   descend. */
.reperes {
    display: flex;
    flex-wrap: wrap;
    gap: var(--espace-3) var(--espace-5);
    list-style: none;
    padding: var(--espace-4) 0 0;
    margin: var(--espace-5) 0 0;
    border-top: 1px solid var(--c-trait);
}

/* Les repères suivent l'alignement de leur bandeau. Sur l'accueil, le bloc
   mesurait 736 px et son dernier repère s'arrêtait à x=930 — 151 px de vide à
   droite — pendant que le h1, le chapô et les boutons étaient centrés : le bloc
   paraissait décroché vers la gauche. Les bandeaux intérieurs, eux, sont alignés
   à gauche à dessein (§ 11.4). */
.heros .reperes {
    justify-content: center;
}

.heros--page .reperes {
    justify-content: flex-start;
}

.repere-terme {
    display: block;
    font-size: var(--pas-xs);
    font-weight: var(--graisse-forte);
    text-transform: uppercase;
    letter-spacing: var(--interlettre-surtitre);
    color: var(--c-encre-doux);
}

.repere-valeur {
    display: block;
    font-family: var(--police-titre);
    font-size: var(--pas-xl);
    font-weight: var(--graisse-forte);
    line-height: var(--interligne-serre);
    letter-spacing: var(--interlettre-titre);
    color: var(--c-encre);
}

/* Un repère chiffré, et un seul par bloc, porte la brique : le prix. */
.repere--montant .repere-valeur {
    color: var(--c-brique);
    font-variant-numeric: tabular-nums;
}

/* --- .repli : le repliement natif ----------------------------------------
   `<details>` / `<summary>` : natif, sans JavaScript, sans Stimulus, conforme à
   l'ADR 0002 — et le texte reste dans le DOM, donc dans le JSON-LD et pour les
   moteurs. Aucun contenu n'est retiré des pages : il est replié.

   ⚠️ Une section repliable qui contient un marqueur `[À CONFIRMER]` ou
   `[À VALIDER]` est rendue `open` dans le gabarit. Le repliement ne doit jamais
   devenir un moyen d'oublier qu'une page n'est pas publiable. */
.repli {
    border-bottom: 1px solid var(--c-trait);
}

.repli > summary {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--espace-4);
    min-height: 2.75rem;
    padding: var(--espace-3) 0;
    cursor: pointer;
    list-style: none;
}

.repli > summary::-webkit-details-marker {
    display: none;
}

/* Le chevron est le motif d'accent, pivoté d'un quart de tour à l'ouverture.
   -35° reste l'angle de repos : c'est le même trait. */
.repli > summary::after {
    content: '';
    flex: none;
    align-self: center;
    width: 0.8rem;
    height: 2.5px;
    background: var(--c-brique);
    border-radius: var(--rayon-plein);
    transform: rotate(-35deg);
    transition: transform var(--transition);
}

.repli[open] > summary::after {
    transform: rotate(35deg);
}

.repli > summary h2,
.repli > summary h3 {
    display: inline;
}

.repli > summary:hover h2,
.repli > summary:hover h3 {
    color: var(--c-brique);
}

/* Le corps d'un repli respire du même écart qu'un paragraphe. */
.repli-corps {
    padding: 0 0 var(--espace-5);
}

/* --- .encart-credit : l'opposition binaire du crédit d'impôt --------------
   Remplace un tableau de douze cellules qui disait une chose et une seule :
   chez vous, 40 € deviennent 20 € ; les trois autres formules ne donnent droit
   à rien. La sauge est ici l'état de succès, pas l'offre soutien scolaire —
   l'encart ne côtoie aucun badge de matière, la lecture n'est pas ambiguë.

   ⚠️ La restriction reste DANS l'encart et n'est jamais reléguée en note de bas
   de page : sur un sujet fiscal, une restriction séparée de son avantage est
   une promesse trompeuse. */
.encart-credit {
    display: grid;
    gap: var(--espace-4);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 9rem), 1fr));
    padding: var(--espace-5);
    background: var(--c-voile-sauge);
    border-radius: var(--rayon-l);
    margin-bottom: var(--espace-5);
}

.encart-credit .chiffre-valeur {
    color: var(--c-sauge-fonce);
}

.encart-credit .note {
    grid-column: 1 / -1;
    margin: 0;
}

/* --- .barre-appel : le moyen d'appeler, en permanence ---------------------
   Le numéro de téléphone apparaissait en moyenne à 91 % de la hauteur des
   pages : au-delà du dixième écran, sur le seul canal de conversion du site.
   La barre est le composant qui répond seul au critère « le moyen d'appeler est
   atteignable sans défiler ».

   Elle porte l'unique `.bouton--principal` visible en mobile ; le bouton de fin
   de page est ramené au registre secondaire sous 40 rem. */
.barre-appel {
    display: none;
}

@media (max-width: 40rem) {
    .barre-appel {
        position: fixed;
        inset: auto 0 0;
        z-index: 6;
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: var(--espace-2);
        padding: var(--espace-2) var(--gouttiere);
        padding-bottom: calc(var(--espace-2) + env(safe-area-inset-bottom));
        background: rgba(251, 248, 243, 0.96);
        backdrop-filter: blur(8px);
        border-top: 1px solid var(--c-trait);
        box-shadow: 0 -2px 12px -6px rgba(35, 32, 29, 0.18);
    }

    .barre-appel .bouton {
        width: 100%;
        padding-inline: var(--espace-3);
    }

    /* Tant que le numéro est provisoire, la barre porte sa propre bande de
       marqueur, en permanence et non plus une seule fois en bas de page : la
       contrainte éditoriale est renforcée, pas contournée. Cette bande dispa-
       raît le jour où `coordonnees.telephone()` porte le vrai numéro. */
    .barre-appel-marqueur {
        grid-column: 1 / -1;
        margin: calc(-1 * var(--espace-2)) calc(-1 * var(--gouttiere)) 0;
        padding: var(--espace-1) var(--gouttiere);
        font-size: var(--pas-xs);
        color: var(--c-brique-fonce);
        background: var(--c-voile-brique);
    }

    /* Le pied de page ne doit pas finir sous la barre. La réserve est posée sur
       `.pied`, qui n'existe que sur les pages qui portent la barre — et pas sur
       `.espace-public`, que la page de connexion partage sans avoir de barre. */
    .pied {
        padding-bottom: calc(var(--espace-7) + 4.5rem);
    }

    /* Le bouton de fin de page cède le registre principal à la barre : la règle
       « un seul `.bouton--principal` par écran » ne souffre pas d'exception, et
       c'est la barre qui porte désormais l'action principale.
       ⚠️ Scopé à `.bloc-contact` : la page de connexion partage `.espace-public`
       et son « Se connecter » doit rester un bouton principal plein. */
    .bloc-contact .bouton--principal {
        background: var(--c-surface);
        color: var(--c-brique);
        border-color: var(--c-trait);
    }

    .bloc-contact .bouton--principal:hover {
        background: var(--c-voile-brique);
        border-color: var(--c-brique);
        color: var(--c-brique-fonce);
    }
}

/* --- Présentation : portrait + texte -------------------------------------
   Bloc « Qui je suis ». C'est le seul endroit du site où une photographie est
   attendue, et ce doit être une vraie photo de Cécile — jamais une image de
   banque. Tout le parti pris tient à ça : une personne, pas une plateforme.
   Cadrage carré, coins adoucis, filet blanc : la photo est posée sur le
   papier, elle ne fait pas bandeau pleine largeur. */
.presentation {
    display: grid;
    gap: var(--espace-6);
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
}

@media (min-width: 46rem) {
    .presentation {
        grid-template-columns: 16rem minmax(0, 1fr);
        gap: var(--espace-7);
    }
}

.portrait {
    display: block;
    width: 100%;
    max-width: 16rem;
    aspect-ratio: 1;
    object-fit: cover;
    border-radius: var(--rayon-l);
    border: 5px solid var(--c-surface);
    box-shadow: var(--ombre-2);
}

/* --- Bande secteur ------------------------------------------------------ */
.communes {
    display: flex;
    flex-wrap: wrap;
    gap: var(--espace-2) var(--espace-3);
    list-style: none;
    padding: 0;
    margin: var(--espace-4) 0 0;
}

.communes li {
    padding: var(--espace-1) var(--espace-3);
    font-size: var(--pas-s);
    background: var(--c-surface);
    border: 1px solid var(--c-trait);
    border-radius: var(--rayon-plein);
}

/* --- Pied de page ------------------------------------------------------- */
.pied {
    margin-top: auto;
    padding: var(--espace-7) var(--gouttiere);
    background: var(--c-encre);
    color: var(--c-voile-papier);
}

.pied-interieur {
    max-width: var(--largeur-page);
    margin: 0 auto;
    display: flex;
    flex-wrap: wrap;
    gap: var(--espace-5) var(--espace-7);
    justify-content: space-between;
    align-items: flex-start;
}

.pied .logotype {
    color: var(--c-papier);
}

/* Sur fond encre, la brique tombe sous le seuil AA : on l'éclaircit.
   --c-brique-clair sur --c-encre = 7,6:1. */
.pied .logotype-hello {
    color: var(--c-brique-clair);
}

.pied a {
    color: var(--c-papier);
}

.pied a:hover {
    color: var(--c-brique-clair);
}

/* L'anneau de focus global est en `--c-brique` : sur l'aplat encre du pied il
   mesurait 2,74:1, sous le seuil de 3:1 de WCAG 1.4.11 — l'anneau y était donc
   littéralement invisible au clavier. Il reprend la même dérogation que le
   logotype. Le pied est le seul aplat sombre du site ; c'est la seule exception. */
.pied :focus-visible {
    outline-color: var(--c-brique-clair);
}

.pied p {
    font-size: var(--pas-s);
    /* --c-papier-doux sur --c-encre = 9,7:1. */
    color: var(--c-papier-doux);
}

.pied ul {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: var(--espace-2);
    font-size: var(--pas-s);
}

/* ---------------------------------------------------------------------------
 * 16. Écrans connectés (back-office et espace famille)
 *
 * Même système, densité supérieure : Cécile y passe ses soirées et saisit
 * souvent depuis son téléphone. On resserre l'échelle et les espacements en
 * redéfinissant les variables sur le <body> — pas en dupliquant des règles.
 * ------------------------------------------------------------------------ */

.espace-admin,
.espace-famille {
    --pas-3xl: 1.75rem;
    --pas-2xl: 1.3125rem;
    --pas-xl: 1.1875rem;
    --espace-9: 4rem;
    --espace-8: 2.5rem;
    --espace-7: 2rem;
    --largeur-page: 62rem;
    --gouttiere: var(--espace-4);
}

/* L'espace famille reste un peu plus aéré que le back-office : les parents y
   viennent ponctuellement, ils n'ont pas l'habitude de l'écran. */
.espace-famille {
    --pas-3xl: 2rem;
    --gouttiere: var(--espace-5);
}

/* --- Bandeau des espaces connectés -------------------------------------- */
.bandeau {
    background: var(--c-surface);
    border-bottom: 1px solid var(--c-trait);
}

.bandeau-interieur {
    max-width: var(--largeur-page);
    margin: 0 auto;
    padding: var(--espace-3) var(--gouttiere);
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--espace-2) var(--espace-5);
}

.bandeau-marque {
    display: flex;
    align-items: center;
    gap: var(--espace-3);
}

.bandeau .logotype {
    font-size: var(--pas-l);
}

/* Nom de l'espace, à droite du logotype : « Administration », « Espace famille ». */
.bandeau-espace {
    font-size: var(--pas-xs);
    font-weight: var(--graisse-forte);
    text-transform: uppercase;
    letter-spacing: var(--interlettre-surtitre);
    color: var(--c-encre-doux);
    padding-left: var(--espace-3);
    border-left: 1px solid var(--c-trait);
}

.bandeau-nav {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--espace-2) var(--espace-4);
    font-size: var(--pas-s);
}

.bandeau-nav a {
    color: var(--c-encre-doux);
    text-decoration: none;
    font-weight: var(--graisse-moyenne);
    padding: var(--espace-1) 0;
    border-bottom: 2px solid transparent;
}

/* Exactement le survol de `.entete-nav` (§ 6) : même composant, même rôle, même
   comportement. Le bandeau connecté se contentait de la couleur ; l'en-tête
   vitrine soulignait. L'état actif reste distingué par le filet accent
   ci-dessous, il ne dépend pas du survol. */
.bandeau-nav a:hover {
    color: var(--c-brique);
    text-decoration: underline;
    text-decoration-thickness: 2px;
}

/* Rubrique courante : marquée par le trait accent, pas par la couleur seule. */
.bandeau-nav a[aria-current='page'] {
    color: var(--c-encre);
    border-bottom-color: var(--c-brique);
}

.bandeau-compte {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--espace-2) var(--espace-4);
    font-size: var(--pas-s);
    color: var(--c-encre-doux);
}

.bandeau-compte form {
    margin: 0;
}

/* Le bandeau connecté mesurait 122 px sur chaque écran du back-office, avant
   tout contenu. Sous 30 rem, le nom de l'espace est masqué — le logotype et
   l'URL suffisent à situer — et la navigation passe en ruban défilant, même
   règle que l'en-tête vitrine (§ 11.1).

   ⚠️ La déconnexion reste un `<form method="post">` avec `csrf_token('logout')`.
   Sa position et son apparence peuvent changer ; sa nature, jamais. */
@media (max-width: 40rem) {
    .bandeau-interieur {
        gap: var(--espace-2);
    }

    .bandeau-nav {
        flex-wrap: nowrap;
        overflow-x: auto;
        scrollbar-width: none;
        margin-inline: calc(-1 * var(--gouttiere));
        padding-inline: var(--gouttiere);
        scroll-snap-type: x proximity;
    }

    .bandeau-nav::-webkit-scrollbar {
        display: none;
    }

    .bandeau-nav a {
        white-space: nowrap;
        scroll-snap-align: start;
    }
}

@media (max-width: 30rem) {
    .bandeau-espace {
        display: none;
    }
}

/* --- Corps de page ------------------------------------------------------ */
.page {
    max-width: var(--largeur-page);
    margin: 0 auto;
    padding: var(--espace-6) var(--gouttiere) var(--espace-9);
}

.page--etroite {
    max-width: var(--largeur-etroite);
    padding-top: var(--espace-8);
}

.page > h1 {
    margin: 0 0 var(--espace-5);
}

.page h2 {
    margin: var(--espace-7) 0 var(--espace-4);
    padding-bottom: var(--espace-2);
    border-bottom: 1px solid var(--c-trait);
}

/* --- Deux filets ne se suivent jamais ------------------------------------
   Le filet bas de `.page h2` tombait à 16 px du filet haut de `.liste` ou de la
   bordure de `.tableau-defilant` : deux traits 1 px de la même teinte, sur six
   écrans (`/admin-app/seances`, `/admin-app/familles/{id}`, `/espace-eleve` et
   `/espace-eleve/seances`).

   Lequel s'efface dépend de la nature du bloc qui suit :
     · bloc OUVERT (`.liste`) — il n'a qu'un bord haut, décoratif : il le perd,
       le soulignement du titre lui sert de bord ;
     · bloc FERMÉ (`.tableau-defilant`, `.carte`) — on ne retire pas un seul côté
       à un objet à bordure complète et à rayon : c'est le soulignement du titre
       qui s'efface, la carte porte alors seule la séparation. */
.page h2 + .liste {
    border-top: 0;
}

.page h2:has(+ .tableau-defilant),
.page h2:has(+ .carte) {
    padding-bottom: 0;
    border-bottom: 0;
}

/* --- En-tête de page — règle unique, back-office et espace famille --------
   Trois éléments au plus, tous ENFANTS DIRECTS de `.page-entete` :

     ligne 1 : le `<h1>` à gauche, la barre `.actions` à droite ;
     ligne 2 : la `<p class="note">`, seule, plafonnée à `--largeur-texte`.

   ⚠️ Aucun `<div>` d'enveloppe autour du titre et de la note. Une enveloppe fait
   de la paire titre + note l'élément de GAUCHE d'un `space-between` : la note y
   impose sa largeur de lecture (576 px) et pousse la barre d'actions à la ligne.
   C'est ce qui donnait deux mises en page à deux fiches jumelles — actions sous
   le titre sur la fiche foyer, actions en haut à droite sur la fiche élève.

   Sans enveloppe, une note frère du `<h1>` devenait l'élément de DROITE et
   partait au bord de la page (jusqu'à 836 px de décalage, sur sept écrans). Les
   deux défauts sont le même défaut, vu des deux côtés ; la règle ci-dessous les
   ferme tous les deux, sans que le gabarit ait à connaître le mécanisme. */
/* Grille explicite et non `flex` + `space-between` : c'est la seule façon de
   dire « la note occupe SA ligne » sans dépendre de la place restante. En flex,
   une note en `flex-basis: 100%` mais plafonnée à `--largeur-texte` remonte sur
   la ligne du titre — l'algorithme casse la ligne sur la taille hypothétique,
   qui est déjà bornée par le `max-width`. Vérifié : elle repassait à côté du
   titre, à 576 px de large. */
.page-entete {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: start;
    gap: var(--espace-3) var(--espace-5);
    margin: 0 0 var(--espace-5);
}

.page-entete h1 {
    margin: 0;
}

.page-entete .note {
    margin: var(--espace-2) 0 0;
}

.page-entete > h1 {
    grid-area: 1 / 1;
}

/* Placement explicite : la barre reste en haut à droite quel que soit l'ordre
   dans lequel le gabarit écrit ses éléments. */
.page-entete > .actions {
    grid-area: 1 / 2;
    justify-self: end;
}

/* La note prend la ligne suivante sur toute la largeur, et retrouve la largeur
   de lecture que `.prose` lui donnait. */
.page-entete > .note {
    grid-column: 1 / -1;
    max-width: var(--largeur-texte);
    margin: 0;
}

/* Sous 40 rem l'en-tête s'empile : la barre d'actions passe sous le titre,
   alignée à gauche comme le reste. Une colonne d'actions à droite d'un titre
   sur 358 px de large réduirait les deux à leur largeur minimale. */
@media (max-width: 40rem) {
    .page-entete {
        grid-template-columns: minmax(0, 1fr);
    }

    .page-entete > .actions {
        grid-area: auto;
        justify-self: start;
    }
}

/* --- Carte de connexion -------------------------------------------------- */
.connexion {
    background: var(--c-surface);
    border: 1px solid var(--c-trait);
    border-radius: var(--rayon-l);
    padding: var(--espace-6) var(--espace-5);
    box-shadow: var(--ombre-2);
}

.connexion h1 {
    font-size: var(--pas-2xl);
    margin: 0 0 var(--espace-5);
}

.connexion-marque {
    display: block;
    text-align: center;
    margin: 0 0 var(--espace-5);
}

/* ---------------------------------------------------------------------------
 * 17. Préférences utilisateur et impression
 * ------------------------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {
    html {
        scroll-behavior: auto;
    }

    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
}

@media (prefers-contrast: more) {
    :root {
        --c-encre-doux: #443f39;
        --c-trait: #b8ae9f;
        --c-trait-fort: #6e6558;
    }
}

@media print {
    .entete,
    .bandeau,
    .pied,
    .evitement,
    .actions {
        display: none !important;
    }

    body {
        background: #fff;
        color: #000;
        font-size: 11pt;
    }

    .carte,
    .tableau-defilant,
    .chiffre {
        box-shadow: none;
        border-color: #999;
    }

    a {
        color: #000;
    }
}

/* ---------------------------------------------------------------------------
 * 18. Marqueurs de relecture éditoriale — TEMPORAIRE
 *
 * docs/contenu-vitrine.md laisse deux marqueurs dans les textes de la vitrine :
 * `[À CONFIRMER : …]` (donnée factuelle manquante — tarif, parcours, règle
 * d'annulation) et `[À VALIDER : …]` (formulation que Cécile doit relire).
 * Ils sont rendus à l'écran, et non cachés dans un commentaire HTML : les cinq
 * pages ne sont pas publiables en l'état, elles doivent le montrer à qui les
 * relit.
 *
 * La version bloc réemployait `.alerte--erreur` — pastille « ! » brique sur
 * voile brique. La page tarifs en compte sept : à l'écran, elle ne se lisait
 * pas comme une page en attente de validation, mais comme une page en panne.
 * Depuis juillet 2026 elle a son propre registre (ci-dessous) : hachures à
 * -35°, l'angle de la marque. Les marqueurs restent visibles — c'est une
 * contrainte, pas une préférence ; ils ne changent que de langage.
 *
 * ⚠️ Section jetable. Le jour où le dernier marqueur disparaît des gabarits,
 * elle se supprime avec `partials/marqueurs.html.twig` et
 * `partials/_marqueur.html.twig`.
 * ------------------------------------------------------------------------ */

/* Étiquette « À confirmer » / « À valider ». Écrite en toutes lettres : c'est
   elle qui distingue les deux marqueurs, pas leur teinte. */
.marqueur-etiquette {
    font-size: var(--pas-xs);
    font-weight: var(--graisse-forte);
    text-transform: uppercase;
    letter-spacing: var(--interlettre-surtitre);
}

/* Marqueur en bloc : « chantier éditorial », et non « erreur système ».
   Les hachures à -35° sont l'angle de la marque — un signe qui n'existe nulle
   part ailleurs sur le site, donc impossible à confondre avec un état de
   l'application. */
.marqueur-bloc {
    padding: var(--espace-3) var(--espace-4);
    margin: 0 0 var(--espace-5);
    font-size: var(--pas-s);
    color: var(--c-encre);
    background: var(--c-voile-papier);
    border-radius: 0 var(--rayon-m) var(--rayon-m) 0;
    border-left: 6px solid transparent;
    border-image: repeating-linear-gradient(
        -35deg,
        var(--c-brique) 0 4px,
        var(--c-voile-brique) 4px 8px
    ) 6;
}

/* « À valider » n'est pas bloquant : la hachure passe en neutre. Même signe,
   même angle — c'est l'étiquette en toutes lettres qui porte la différence,
   la teinte ne fait que la doubler. */
.marqueur-bloc--valider {
    border-image: repeating-linear-gradient(
        -35deg,
        var(--c-trait-fort) 0 4px,
        var(--c-voile-papier) 4px 8px
    ) 6;
}

.marqueur-bloc--valider .marqueur-etiquette {
    color: var(--c-encre-doux);
}

/* Dans un bloc, l'étiquette tient lieu d'intitulé : elle précède le texte. */
.marqueur-bloc .marqueur-etiquette {
    display: block;
    margin: 0 0 var(--espace-1);
    color: var(--c-brique-fonce);
}

.marqueur-bloc ul {
    margin: var(--espace-2) 0 0;
    padding-left: var(--espace-5);
}

.marqueur-bloc > :last-child {
    margin-bottom: 0;
}

/* Marqueur en ligne : un trou au milieu d'une phrase. `<mark>` porte le sens,
   la mise en forme reste celle du système — pas de jaune de navigateur.
   `box-decoration-break` conserve le fond et les coins quand le marqueur passe
   à la ligne, ce qui arrive vite à 375 px. */
.marqueur {
    padding: 0.1em 0.4em;
    font-size: var(--pas-s);
    background: var(--c-voile-papier);
    color: var(--c-encre-doux);
    border-radius: var(--rayon-s);
    -webkit-box-decoration-break: clone;
    box-decoration-break: clone;
}

.marqueur .marqueur-etiquette {
    margin-right: var(--espace-1);
}

/* Donnée manquante : voile brique. 7,0:1 — le couple le plus contrasté de la
   charte, parce que c'est ce qui bloque la mise en ligne. */
.marqueur--confirmer {
    background: var(--c-voile-brique);
    color: var(--c-brique-fonce);
}
