/* ==========================================================================================
   RESALLE — feuille de style applicative
   Commune de Villeneuve-sur-Lay — © SAS HUBAHUB
   ------------------------------------------------------------------------------------------
   REGISTRE VISUEL : « institutionnel affirmé ». Échelle typographique nette, bleu
   institutionnel réservé à l'action et à l'identité, aplats sobres, filets fins, très peu
   d'ombres, angles discrets. L'écran doit rester lisible projeté en salle du conseil et
   imprimable tel quel.

   RÈGLE DE BLOCS (canonique §6.1) : ce fichier est partagé par les sept lots.
   Le lot 0 écrit le SYSTÈME DE DESIGN ci-dessous (jetons, socle, composants `.rs-*`) ; chaque
   lot ajoute ses propres règles d'écran dans sa feuille `lotN.css`, et nulle part ailleurs.

   Bootstrap 5.3.8 est consommé TEL QUEL depuis l'importmap : aucune surcharge SCSS, aucun
   thème acheté, aucune police distante. La personnalisation passe exclusivement par les
   variables CSS natives `--bs-*` de Bootstrap et par nos jetons `--rs-*`.

   Le contrat d'emploi de ces classes est docs/spec/70-SYSTEME-DE-DESIGN.md.
   ========================================================================================== */

/* ------------------------------------------------------------------------------------------
   Feuilles par lot — un fichier par lot, aucune écriture concurrente.
   Les `@import` sont en tête (contrainte CSS) : les règles de lot sont donc posées AVANT le
   socle. Un lot qui doit primer sur le socle le dit par une sélectivité plus forte.
   ------------------------------------------------------------------------------------------ */
/* Thème du portail public pour le prospect Caudry — couche additive, retirable telle quelle.
   Elle ne s'applique qu'à `.rs-zone-publique` : le back-office et le socle sont intacts. */
@import url("caudry-aPQuMGd.css");
@import url("lot1-4aCuMoO.css");
@import url("lot2-rPwZbcE.css");
@import url("lot3-ytVa3Vm.css");
@import url("lot4-NaeFb_l.css");
@import url("lot5-KBrLpeq.css");
@import url("lot6-GE_2_TR.css");

/* ==========================================================================================
   1. PALETTE ET JETONS DE MARQUE (30-UI-ROUTES §9.2)
   ========================================================================================== */
:root {
    /* Bleu institutionnel — couleur d'action et d'identité */
    --rs-bleu-900: #0F3057;   /* bandeaux, titres de section          contraste 13,3:1 sur blanc */
    --rs-bleu-700: #1B4F8C;   /* --bs-primary                         contraste  8,2:1 sur blanc */
    --rs-bleu-500: #2E6FB7;   /* survol, liens visités                                            */
    --rs-bleu-100: #DDE8F4;   /* fonds de sélection, ligne active                                 */
    --rs-bleu-050: #F2F7FC;   /* fond de tableau alterné, barre latérale active                   */

    /* --- Neutres : DU PAPIER, PAS DU BLANC D'ÉCRAN -------------------------------------------
       Le registre était juste sur le papier et faux à l'écran, et la cause tenait à ces six
       lignes. Les trois familles typographiques étaient déjà en place — le serif énonce, la
       chasse fixe compte — mais elles s'exerçaient sur un blanc pur posé sur un gris bleuté :
       la texture d'un composant applicatif, pas celle d'un registre communal. Une lettre de
       labeur sur du #FFFFFF ne fait pas de l'encre sur du papier ; elle fait du texte dans une
       boîte.

       Les neutres passent donc du côté chaud. Le geste est délibérément SOBRE : on vise le
       papier d'un arrêté municipal, pas le parchemin. La chromie reste imperceptible en tant
       que couleur — on ne doit pas pouvoir nommer la teinte du fond — et ne se lit que comme
       l'absence de la froideur d'écran qui était là avant.

       L'ENCRE, ELLE, RESTE FROIDE. C'est ce qui distingue ce registre d'un pastiche
       d'ancien : le texte et le bleu institutionnel ne bougent pas d'un point, seul leur
       support change. Le contraste du couple encre/papier est recalculé ci-dessous, et aucun
       ne descend sous le seuil AAA qu'il tenait sur le blanc. */
    --rs-ardoise-800: #2B3440; /* texte principal                    contraste 12,2:1 sur papier */
    --rs-ardoise-600: #495867; /* texte secondaire                   contraste  7,0:1 sur papier */
    --rs-ardoise-300: #C6C2B6; /* bordures — le filet chaud du registre                          */
    --rs-ardoise-100: #E9E5DC; /* fonds neutres                                                  */
    --rs-papier:      #FBFAF6; /* surface d'un panneau — un papier, jamais #FFFFFF               */
    --rs-fond:        #EFECE4; /* fond d'application — la table sur laquelle le panneau est posé */

    /* Sémantiques — toutes vérifiées avec texte blanc, sauf l'ambre (texte sombre) */
    --rs-vert-700:   #1E7A45;  /* succès, accordé, réglé              blanc  5,4:1  */
    --rs-ambre-500:  #F0A202;  /* attention, en attente, retard       #1B1B1B 8,1:1 */
    --rs-rouge-700:  #B02A22;  /* refus, incident critique, impayé    blanc  6,6:1  */
    --rs-indigo-600: #4A4E9B;  /* information, en cours               blanc  6,8:1  */

    /* --- Déclinaisons pâles et texte appuyé de chaque ton -----------------------------------
       Un aplat pâle porte TOUJOURS son texte appuyé : les couples ci-dessous sont mesurés
       entre 7,3:1 et 10,4:1, donc AAA. Aucune autre teinte n'est introduite ailleurs. */
    --rs-aplat-bleu:     #EEF4FB;  --rs-trait-bleu:     #B9D0E8;  --rs-encre-bleu:     #123A69;
    --rs-aplat-ardoise:  #EFEDE5;  --rs-trait-ardoise:  #D0CCC0;  --rs-encre-ardoise:  #33404D;
    --rs-aplat-vert:     #EAF5EE;  --rs-trait-vert:     #B7DCC6;  --rs-encre-vert:     #145730;
    --rs-aplat-ambre:    #FDF3E0;  --rs-trait-ambre:    #F6D9A0;  --rs-encre-ambre:    #6B4A02;
    --rs-aplat-rouge:    #FBEDEC;  --rs-trait-rouge:    #E8B6B2;  --rs-encre-rouge:    #7E1E18;
    --rs-aplat-indigo:   #EEEEF7;  --rs-trait-indigo:   #C0C2E2;  --rs-encre-indigo:   #33366D;
    --rs-aplat-encre:    #EAE7DE;  --rs-trait-encre:    #C9C5B9;  --rs-encre-encre:    #2B3440;
    /* L'encre du trio « papier » descend de #495867 à #414D5A : l'aplat ayant été réchauffé,
       le couple tombait à 6,5:1 — encore AA, mais sous le seuil AAA que ce §2.2 revendique
       pour TOUS ses trios. On corrige la teinte plutôt que la promesse. Mesuré : 7,7:1. */
    --rs-aplat-papier:   #F4F2EA;  --rs-trait-papier:   #DCD8CC;  --rs-encre-papier:   #414D5A;

    /* ======================================================================================
       2. ÉCHELLE TYPOGRAPHIQUE — modulaire, raison ≈ 1,15 à 1,25, base 16 px.
       Un titre est un titre, une donnée est une donnée : on n'emploie JAMAIS une taille
       littérale dans une feuille de lot, seulement les alias sémantiques ci-dessous.
       ====================================================================================== */
    --rs-texte-100: .75rem;     /* 12 px */
    --rs-texte-200: .8125rem;   /* 13 px */
    --rs-texte-300: .875rem;    /* 14 px */
    --rs-texte-400: 1rem;       /* 16 px — pas de base */
    --rs-texte-500: 1.125rem;   /* 18 px */
    --rs-texte-600: 1.3125rem;  /* 21 px */
    --rs-texte-700: 1.5rem;     /* 24 px */
    --rs-texte-800: 1.875rem;   /* 30 px */
    --rs-texte-900: 2.5rem;     /* 40 px */

    /* Alias sémantiques — c'est CELA que l'on emploie. */
    --rs-texte-titre-page:      var(--rs-texte-700);
    --rs-texte-titre-page-hors: var(--rs-texte-800); /* portail public, page d'accueil */
    --rs-texte-titre-section:   var(--rs-texte-600);
    --rs-texte-titre-carte:     var(--rs-texte-500);
    --rs-texte-corps:           var(--rs-texte-400);
    --rs-texte-corps-dense:     var(--rs-texte-300);
    --rs-texte-libelle:         var(--rs-texte-200);
    --rs-texte-entete-colonne:  var(--rs-texte-100);
    --rs-texte-annotation:      var(--rs-texte-100);
    --rs-texte-donnee:          var(--rs-texte-400);
    --rs-texte-donnee-cle:      var(--rs-texte-800);
    --rs-texte-surtitre:        var(--rs-texte-100);

    /* Graisses — quatre valeurs, pas une de plus. */
    --rs-graisse-normale: 400;
    --rs-graisse-moyenne: 500;
    --rs-graisse-appuyee: 600;
    --rs-graisse-forte:   700;

    /* Interlignages */
    --rs-interligne-titre:  1.2;
    --rs-interligne-donnee: 1.35;
    --rs-interligne-corps:  1.5;
    --rs-interligne-large:  1.65;  /* textes longs : règlement intérieur, aide */

    /* Interlettrages */
    --rs-interlettre-titre:  -.01em;
    --rs-interlettre-normal: 0;
    --rs-interlettre-capitales: .06em;

    /* ======================================================================================
       3. ÉCHELLE D'ESPACEMENT — pas de 4 px. Aucune marge arbitraire ailleurs.
       ====================================================================================== */
    --rs-espace-0: 0;
    --rs-espace-1: .25rem;   /*  4 px */
    --rs-espace-2: .5rem;    /*  8 px */
    --rs-espace-3: .75rem;   /* 12 px */
    --rs-espace-4: 1rem;     /* 16 px */
    --rs-espace-5: 1.5rem;   /* 24 px */
    --rs-espace-6: 2rem;     /* 32 px */
    --rs-espace-7: 3rem;     /* 48 px */
    --rs-espace-8: 4rem;     /* 64 px */

    /* Rythme vertical unique : un seul intervalle entre deux blocs de même niveau. */
    --rs-rythme:         var(--rs-espace-5);  /* entre deux panneaux, entre deux sections   */
    --rs-rythme-interne: var(--rs-espace-4);  /* entre deux rangées d'un même panneau       */
    --rs-rythme-serre:   var(--rs-espace-2);  /* entre un libellé et sa valeur              */

    /* ======================================================================================
       4. GÉOMÉTRIE
       ====================================================================================== */
    /* ANGLE PRESQUE VIF. Le rayon a été ramené de 4 px à 2 px : à cette valeur, il ne se lit
       plus comme une intention de douceur, seulement comme l'absence d'une arête coupante.
       C'est ce que R3 demandait depuis toujours — « angles discrets » — et que 4 px trahissait
       en donnant à chaque panneau un air de composant applicatif. */
    --rs-rayon:        .125rem;
    --rs-rayon-petit:  .125rem;
    --rs-filet:        1px;
    --rs-filet-appuye: 2px;
    --rs-filet-lateral: 3px;     /* qualification d'une ligne de tableau */
    --rs-hauteur-controle: 2.375rem;
    --rs-cible-tactile: 44px;

    /* Chrome d'un contrôle : marges internes gauche + droite + bordures. Sert au calibrage
       des largeurs de champ, exprimées en `ch` (largeur du chiffre 0).
       `--rs-chrome-selecteur` ajoute en outre la pastille que le navigateur pose lui-même sur
       un sélecteur de date, d'heure ou de liste : sans elle, la valeur serait rognée. */
    --rs-chrome-champ: 1.9rem;
    --rs-chrome-selecteur: 3.4rem;

    /* Largeurs de champ calibrées — la largeur ANNONCE ce que l'on saisit.
       Le nombre de `ch` est le nombre de caractères effectivement saisis, pas davantage. */
    --rs-champ-nombre:   calc(6ch  + var(--rs-chrome-champ));      /* 999                 */
    --rs-champ-cp:       calc(7ch  + var(--rs-chrome-champ));      /* 85000               */
    --rs-champ-montant:  calc(11ch + var(--rs-chrome-champ));      /* 12 345,67           */
    --rs-champ-code:     calc(14ch + var(--rs-chrome-champ));      /* SALLE_FETES         */
    --rs-champ-tel:      calc(16ch + var(--rs-chrome-champ));      /* 02 51 00 00 00      */
    --rs-champ-court:    calc(20ch + var(--rs-chrome-champ));
    --rs-champ-moyen:    calc(34ch + var(--rs-chrome-champ));
    --rs-champ-long:     calc(60ch + var(--rs-chrome-champ));
    --rs-champ-date:     calc(10ch + var(--rs-chrome-selecteur));  /* 31/12/2026          */
    --rs-champ-heure:    calc(5ch  + var(--rs-chrome-selecteur));  /* 23:00               */
    --rs-champ-duree:    calc(6ch  + var(--rs-chrome-selecteur));  /* 2 h 30              */
    --rs-champ-datetime: calc(16ch + var(--rs-chrome-selecteur));  /* 31/12/2026 23:00    */

    /* Géométrie du calendrier (lot 1) */
    --rs-hauteur-creneau: 1.75rem;   /* hauteur d'une ligne de 30 minutes */
    --rs-largeur-echelle: 4.5rem;    /* colonne des heures                */
    --rs-largeur-barre-laterale: 248px;

    /* ======================================================================================
       5. RÉAFFECTATION DES VARIABLES BOOTSTRAP
       ====================================================================================== */
    --bs-primary: var(--rs-bleu-700);
    --bs-primary-rgb: 27, 79, 140;
    --bs-secondary: var(--rs-ardoise-600);
    --bs-secondary-rgb: 73, 88, 103;
    --bs-success: var(--rs-vert-700);
    --bs-success-rgb: 30, 122, 69;
    --bs-warning: var(--rs-ambre-500);
    --bs-warning-rgb: 240, 162, 2;
    --bs-danger: var(--rs-rouge-700);
    --bs-danger-rgb: 176, 42, 34;
    --bs-info: var(--rs-indigo-600);
    --bs-info-rgb: 74, 78, 155;
    --bs-light: var(--rs-ardoise-100);
    --bs-light-rgb: 237, 240, 243;
    --bs-dark: var(--rs-ardoise-800);
    --bs-dark-rgb: 43, 52, 64;

    --bs-body-color: var(--rs-ardoise-800);
    --bs-body-color-rgb: 43, 52, 64;
    --bs-body-bg: var(--rs-fond);
    --bs-body-font-size: var(--rs-texte-corps);
    --bs-body-line-height: var(--rs-interligne-corps);
    --bs-secondary-color: var(--rs-ardoise-600);
    --bs-secondary-color-rgb: 73, 88, 103;
    --bs-heading-color: var(--rs-bleu-900);
    --bs-border-color: var(--rs-ardoise-300);
    --bs-link-color: var(--rs-bleu-700);
    --bs-link-color-rgb: 27, 79, 140;
    --bs-link-hover-color: var(--rs-bleu-900);
    --bs-border-radius: var(--rs-rayon);
    --bs-border-radius-sm: var(--rs-rayon-petit);
    --bs-border-radius-lg: var(--rs-rayon);
    --bs-focus-ring-color: rgba(27, 79, 140, .28);
    --bs-font-sans-serif: var(--rs-police-corps);

    /* Bootstrap laisse `<code>` en #d63384 — un rose qui n'est dans aucune table du §2 et qui
       peignait seize écrans, dont les empreintes SHA-256 du journal d'audit. */
    --bs-code-color: var(--rs-encre-bleu);
}

/* ==========================================================================================
   5 bis. LES TROIS FAMILLES — et ce que chacune dit

   UNE SEULE FAMILLE NE SAIT PAS DISTINGUER UN TITRE D'UNE DONNÉE. C'est ce qui manquait :
   l'écran était juste, réglé, contrasté — et uniformément gris de texture. Trois familles
   séparent trois natures, et cette séparation fait la moitié du travail de hiérarchie que la
   taille et la graisse faisaient seules jusqu'ici.

   — Le SERIF énonce. Titres et intitulés d'écran. C'est la lettre de l'arrêté, de la
     délibération, du registre communal : dans une mairie, ce n'est pas un ornement, c'est le
     registre juste. Il donne au titre une autorité que le sans-serif gras cherchait à obtenir
     par la seule masse.
   — Le SANS énonce le courant. Corps, contrôles, navigation. Rien ne change ici.
   — La CHASSE FIXE compte. Montants, dates, références, compteurs. Elle aligne les colonnes
     à la verticale et donne au chiffre le poids d'une écriture portée au grand livre.

   AUCUNE POLICE DISTANTE (interdit du §10) : les trois piles sont locales et se dégradent
   proprement, la dernière valeur étant toujours le générique de la famille.
   ========================================================================================== */
:root {
    --rs-police-corps: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial,
                       "Noto Sans", sans-serif;
    --rs-police-titre: "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua",
                       "Liberation Serif", "DejaVu Serif", Georgia, "Times New Roman", serif;
    --rs-police-chiffre: ui-monospace, "SF Mono", "Cascadia Mono", "DejaVu Sans Mono",
                         "Liberation Mono", "Roboto Mono", Menlo, Consolas, monospace;
}

/* Aucune autre couleur n'est introduite. Le mode sombre n'est PAS livré : il n'est pas demandé
   au contrat, et une demi-implémentation nuirait à la lisibilité des badges de statut. */

/* ==========================================================================================
   6. SOCLE TYPOGRAPHIQUE — l'échelle s'applique d'office, sans classe à poser.
   ========================================================================================== */
body {
    font-size: var(--rs-texte-corps);
    line-height: var(--rs-interligne-corps);
    letter-spacing: var(--rs-interlettre-normal);
    -webkit-font-smoothing: antialiased;
}

h1, .h1, h2, .h2, h3, .h3, h4, .h4, h5, .h5, h6, .h6 {
    font-family: var(--rs-police-titre);
    line-height: var(--rs-interligne-titre);
    letter-spacing: var(--rs-interlettre-titre);
    font-weight: var(--rs-graisse-appuyee);
    color: var(--rs-bleu-900);
    text-wrap: balance;
}

h1, .h1 { font-size: var(--rs-texte-titre-page-hors); font-weight: var(--rs-graisse-forte); }
h2, .h2 { font-size: var(--rs-texte-titre-section); }
h3, .h3 { font-size: var(--rs-texte-titre-carte); }
h4, .h4 { font-size: var(--rs-texte-corps); }
h5, .h5 { font-size: var(--rs-texte-corps); }
h6, .h6 { font-size: var(--rs-texte-corps-dense); letter-spacing: var(--rs-interlettre-normal); }

/* Le back-office est dense : le titre d'écran y est plus mesuré que sur le portail. */
.rs-zone-admin h1,
.rs-zone-admin .h1 { font-size: var(--rs-texte-titre-page); }

/* `small` et `.small` portent l'annotation, pas un second corps de texte. */
small, .small { font-size: var(--rs-texte-annotation); }

/* Textes longs (règlement intérieur, aide, mentions) : interlignage de lecture. */
.rs-texte-long {
    line-height: var(--rs-interligne-large);
    max-width: 68ch;
}

/* Surtitre : étiquette courte au-dessus d'un titre. Capitales, jamais en couleur vive. */
.rs-surtitre {
    display: block;
    font-size: var(--rs-texte-surtitre);
    font-weight: var(--rs-graisse-appuyee);
    letter-spacing: var(--rs-interlettre-capitales);
    text-transform: uppercase;
    color: var(--rs-ardoise-600);
    margin-bottom: var(--rs-espace-1);
}

/* Annotation : mention discrète sous une donnée ou dans un pied de carte. */
.rs-annotation {
    font-size: var(--rs-texte-annotation);
    line-height: var(--rs-interligne-corps);
    color: var(--rs-ardoise-600);
}

/* ------------------------------------------------------------------------------------------
   Chiffres tabulaires — tout ce qui est montant, date, référence, heure ou compteur.
   Sans quoi une colonne de nombres ne s'aligne pas d'une ligne à l'autre.
   ------------------------------------------------------------------------------------------ */
.rs-donnee,
.rs-reference,
.rs-date,
.rs-heure,
.rs-compteur,
.rs-montant, td.rs-montant, th.rs-montant,
.rs-nombre, td.rs-nombre, th.rs-nombre,
time,
input[type='number'],
input[type='date'],
input[type='time'],
input[type='datetime-local'],
[data-largeur='cp'],
[data-largeur='code'],
[data-largeur='nombre'],
[data-largeur='montant'] {
    font-variant-numeric: tabular-nums;
    font-feature-settings: 'tnum' 1;
}

/* La chasse fixe sur ce qui se COMPTE — jamais sur ce qui se saisit. Un champ en chasse fixe
   casse le rythme d'un formulaire et signale à tort une donnée technique ; la valeur affichée,
   elle, gagne à s'aligner colonne par colonne comme une écriture de grand livre. */
.rs-donnee,
.rs-reference,
.rs-date,
.rs-heure,
.rs-compteur,
.rs-montant, td.rs-montant, th.rs-montant,
.rs-nombre, td.rs-nombre, th.rs-nombre,
time {
    font-family: var(--rs-police-chiffre);
    letter-spacing: var(--rs-interlettre-normal);
}

/* UN MONTANT S'ALIGNE À DROITE DANS UNE COLONNE, PAS DANS UNE FICHE.

   Les sélecteurs nus `.rs-montant` et `.rs-nombre` portaient `text-align: right` partout : la
   valeur d'une `<dd>` de fiche, d'une tuile ou d'un `<span>` de donnée-clef fuyait au bord
   droit de son conteneur, à cinq ou six cents pixels de l'intitulé qu'elle complète. Le socle
   avait déjà buté dessus et posé une rustine locale à l'intérieur du registre (§18) ; elle
   traitait un symptôme.

   Le §3.4 ne prescrit l'alignement à droite QUE dans un tableau, et les variantes `td.`/`th.`
   étaient déjà écrites juste à côté. On retire donc les deux sélecteurs nus de la seule règle
   d'alignement. La chasse fixe et l'insécabilité, elles, restent globales : elles valent pour
   un montant où qu'il soit. */
td.rs-montant, th.rs-montant,
td.rs-nombre, th.rs-nombre {
    text-align: right;
}

.rs-montant, .rs-nombre {
    white-space: nowrap;
}

/* Une date et une heure ne se coupent pas davantage qu'un montant : « 11/08/ » sur une ligne
   et « 2026 » sur la suivante se relit deux fois. */
.rs-date, .rs-heure { white-space: nowrap; }

/* Référence de dossier, de facture, de titre : lue en colonne, jamais coupée. */
.rs-reference {
    white-space: nowrap;
    font-weight: var(--rs-graisse-appuyee);
}

/* Donnée numérique mise en avant (tuile de pilotage, compteur d'écran). */
.rs-donnee-cle {
    display: block;
    font-family: var(--rs-police-chiffre);
    font-size: var(--rs-texte-donnee-cle);
    font-weight: var(--rs-graisse-forte);
    line-height: var(--rs-interligne-titre);
    letter-spacing: var(--rs-interlettre-titre);
    font-variant-numeric: tabular-nums;
    color: var(--rs-bleu-900);
}

/* ==========================================================================================
   6 bis. LE RYTHME ENTRE BLOCS APPARTIENT À LA COLONNE, PAS AUX COMPOSANTS

   La même relation « un filtre, puis ses résultats » valait 0, 24 ou 32 px selon l'écran. Trois
   mécanismes se disputaient la marge, aucun n'était le même :

     — `.rs-panneau:last-child { margin-bottom: 0 }` s'appliquait parce que chaque panneau est
       seul dans son `<form>` ; la règle visait un panneau en fin de CARTE, elle attrapait un
       panneau en fin de PAGE ;
     — `.rs-formulaire { margin-bottom: 0 }` retirait la sienne ;
     — et surtout `turbo-frame` est un élément inconnu du navigateur, donc calculé
       `display: inline` : **il n'accepte aucune marge verticale**. Deux panneaux encadrés
       chacun d'un filet d'un pixel se retrouvaient bord à bord, et l'on ne lisait plus qu'un
       seul objet coupé par une barre.

   On cesse donc de confier le rythme aux composants : c'est la colonne de contenu qui l'impose
   à ses enfants directs, une fois, uniformément. Le portail respire à 32 px, le back-office —
   dense par métier — à 24.
   ========================================================================================== */
main turbo-frame { display: block; }

.rs-zone-publique main > * + * { margin-top: var(--rs-espace-6); }
.rs-zone-admin main > * + * { margin-top: var(--rs-rythme); }

/* La marge basse revient à la colonne : un composant ne pousse plus son voisin. */
main > .rs-panneau,
main > .rs-formulaire,
main > turbo-frame > .rs-panneau:last-child { margin-bottom: 0; }

/* ------------------------------------------------------------------------------------------
   LA CIBLE TACTILE VAUT POUR TOUTE LA RANGÉE, PAS SEULEMENT POUR LE BOUTON.

   `--rs-cible-tactile` vaut 44 px et les boutons l'appliquent ; les cases à cocher mesuraient
   17 × 17 px dans une rangée de 24, et les champs 38 px de haut alors qu'ils portaient déjà la
   classe. Deux hauteurs dans une même rangée cassent la ligne de base à l'œil, et une case de
   17 px se rate au doigt. La règle est posée hors requête de média : un portable tactile ne se
   déclare pas toujours en `pointer: coarse`.
   ------------------------------------------------------------------------------------------ */
.form-control.rs-cible-tactile,
.form-select.rs-cible-tactile { min-height: var(--rs-cible-tactile); }

/* ------------------------------------------------------------------------------------------
   LE CONTOUR DU CHAMP DOIT SE VOIR.

   Le trait des contrôles reprenait `--rs-ardoise-300`, mesuré à **1,54:1** sur le fond
   d'application — bien sous le seuil de 3:1 que la norme impose au contour d'un composant
   d'interface. Un champ vide ne se distinguait du papier que par une nuance ; sur une
   projection ou un écran mal réglé, le formulaire n'avait plus de forme du tout.

   La palette du §2 étant fermée, on ne compose pas une teinte intermédiaire : on prend le
   jeton existant qui satisfait la règle, `--rs-ardoise-600` (6,87:1). À un pixel, il se lit
   comme un filet ferme, pas comme un cadre — c'est exactement le registre.
   ------------------------------------------------------------------------------------------ */
.form-control,
.form-select { border-color: var(--rs-ardoise-600); }

/* ==========================================================================================
   7. ACCESSIBILITÉ ET STRUCTURE (30-UI-ROUTES §10)
   ========================================================================================== */

/* Anneau de focus UNIQUE : anneau bleu à l'extérieur, halo clair à l'intérieur. Le halo rend
   l'anneau visible même posé sur un aplat bleu, où la couleur d'anneau se confondrait. */
:focus-visible {
    outline: var(--rs-filet-lateral) solid var(--rs-bleu-700);
    outline-offset: var(--rs-filet-lateral);
    box-shadow: 0 0 0 var(--rs-filet-lateral) var(--rs-papier);
}

/* Sur un fond bleu institutionnel, l'anneau s'inverse : clair au-dehors, bleu au-dedans. */
.rs-bandeau-institutionnel :focus-visible,
.rs-inverse :focus-visible {
    outline-color: var(--rs-papier);
    box-shadow: 0 0 0 var(--rs-filet-lateral) var(--rs-bleu-900);
}

/* Un contrôle de formulaire garde l'anneau au lieu de la lueur diffuse de Bootstrap. */
.form-control:focus,
.form-select:focus,
.form-check-input:focus {
    border-color: var(--rs-bleu-700);
    box-shadow: none;
}

/* ------------------------------------------------------------------------------------------
   L'ANNEAU DE FOCUS DOIT SURVIVRE À BOOTSTRAP — IL N'Y SURVIVAIT PAS.

   Le socle pose un anneau sur `:focus-visible`, spécificité (0,1,0). Bootstrap pose
   `outline: 0` sur `.btn:focus-visible`, spécificité (0,2,0) : il l'emportait. Un bouton
   atteint au CLAVIER n'avait donc aucun anneau — mesuré `outline: 0px none`, `box-shadow: none`
   — et l'usager qui tabule ne voyait plus où il se trouvait. C'est un manquement au critère
   RGAA « visibilité de la prise de focus », et c'est aussi ce qui rend la navigation au clavier
   désagréable : rien ne répond.

   On reprend donc la main à égalité de spécificité, sur tout ce qui reçoit le focus.
   ------------------------------------------------------------------------------------------ */
.btn:focus-visible,
.nav-link:focus-visible,
.page-link:focus-visible,
.form-control:focus-visible,
.form-select:focus-visible,
.form-check-input:focus-visible,
.dropdown-item:focus-visible,
summary:focus-visible {
    outline: var(--rs-filet-lateral) solid var(--rs-bleu-700);
    outline-offset: var(--rs-filet-lateral);
    box-shadow: 0 0 0 var(--rs-filet-lateral) var(--rs-papier);
}

/* Lien d'évitement — premier élément focusable, visible au focus seulement. */
.rs-lien-evitement {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 1080;
    padding: var(--rs-espace-3) var(--rs-espace-5);
    background: var(--rs-bleu-900);
    color: var(--rs-papier);
    text-decoration: none;
    font-weight: var(--rs-graisse-appuyee);
}

.rs-lien-evitement:focus {
    left: 0;
}

/* Cibles tactiles d'au moins 44 × 44 px : imposé sur pointeur grossier, où l'on vise mal. */
.rs-cible-tactile {
    min-width: var(--rs-cible-tactile);
    min-height: var(--rs-cible-tactile);
}

@media (pointer: coarse) {
    .btn,
    .nav-link,
    .page-link,
    .dropdown-item,
    .form-control,
    .form-select {
        min-height: var(--rs-cible-tactile);
    }

    .form-check-input {
        width: 1.25rem;
        height: 1.25rem;
        margin-top: .18rem;
    }
}

/* ==========================================================================================
   8. OSSATURE — bandeau, barre latérale, pied de page
   ========================================================================================== */
.rs-bandeau-institutionnel {
    background: var(--rs-bleu-900);
    color: var(--rs-papier);
    font-size: var(--rs-texte-corps-dense);
}

.rs-bandeau-institutionnel a { color: var(--rs-papier); }

/* Sur fond bleu, une annotation reprend l'encre du bandeau : la teinte grise du socle y
   tomberait à 2:1. On atténue par l'opacité, jamais par une couleur plus claire. */
.rs-bandeau-institutionnel .rs-annotation,
.rs-bandeau-institutionnel .rs-surtitre,
.rs-inverse .rs-annotation,
.rs-inverse .rs-surtitre {
    color: inherit;
    opacity: .85;
}

/* Conteneur du portail public : respiration généreuse, largeur de lecture confortable. */
.rs-conteneur-public { max-width: 1140px; }

/* Colonne de la barre latérale du back-office. */
.rs-barre-laterale {
    width: var(--rs-largeur-barre-laterale);
    flex: 0 0 var(--rs-largeur-barre-laterale);
    background: var(--rs-papier);
    border-right: var(--rs-filet) solid var(--rs-ardoise-300);
}

/* Navigation de module : entrées à hauteur constante, groupe annoncé par un surtitre. */
.rs-navigation .rs-navigation-groupe + .rs-navigation-groupe {
    margin-top: var(--rs-espace-4);
    padding-top: var(--rs-espace-4);
    border-top: var(--rs-filet) solid var(--rs-ardoise-100);
}

.rs-navigation-titre {
    font-size: var(--rs-texte-surtitre);
    font-weight: var(--rs-graisse-appuyee);
    letter-spacing: var(--rs-interlettre-capitales);
    text-transform: uppercase;
    color: var(--rs-ardoise-600);
    padding: 0 var(--rs-espace-4);
    margin-bottom: var(--rs-espace-2);
}

.rs-navigation .nav-link {
    font-size: var(--rs-texte-corps-dense);
    border-radius: 0;
    border-left: var(--rs-filet-lateral) solid transparent;
    color: var(--rs-ardoise-800);
}

.rs-navigation .nav-link:hover { background: var(--rs-aplat-papier); }

.rs-navigation .nav-link.actif,
.rs-navigation .nav-link[aria-current='page'] {
    background: var(--rs-bleu-050);
    border-left-color: var(--rs-bleu-700);
    color: var(--rs-bleu-900);
    font-weight: var(--rs-graisse-appuyee);
}

.rs-pied-de-page {
    background: var(--rs-papier);
    border-top: var(--rs-filet) solid var(--rs-ardoise-300);
    font-size: var(--rs-texte-annotation);
    color: var(--rs-ardoise-600);
}

/* ==========================================================================================
   9. EN-TÊTE DE PAGE — titre, sous-titre, compteur, actions
   ========================================================================================== */

/* Bloc d'en-tête d'écran : ce qui identifie la page à gauche, ce qui agit dessus à droite. */
.rs-entete-page {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--rs-espace-3) var(--rs-espace-5);
    padding-bottom: var(--rs-espace-3);
    margin-bottom: var(--rs-rythme);
    border-bottom: var(--rs-filet-appuye) solid var(--rs-ardoise-300);
}

/* Colonne d'identification : surtitre, titre, sous-titre, compteur. */
.rs-entete-page-identite { min-width: 0; }

/* Titre d'écran — un seul `<h1>` par page. */
.rs-titre-page {
    font-size: var(--rs-texte-titre-page);
    font-weight: var(--rs-graisse-forte);
    line-height: var(--rs-interligne-titre);
    letter-spacing: var(--rs-interlettre-titre);
    color: var(--rs-bleu-900);
    margin: 0;
}

/* Sous-titre : ce que la page montre, en une ligne, jamais une consigne. */
.rs-sous-titre {
    font-size: var(--rs-texte-corps-dense);
    color: var(--rs-ardoise-600);
    margin: var(--rs-espace-1) 0 0;
}

/* Compteur de résultats : « 56 dossiers · affichés 1 à 25 ». */
.rs-compteur-resultats {
    font-size: var(--rs-texte-corps-dense);
    color: var(--rs-ardoise-600);
    font-variant-numeric: tabular-nums;
    margin: var(--rs-espace-1) 0 0;
}

/* Groupe d'actions d'en-tête : un seul bouton primaire, le reste en contour. */
.rs-entete-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--rs-espace-2);
}

/* ==========================================================================================
   10. PANNEAU / CARTE DE SECTION
   ========================================================================================== */

/* Panneau : aplat papier, filet fin, angle discret, aucune ombre portée. */
.rs-panneau {
    background: var(--rs-papier);
    border: var(--rs-filet) solid var(--rs-ardoise-300);
    border-radius: var(--rs-rayon);
    margin-bottom: var(--rs-rythme);
}

.rs-panneau:last-child { margin-bottom: 0; }

/* En-tête de panneau : titre à gauche, actions locales à droite. */
.rs-panneau-entete {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--rs-espace-2) var(--rs-espace-4);
    padding: var(--rs-espace-3) var(--rs-espace-4);
    border-bottom: var(--rs-filet) solid var(--rs-ardoise-300);
    background: var(--rs-aplat-papier);
}

/* Titre de panneau. */
.rs-titre-carte {
    font-size: var(--rs-texte-titre-carte);
    font-weight: var(--rs-graisse-appuyee);
    line-height: var(--rs-interligne-titre);
    color: var(--rs-bleu-900);
    margin: 0;
}

/* Corps de panneau : c'est lui qui porte la respiration. */
.rs-panneau-corps { padding: var(--rs-espace-4); }

/* Un panneau qui contient un tableau ne met pas de marge autour : le tableau va bord à bord. */
.rs-panneau-corps--tableau { padding: 0; }

/* Pied de panneau : totaux, mentions, actions secondaires. */
.rs-panneau-pied {
    padding: var(--rs-espace-3) var(--rs-espace-4);
    border-top: var(--rs-filet) solid var(--rs-ardoise-300);
    background: var(--rs-aplat-papier);
    font-size: var(--rs-texte-corps-dense);
}

/* Variante sans cadre : un simple filet supérieur, pour empiler des sections sans les boîter. */
.rs-panneau--discret {
    background: transparent;
    border: 0;
    border-top: var(--rs-filet-appuye) solid var(--rs-ardoise-300);
    border-radius: 0;
    padding-top: var(--rs-espace-4);
}

/* Titre de section dans le flux, hors panneau. */
.rs-titre-section {
    font-size: var(--rs-texte-titre-section);
    font-weight: var(--rs-graisse-appuyee);
    line-height: var(--rs-interligne-titre);
    color: var(--rs-bleu-900);
    margin: var(--rs-rythme) 0 var(--rs-rythme-interne);
}

.rs-titre-section:first-child { margin-top: 0; }

/* Séparateur horizontal sobre. */
.rs-separateur {
    height: 0;
    border: 0;
    border-top: var(--rs-filet) solid var(--rs-ardoise-300);
    margin: var(--rs-rythme) 0;
    opacity: 1;
}

/* Séparateur portant un intitulé, pour couper une longue liste de champs. */
.rs-separateur-titre {
    display: flex;
    align-items: center;
    gap: var(--rs-espace-3);
    margin: var(--rs-rythme) 0 var(--rs-rythme-interne);
    font-size: var(--rs-texte-surtitre);
    font-weight: var(--rs-graisse-appuyee);
    letter-spacing: var(--rs-interlettre-capitales);
    text-transform: uppercase;
    color: var(--rs-ardoise-600);
}

.rs-separateur-titre::after {
    content: '';
    flex: 1 1 auto;
    border-top: var(--rs-filet) solid var(--rs-ardoise-300);
}

/* Pile verticale au rythme unique : évite les marges posées à la main. */
.rs-pile > * + * { margin-top: var(--rs-rythme); }
.rs-pile-serree > * + * { margin-top: var(--rs-rythme-interne); }

/* ==========================================================================================
   11. FORMULAIRES — rangée, libellé, aide, erreur, largeurs calibrées
   ========================================================================================== */

/* Un formulaire pose son propre rythme : ses rangées, ses groupes, sa barre d'actions. */
.rs-formulaire { margin-bottom: 0; }

/* Groupe de champs : un `<fieldset>` avec sa `<legend>`, jamais une simple carte muette. */
.rs-groupe-champs {
    background: var(--rs-papier);
    border: var(--rs-filet) solid var(--rs-ardoise-300);
    border-radius: var(--rs-rayon);
    padding: var(--rs-espace-4);
    margin-bottom: var(--rs-rythme);
    min-width: 0;
}

.rs-groupe-legende {
    float: none;             /* annule le comportement `legend` de Bootstrap Reboot */
    width: auto;
    padding: 0 var(--rs-espace-2);
    margin: 0 0 var(--rs-espace-4) calc(var(--rs-espace-2) * -1);
    font-size: var(--rs-texte-titre-carte);
    font-weight: var(--rs-graisse-appuyee);
    line-height: var(--rs-interligne-titre);
    color: var(--rs-bleu-900);
}

/* Grille de champs : colonnes fluides, jamais un `col-` posé au jugé. Chaque champ occupe
   la place que sa largeur calibrée réclame, et la grille se replie seule. */
.rs-grille-champs {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    gap: var(--rs-rythme-interne) var(--rs-espace-4);
    align-items: stretch;
}

.rs-grille-champs--serree { grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); }
.rs-grille-champs--large  { grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)); }

/*
   Modificateur d'appoint pour une `.row` Bootstrap dont les colonnes portent des rangées.
   La grille `.rs-grille-champs` reste la forme normale ; `.rs-rangees` sert aux écrans qui
   conservent leurs `col-` : il rend chaque colonne flexible, ce qui redonne à la rangée sa
   hauteur de 100 % et réaligne tous les contrôles sur une même ligne.
*/
.rs-rangees > [class*='col-'] {
    display: flex;
    flex-direction: column;
}

/* Un champ qui réclame deux colonnes de la grille, ou toute sa largeur. Ces deux classes
   commandent l'OCCUPATION de la grille ; la largeur du contrôle, elle, vient de
   `data-largeur`. Ne pas confondre `.rs-colonne-pleine` (occupe toute la rangée) et
   `[data-largeur='plein']` (le contrôle n'est pas bridé). */
.rs-colonne-double { grid-column: span 2; }
.rs-colonne-pleine { grid-column: 1 / -1; }

@media (max-width: 575.98px) {
    .rs-colonne-double { grid-column: 1 / -1; }
}

/* ------------------------------------------------------------------------------------------
   RANGÉE — le rythme qui tient quelle que soit la longueur du libellé.

   La rangée est une colonne flexible haute de 100 % de sa case de grille : la zone haute
   (libellé + aide) absorbe la place restante, si bien que TOUS les contrôles d'une même
   rangée s'alignent sur la même ligne, qu'un libellé tienne sur une ligne ou sur trois.
   C'est aussi pourquoi le texte d'aide est posé SOUS LE LIBELLÉ et non sous le champ :
   là-haut il est absorbé par la zone flexible ; là-dessous il décalerait les voisins.
   ------------------------------------------------------------------------------------------ */
.rs-rangee {
    display: flex;
    flex-direction: column;
    height: 100%;
    min-width: 0;
    margin-bottom: 0;
}

/* Zone haute : libellé puis aide. Elle grandit, le contrôle reste en bas. */
/* L'ESCALIER DES LIBELLÉS, ET COMMENT ON LE DESCEND.

   L'intitulé était calé sur le BAS de sa cellule. Dès qu'un champ d'une rangée portait deux
   lignes d'aide et pas son voisin, tout le mou se reversait AU-DESSUS du libellé : les
   contrôles restaient alignés, les libellés partaient en marches d'escalier — 40 px d'écart
   mesurés sur l'écran de prêt, 36 px sur la recherche de créneau. C'est le « ça flotte » à
   l'état pur, et le défaut le plus répété du produit.

   Le libellé se cale donc en haut, et c'est le CONTRÔLE qui absorbe le mou par le bas, où il
   ne se voit pas : les intitulés partagent une ligne, les champs aussi. */
.rs-rangee-intitule {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    margin-bottom: var(--rs-rythme-serre);
}

/* Libellé de champ : toujours visible, jamais un `placeholder`, jamais un `form-floating`. */
.rs-etiquette,
.rs-formulaire .form-label {
    display: block;
    font-size: var(--rs-texte-libelle);
    font-weight: var(--rs-graisse-appuyee);
    line-height: var(--rs-interligne-donnee);
    color: var(--rs-ardoise-800);
    margin-bottom: 0;
}

/* Astérisque du champ obligatoire : il accompagne la légende posée en tête de formulaire,
   il ne la remplace pas. Jamais rouge nu : la couleur n'est pas seule porteuse du sens.

   Les déclarations `text-decoration: none` et `border: 0` qui figuraient ici visaient le
   pointillé que Bootstrap Reboot accroche à `abbr[title]`. Elles ne pouvaient pas gagner :
   `abbr[title]` l'emporte en spécificité sur une classe. Le marqueur n'est plus un `<abbr>`,
   et le pointillé n'a plus lieu d'être combattu. */
.rs-obligatoire {
    color: var(--rs-rouge-700);
    font-weight: var(--rs-graisse-forte);
    margin-left: .15em;
}

/* Légende « * champ obligatoire », posée UNE FOIS par formulaire, avant le premier champ. */
.rs-mention-obligatoire {
    font-size: var(--rs-texte-annotation);
    color: var(--rs-ardoise-600);
    margin: 0 0 var(--rs-rythme-interne);
}

/* Texte d'aide : sous le libellé, au-dessus du champ, cité par `aria-describedby`. */
.rs-aide,
.rs-rangee .form-text {
    display: block;
    font-size: var(--rs-texte-annotation);
    line-height: var(--rs-interligne-corps);
    color: var(--rs-ardoise-600);
    margin: var(--rs-espace-1) 0 0;
}

/* Zone de contrôle : elle ne grandit pas, elle est le repère d'alignement de la rangée. */
.rs-rangee-controle {
    flex: 0 0 auto;
    margin-top: auto;
}

/* Message d'erreur : sous le champ, en `role="alert"`, avec son repère graphique. */
.rs-erreur,
.rs-rangee .invalid-feedback {
    display: block;
    font-size: var(--rs-texte-annotation);
    font-weight: var(--rs-graisse-moyenne);
    line-height: var(--rs-interligne-corps);
    color: var(--rs-encre-rouge);
    margin-top: var(--rs-espace-1);
}

/* Un message d'erreur VIDE ne porte pas de triangle. Le gabarit de formulaire émet la boîte
   `.rs-erreur` avant de savoir s'il y a quelque chose à dire ; sans cette garde, un triangle
   rouge apparaît seul sous un champ correct. `:empty::before { content: none }` plutôt que
   `display: none` : la classe voisine `.d-block` de Bootstrap porte `!important` et gagnerait. */
.rs-erreur:empty::before { content: none; }

.rs-erreur::before {
    content: '';
    display: inline-block;
    width: .8em;
    height: .8em;
    margin-right: .35em;
    vertical-align: -.05em;
    background: var(--rs-rouge-700);
    /* Triangle plein : le message reste identifiable en niveaux de gris. */
    clip-path: polygon(50% 0, 100% 100%, 0 100%);
}

/* Un champ en erreur porte un filet rouge appuyé, en plus de `aria-invalid`. */
.rs-rangee [aria-invalid='true'],
.form-control.is-invalid,
.form-select.is-invalid {
    border-color: var(--rs-rouge-700);
    border-width: var(--rs-filet-appuye);
    background-image: none;   /* l'icône de fond de Bootstrap écrase la valeur saisie */
    padding-right: var(--rs-espace-3);
}

/* ------------------------------------------------------------------------------------------
   CONTRÔLES
   ------------------------------------------------------------------------------------------ */
/* LA LIGNE `border-color` A ÉTÉ RETIRÉE D'ICI, ET C'EST LA CORRECTION LA PLUS LARGE DU SOCLE.

   Cinq cents lignes plus haut, un commentaire de dix lignes explique pourquoi le contour d'un
   champ doit être `--rs-ardoise-600` : `--rs-ardoise-300` se mesure à 1,54:1, très en dessous
   du seuil de 3:1 que la norme impose au contour d'un composant d'interface. La règle voulue
   est écrite là-haut — et elle était REPOSÉE ICI à sélectivité égale, plus bas dans le
   fichier, donc gagnante. Le contour de TOUS les champs de l'application valait 1,54:1 : la
   décision documentée était morte, écrasée par elle-même.

   On ne redéclare donc rien : on retire la déclaration fautive, et la règle du §7 s'applique
   enfin. */
.form-control,
.form-select {
    font-size: var(--rs-texte-corps-dense);
    min-height: var(--rs-hauteur-controle);
    color: var(--rs-ardoise-800);
}

.form-control::placeholder { color: var(--rs-ardoise-600); opacity: .75; }

.form-control:disabled,
.form-select:disabled {
    background: var(--rs-ardoise-100);
    color: var(--rs-ardoise-600);
}

/* Zone de texte : hauteur de lecture, jamais un champ d'une ligne étiré. */
textarea.form-control {
    min-height: 6.5rem;
    line-height: var(--rs-interligne-corps);
}

/* Sélecteur multiple : il montre ses choix sans obliger à faire défiler à l'aveugle. */
/* Un sélecteur multiple est une ÉNUMÉRATION dense, pas un champ de saisie : il est rendu un
   cran plus bas dans l'échelle, ce qui suffit à faire tenir « Pièces complémentaires
   attendues » dans une colonne de filtre sans le tronquer. */
select[multiple].form-select,
select[size].form-select {
    min-height: 9.5rem;
    padding: var(--rs-espace-1) var(--rs-espace-2);
    font-size: var(--rs-texte-libelle);
    line-height: var(--rs-interligne-donnee);
    background-image: none;
}

select[multiple].form-select option {
    padding: var(--rs-espace-1) var(--rs-espace-2);
    line-height: var(--rs-interligne-donnee);
}

/* Groupe de saisie : le suffixe (€, %, km) est un aplat neutre, pas un bouton. */
.input-group-text {
    font-size: var(--rs-texte-corps-dense);
    background: var(--rs-ardoise-100);
    border-color: var(--rs-ardoise-300);
    color: var(--rs-ardoise-600);
}

/* Case à cocher et bouton radio : alignés sur la ligne des contrôles voisins. */
/* La rangée d'une case vaut une cible tactile entière : la case mesurait 17 px dans une
   rangée de 20, quand le socle déclare 44 px et que les boutons voisins les respectent. Deux
   hauteurs dans une même rangée cassent la ligne de base à l'œil, et 17 px se ratent au doigt.
   On passe du calage par retrait négatif à un alignement franc. */
.form-check {
    display: flex;
    align-items: center;
    gap: var(--rs-espace-2);
    min-height: var(--rs-cible-tactile);
    margin-bottom: 0;
    padding-left: 0;
}

/* LA NEUTRALISATION ÉTAIT ÉCRITE, ET ELLE PERDAIT.

   `.form-check-input { margin: 0 }` pèse 0,1,0. Bootstrap déclare
   `.form-check .form-check-input { float: left; margin-left: -1.5em }`, qui pèse 0,2,0 et
   l'emporte quel que soit l'ordre des feuilles. La case était donc peinte 24 px À GAUCHE de sa
   boîte — hors du panneau sur douze écrans, à cheval sur le filet du fieldset, et sous le bord
   de la carte à 390 px. Le `padding-left: 1.5em` compensatoire de Bootstrap ayant été retiré
   juste au-dessus, plus rien ne rattrapait ce retrait.

   La règle est donc réécrite À SÉLECTIVITÉ ÉGALE et postérieure. `float: none` s'ajoute par
   sûreté : la case est déjà un élément flexible, où le flottement est ignoré, mais la
   déclaration dit ce qu'on veut au lecteur qui n'ira pas vérifier le contexte. */
.form-check > .form-check-input {
    width: 1.25rem;
    height: 1.25rem;
    float: none;
    margin: 0;
    flex: 0 0 auto;
    border-color: var(--rs-ardoise-600);
}

.form-check-input:checked {
    background-color: var(--rs-bleu-700);
    border-color: var(--rs-bleu-700);
}

.form-check-label {
    font-size: var(--rs-texte-corps-dense);
    line-height: var(--rs-interligne-donnee);
    color: var(--rs-ardoise-800);
}

/* Une case seule dans une grille de champs : sa zone haute reste vide, la case vient
   s'aligner à la hauteur des champs voisins au lieu de flotter en haut de la case. */
.rs-rangee--case .rs-rangee-controle {
    display: flex;
    align-items: center;
    min-height: var(--rs-hauteur-controle);
}

/* Rangée portant un groupe de cases : le `<fieldset>` n'a ni cadre ni fond, seule sa légende
   le signale — le cadre serait redondant avec celui du panneau qui le contient. */
.rs-rangee--groupe {
    border: 0;
    padding: 0;
    margin: 0;
    min-width: 0;
}

/* Rangée ne portant qu'un bouton : c'est la barre d'actions qui espace les boutons. */
.rs-rangee-bouton { display: inline-flex; }

/* Groupe de cases ou de radios : un `<fieldset>` avec sa légende, cases empilées au pas serré. */
.rs-groupe-cases {
    display: flex;
    flex-direction: column;
    gap: var(--rs-espace-2);
    padding: var(--rs-espace-2) 0 0;
    min-width: 0;
}

.rs-groupe-cases--en-ligne {
    flex-direction: row;
    flex-wrap: wrap;
    gap: var(--rs-espace-2) var(--rs-espace-5);
}

/* CE BLOC A ÉTÉ RETIRÉ, ET C'EST UNE LEÇON À GARDER.

   Il neutralisait le retrait négatif de Bootstrap sous `.rs-groupe-cases` seulement — la
   gouttière de la recherche rapide, où le défaut avait été VU. Le même défaut existait sur
   douze autres écrans, sans être vu, parce qu'on avait corrigé là où l'on regardait plutôt
   qu'à la cause. La cause est traitée à `.form-check > .form-check-input`, plus haut, et ce
   bloc n'a plus lieu d'être — son `min-height: 0` allait jusqu'à défaire la cible tactile de
   44 px que le socle rétablit désormais partout. */

/* (bloc retiré — voir ci-dessus) */
/* LA GOUTTIÈRE DE 24 px ÉTAIT MANGÉE PAR BOOTSTRAP, ET SE VOYAIT.

   Sur la recherche rapide de l'accueil, les deux cases se touchaient : on lisait
   « Cuisine équipée☐ Accessible aux personnes… », la case de la seconde collée au libellé de
   la première. La gouttière était pourtant bien déclarée juste au-dessus.

   `.form-check` de Bootstrap réserve la place de sa case par un `padding-left: 1.5em`, puis
   fait SORTIR la case de la boîte par `float: left; margin-left: -1.5em`. La case est donc
   peinte 1,5 em À GAUCHE du bord de l'élément flexible — c'est-à-dire dans la gouttière, qui
   vaut précisément `--rs-espace-5`, soit 1,5 rem. Les deux valeurs s'annulent, et l'espace
   déclaré n'existe plus à l'écran.

   On ne compense pas en élargissant la gouttière — cela laisserait la case hors de sa boîte,
   et le défaut réapparaîtrait à la moindre autre valeur. On retire la cause : dans un groupe
   de cases, `.form-check` devient une rangée flexible ordinaire et sa case rentre dans la
   boîte. Le repère visuel de Bootstrap disparaît, l'alignement redevient celui qu'on déclare. */

/* ------------------------------------------------------------------------------------------
   LARGEURS DE CHAMP CALIBRÉES — la largeur annonce ce que l'on saisit.

   Deux écritures équivalentes, l'une et l'autre reconnues :
     * `attr: { 'data-largeur': 'cp' }` posé dans le FormType — À PRIVILÉGIER, car un gabarit
       qui repasse `attr: { class: 'form-control' }` écrase la classe mais jamais l'attribut ;
     * classe `.rs-champ-cp` posée dans le gabarit, quand le champ n'est pas rendu par un
       FormType.
   ------------------------------------------------------------------------------------------ */
/* Calibrage d'appoint, tant qu'un FormType n'a pas déclaré son `data-largeur` : un champ qui
   annonce déjà sa longueur maximale n'a aucune raison d'occuper la largeur d'une adresse.
   `:where()` neutralise la sélectivité de `.form-control` : ces règles pèsent 0-1-0 et cèdent
   donc à `[data-largeur]`, déclaré juste après, qui reste la convention. Au-delà de douze
   caractères, la longueur ne renseigne plus assez : la largeur n'est pas contrainte. */
:where(.form-control)[maxlength='1'],
:where(.form-control)[maxlength='2'],
:where(.form-control)[maxlength='3'] { max-width: var(--rs-champ-nombre); }

:where(.form-control)[maxlength='4'],
:where(.form-control)[maxlength='5'] { max-width: var(--rs-champ-cp); }

:where(.form-control)[maxlength='6'],
:where(.form-control)[maxlength='7'],
:where(.form-control)[maxlength='8'],
:where(.form-control)[maxlength='9'],
:where(.form-control)[maxlength='10'],
:where(.form-control)[maxlength='11'],
:where(.form-control)[maxlength='12'] { max-width: var(--rs-champ-code); }

.rs-champ-code,    [data-largeur='code']    { max-width: var(--rs-champ-code); }
.rs-champ-cp,      [data-largeur='cp']      { max-width: var(--rs-champ-cp); }
.rs-champ-nombre,  [data-largeur='nombre']  { max-width: var(--rs-champ-nombre); text-align: right; }
.rs-champ-montant, [data-largeur='montant'] { max-width: var(--rs-champ-montant); text-align: right; }
.rs-champ-date,    [data-largeur='date']    { max-width: var(--rs-champ-date); }
.rs-champ-datetime,[data-largeur='datetime']{ max-width: var(--rs-champ-datetime); }
.rs-champ-heure,   [data-largeur='heure']   { max-width: var(--rs-champ-heure); }
.rs-champ-duree,   [data-largeur='duree']   { max-width: var(--rs-champ-duree); }
.rs-champ-tel,     [data-largeur='tel']     { max-width: var(--rs-champ-tel); }
.rs-champ-court,   [data-largeur='court']   { max-width: var(--rs-champ-court); }
.rs-champ-moyen,   [data-largeur='moyen']   { max-width: var(--rs-champ-moyen); }
.rs-champ-long,    [data-largeur='long']    { max-width: var(--rs-champ-long); }
.rs-champ-plein,   [data-largeur='plein']   { max-width: none; width: 100%; }

/* Dans un groupe de saisie, la largeur porte sur le groupe entier, suffixe compris. */
.input-group.rs-champ-montant,
.input-group[data-largeur='montant'] { max-width: calc(var(--rs-champ-montant) + 3.5rem); }

.input-group .form-control[data-largeur],
.input-group .form-control[class*='rs-champ-'] { max-width: none; }

/* ------------------------------------------------------------------------------------------
   BARRE D'ACTIONS DE FORMULAIRE — collante en bas, pour ne pas dérouler jusqu'au bout.
   ------------------------------------------------------------------------------------------ */
/* LA BARRE D'ACTIONS NE COLLE PLUS AU BAS DE LA FENÊTRE.

   Ancrée en `sticky; bottom: 0` avec un fond opaque, elle survolait par construction le
   contenu de son propre formulaire : sur l'assistant, elle tranchait « Fréquence de la
   répétition » en son milieu ; à 390 px, elle recouvrait cinq éléments de l'écran de prêt,
   dont un champ de date.

   Le remède qu'on lit partout — réserver un `padding-bottom` au formulaire — a été essayé et
   MESURÉ SANS EFFET : réserver de la place à la FIN d'un formulaire ne protège pas son MILIEU.
   Une barre docké est simplement le mauvais patron pour un formulaire plus haut que la
   fenêtre. Elle reprend donc sa place dans le flux, à la fin, là où l'on arrive quand on a
   fini de remplir.

   Son filet supérieur redevient un filet ordinaire : le trait bleu appuyé annonce une section
   qui s'ouvre, or celle-ci ferme. */
.rs-barre-actions {
    position: static;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--rs-espace-2) var(--rs-espace-4);
    margin-top: var(--rs-rythme);
    padding: var(--rs-espace-3) var(--rs-espace-4);
    background: var(--rs-papier);
    border: var(--rs-filet) solid var(--rs-ardoise-300);
    border-radius: var(--rs-rayon);
    border-top: var(--rs-filet) solid var(--rs-ardoise-300);
}

/* Boutons de la barre : le principal d'abord, les secondaires ensuite. */
.rs-barre-actions-groupe {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--rs-espace-2);
}

/* Mention poussée à droite : rappel d'enregistrement, dernière modification. */
.rs-barre-actions-mention {
    margin-left: auto;
    font-size: var(--rs-texte-annotation);
    color: var(--rs-ardoise-600);
}

@media (max-width: 575.98px) {
    .rs-barre-actions { flex-direction: column; align-items: stretch; }
    .rs-barre-actions .btn { width: 100%; }
    .rs-barre-actions-mention { margin-left: 0; }
}

/* ==========================================================================================
   12. BOUTONS — le bleu institutionnel est la couleur de l'action
   ========================================================================================== */
.btn {
    --bs-btn-font-size: var(--rs-texte-corps-dense);
    --bs-btn-font-weight: var(--rs-graisse-appuyee);
    --bs-btn-padding-y: .4375rem;
    --bs-btn-padding-x: var(--rs-espace-3);
    --bs-btn-border-radius: var(--rs-rayon);
    letter-spacing: var(--rs-interlettre-normal);
}

.btn-primary {
    --bs-btn-bg: var(--rs-bleu-700);
    --bs-btn-border-color: var(--rs-bleu-700);
    --bs-btn-hover-bg: var(--rs-bleu-900);
    --bs-btn-hover-border-color: var(--rs-bleu-900);
    --bs-btn-active-bg: var(--rs-bleu-900);
    --bs-btn-active-border-color: var(--rs-bleu-900);
    --bs-btn-disabled-bg: var(--rs-bleu-700);
    --bs-btn-disabled-border-color: var(--rs-bleu-700);
}

.btn-outline-primary {
    --bs-btn-color: var(--rs-bleu-700);
    --bs-btn-border-color: var(--rs-bleu-700);
    --bs-btn-hover-bg: var(--rs-bleu-700);
    --bs-btn-hover-border-color: var(--rs-bleu-700);
    --bs-btn-active-bg: var(--rs-bleu-900);
    --bs-btn-active-border-color: var(--rs-bleu-900);
    --bs-btn-disabled-color: var(--rs-bleu-700);
    --bs-btn-disabled-border-color: var(--rs-bleu-700);
}

.btn-secondary,
.btn-outline-secondary {
    --bs-btn-color: var(--rs-ardoise-800);
    --bs-btn-border-color: var(--rs-ardoise-300);
    --bs-btn-hover-color: var(--rs-ardoise-800);
    --bs-btn-hover-bg: var(--rs-ardoise-100);
    --bs-btn-hover-border-color: var(--rs-ardoise-600);
    --bs-btn-active-color: var(--rs-ardoise-800);
    --bs-btn-active-bg: var(--rs-ardoise-100);
    --bs-btn-active-border-color: var(--rs-ardoise-600);
    --bs-btn-disabled-color: var(--rs-ardoise-600);
    --bs-btn-disabled-border-color: var(--rs-ardoise-300);
}

.btn-secondary {
    --bs-btn-bg: var(--rs-papier);
    --bs-btn-disabled-bg: var(--rs-ardoise-100);
}

.btn-danger {
    --bs-btn-bg: var(--rs-rouge-700);
    --bs-btn-border-color: var(--rs-rouge-700);
    --bs-btn-hover-bg: #8E211B;
    --bs-btn-hover-border-color: #8E211B;
    --bs-btn-active-bg: #8E211B;
    --bs-btn-active-border-color: #8E211B;
    --bs-btn-disabled-bg: var(--rs-rouge-700);
    --bs-btn-disabled-border-color: var(--rs-rouge-700);
}

.btn-outline-danger {
    --bs-btn-color: var(--rs-encre-rouge);
    --bs-btn-border-color: var(--rs-rouge-700);
    --bs-btn-hover-bg: var(--rs-rouge-700);
    --bs-btn-hover-border-color: var(--rs-rouge-700);
    --bs-btn-active-bg: var(--rs-rouge-700);
    --bs-btn-active-border-color: var(--rs-rouge-700);
}

/* Le vert d'action ne sert qu'à confirmer un acte irréversible favorable (accorder, valider). */
.btn-success {
    --bs-btn-bg: var(--rs-vert-700);
    --bs-btn-border-color: var(--rs-vert-700);
    --bs-btn-hover-bg: #176036;
    --bs-btn-hover-border-color: #176036;
    --bs-btn-active-bg: #176036;
    --bs-btn-active-border-color: #176036;
    --bs-btn-disabled-bg: var(--rs-vert-700);
    --bs-btn-disabled-border-color: var(--rs-vert-700);
}

/* Le jaune n'est jamais un bouton : un avertissement ne s'actionne pas. */
.btn-warning {
    --bs-btn-color: var(--rs-encre-ambre);
    --bs-btn-bg: var(--rs-aplat-ambre);
    --bs-btn-border-color: var(--rs-trait-ambre);
    --bs-btn-hover-color: var(--rs-encre-ambre);
    --bs-btn-hover-bg: #FBE9C8;
    --bs-btn-hover-border-color: var(--rs-ambre-500);
    --bs-btn-active-color: var(--rs-encre-ambre);
    --bs-btn-active-bg: #FBE9C8;
    --bs-btn-active-border-color: var(--rs-ambre-500);
}

/* Bouton de lien : mêmes proportions qu'un bouton, aucun aplat. */
.btn-link {
    --bs-btn-color: var(--rs-bleu-700);
    --bs-btn-hover-color: var(--rs-bleu-900);
    text-decoration: underline;
    text-underline-offset: .15em;
}

/* Un bouton désactivé reste lisible : la raison de son inaction est écrite à côté. */
.btn:disabled,
.btn.disabled { opacity: .55; }

/* ==========================================================================================
   13. STATUTS — badges et pastilles
   ========================================================================================== */

/* Badge de statut : aplat pâle, texte appuyé de la même teinte, filet fin de la même famille.
   Tous les tons ont ainsi le MÊME contraste apparent (7,3:1 à 10,4:1) et la même hauteur.
   Le libellé est écrit en toutes lettres : la couleur ne porte jamais le sens à elle seule. */
.rs-badge-statut {
    display: inline-flex;
    align-items: center;
    gap: .4em;
    max-width: 22ch;              /* borne le badge : plus de pavé sur une seule ligne */
    /* Le badge se replie sur plusieurs lignes s'il le faut, mais jamais AU MILIEU d'un mot :
       « Conven- / tion envoyée » se relit deux fois. */
    overflow-wrap: normal;
    word-break: normal;
    padding: .25em .55em;
    border: var(--rs-filet) solid var(--rs-trait-ardoise);
    border-radius: var(--rs-rayon-petit);
    background: var(--rs-aplat-ardoise);
    color: var(--rs-encre-ardoise);
    font-size: var(--rs-texte-annotation);
    font-weight: var(--rs-graisse-appuyee);
    line-height: var(--rs-interligne-donnee);
    letter-spacing: var(--rs-interlettre-normal);
    text-align: left;
    white-space: normal;          /* un long libellé passe à la ligne, il ne s'étire pas */
    text-wrap: balance;
    vertical-align: middle;
}

/* La puce reprend la teinte pleine : repère de balayage, jamais l'unique porteur du sens. */
.rs-badge-statut::before {
    content: '';
    flex: 0 0 auto;
    width: .5em;
    height: .5em;
    border-radius: 50%;
    background: currentColor;
}

/* Le badge neutralise `text-bg-*` de Bootstrap s'il en porte encore un : sélectivité 0-2-0. */
.rs-badge-statut.text-bg-primary,
.rs-badge-statut.text-bg-secondary,
.rs-badge-statut.text-bg-success,
.rs-badge-statut.text-bg-danger,
.rs-badge-statut.text-bg-warning,
.rs-badge-statut.text-bg-info,
.rs-badge-statut.text-bg-light,
.rs-badge-statut.text-bg-dark { background-color: transparent !important; }

.rs-badge-statut.text-bg-primary,   .rs-badge--primary   { background: var(--rs-aplat-bleu)    !important; color: var(--rs-encre-bleu)    !important; border-color: var(--rs-trait-bleu); }
.rs-badge-statut.text-bg-secondary, .rs-badge--secondary { background: var(--rs-aplat-ardoise) !important; color: var(--rs-encre-ardoise) !important; border-color: var(--rs-trait-ardoise); }
.rs-badge-statut.text-bg-success,   .rs-badge--success   { background: var(--rs-aplat-vert)    !important; color: var(--rs-encre-vert)    !important; border-color: var(--rs-trait-vert); }
.rs-badge-statut.text-bg-danger,    .rs-badge--danger    { background: var(--rs-aplat-rouge)   !important; color: var(--rs-encre-rouge)   !important; border-color: var(--rs-trait-rouge); }
.rs-badge-statut.text-bg-warning,   .rs-badge--warning   { background: var(--rs-aplat-ambre)   !important; color: var(--rs-encre-ambre)   !important; border-color: var(--rs-trait-ambre); }
.rs-badge-statut.text-bg-info,      .rs-badge--info      { background: var(--rs-aplat-indigo)  !important; color: var(--rs-encre-indigo)  !important; border-color: var(--rs-trait-indigo); }
.rs-badge-statut.text-bg-light,     .rs-badge--light     { background: var(--rs-aplat-papier)  !important; color: var(--rs-encre-papier)  !important; border-color: var(--rs-trait-papier); }
.rs-badge-statut.text-bg-dark,      .rs-badge--dark      { background: var(--rs-aplat-encre)   !important; color: var(--rs-encre-encre)   !important; border-color: var(--rs-trait-encre); }

/* Badge de liste dense. */
.rs-badge-petit {
    font-size: var(--rs-texte-100);
    padding: .15em .45em;
    max-width: 18ch;
}

/* Un statut terminal se distingue d'un dossier vivant : la puce se creuse. */
.rs-badge-terminal::before {
    background: transparent;
    box-shadow: inset 0 0 0 2px currentColor;
}

/* Pastille de statut : la teinte pleine suivie du libellé, en liste très dense, là où le
   badge serait trop lourd. */
.rs-puce-statut {
    display: inline-flex;
    align-items: center;
    gap: var(--rs-espace-2);
    font-size: var(--rs-texte-corps-dense);
    line-height: var(--rs-interligne-donnee);
}

.rs-puce {
    flex: 0 0 auto;
    width: .625rem;
    height: .625rem;
    border-radius: 50%;
    background: var(--rs-ardoise-600);
}

.rs-puce--primary   { background: var(--rs-bleu-700); }
.rs-puce--secondary { background: var(--rs-ardoise-600); }
.rs-puce--success   { background: var(--rs-vert-700); }
.rs-puce--danger    { background: var(--rs-rouge-700); }
.rs-puce--warning   { background: var(--rs-ambre-500); }
.rs-puce--info      { background: var(--rs-indigo-600); }
.rs-puce--light     { background: var(--rs-ardoise-300); }
.rs-puce--dark      { background: var(--rs-ardoise-800); }

/* Compteur : le seul objet arrondi en pilule de l'application. La forme le distingue du
   badge de statut, qui est rectangulaire. */
.rs-compteur,
.badge.rounded-pill {
    font-size: var(--rs-texte-100);
    font-weight: var(--rs-graisse-forte);
    font-variant-numeric: tabular-nums;
    padding: .2em .5em;
    min-width: 1.7em;
    text-align: center;
}

/* Frise d'avancement : les places franchies, la place courante, les places restantes. */
.rs-frise {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--rs-espace-2);
    list-style: none;
    padding: 0;
    margin: 0;
}

.rs-frise-place[aria-current='step'] {
    border-width: var(--rs-filet-appuye);
    font-weight: var(--rs-graisse-forte);
}

.rs-frise-place--restante {
    background: transparent !important;
    color: var(--rs-ardoise-600) !important;
    border-style: dashed;
}

/* ==========================================================================================
   14. TABLEAUX DE DONNÉES
   ========================================================================================== */

/* Conteneur : le tableau défile horizontalement DANS sa boîte, la page ne défile jamais. */
.rs-tableau-conteneur,
.table-responsive {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

/* Marqueur d'un tableau de DONNÉES, par opposition à un tableau de mise en page (planning,
   grille tarifaire), qui a ses propres règles de lot. Il accompagne toujours `.table`. */
.rs-tableau { width: 100%; border-collapse: collapse; }

.table {
    --bs-table-bg: var(--rs-papier);
    --bs-table-color: var(--rs-ardoise-800);
    --bs-table-border-color: var(--rs-ardoise-100);
    --bs-table-hover-bg: var(--rs-bleu-050);
    --bs-table-hover-color: var(--rs-ardoise-800);
    --bs-table-striped-bg: var(--rs-aplat-papier);
    --bs-table-striped-color: var(--rs-ardoise-800);
    --bs-table-active-bg: var(--rs-bleu-100);
    --bs-table-active-color: var(--rs-ardoise-800);
    font-size: var(--rs-texte-corps-dense);
    margin-bottom: 0;
}

/* En-tête de colonne : capitales fines, filet appuyé dessous. On distingue au premier coup
   d'œil un en-tête d'une donnée — c'est le premier étage de la hiérarchie. */
.table > thead > tr > th,
.rs-entete-colonne {
    font-size: var(--rs-texte-entete-colonne);
    font-weight: var(--rs-graisse-appuyee);
    letter-spacing: var(--rs-interlettre-capitales);
    text-transform: uppercase;
    color: var(--rs-ardoise-600);
    background: var(--rs-aplat-papier);
    border-bottom: var(--rs-filet-appuye) solid var(--rs-ardoise-300);
    vertical-align: bottom;
    padding-top: var(--rs-espace-2);
    padding-bottom: var(--rs-espace-2);
}

.table > thead > tr > th a {
    color: inherit;
    text-decoration: none;
}

.table > thead > tr > th a:hover { color: var(--rs-bleu-700); text-decoration: underline; }

/* Cellules : respiration constante, alignement haut, filet de séparation discret.

   `vertical-align` disait `middle` quatre lignes sous un commentaire qui annonce l'alignement
   haut. Sur une ligne d'une seule ligne de texte, les deux se valent ; sur une ligne haute de
   cinq cents pixels — une colonne de conditions, une liste d'étapes — le calage centré fait
   flotter l'identifiant loin du filet supérieur, et l'œil ne sait plus quelle valeur va avec
   quelle ligne. On rend donc au code ce que le commentaire promettait. */
.table > tbody > tr > td,
.table > tbody > tr > th {
    padding: var(--rs-espace-2) var(--rs-espace-3);
    vertical-align: top;
    border-bottom-color: var(--rs-ardoise-100);
}

.table > thead > tr > th { padding-left: var(--rs-espace-3); padding-right: var(--rs-espace-3); }

/* Tableau dense : listes longues projetées en séance. */
.rs-tableau--dense > tbody > tr > td,
.rs-tableau--dense > thead > tr > th { padding: var(--rs-espace-1) var(--rs-espace-2); }

/* Cellule principale : le libellé qui identifie la ligne, et son complément en annotation. */
.rs-cellule-principale {
    font-weight: var(--rs-graisse-appuyee);
    color: var(--rs-bleu-900);
    text-decoration: none;
}

.rs-cellule-principale:hover { text-decoration: underline; }

.rs-cellule-complement {
    display: block;
    font-size: var(--rs-texte-annotation);
    color: var(--rs-ardoise-600);
    line-height: var(--rs-interligne-donnee);
}

/* Colonnes de nombres : à droite, en-tête compris — sinon rien ne s'aligne. */
.table th.rs-montant,
.table th.rs-nombre,
.table th.rs-colonne-nombre,
.table td.rs-colonne-nombre {
    text-align: right;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* ------------------------------------------------------------------------------------------
   QUALIFICATION D'UNE LIGNE — deux canaux ORTHOGONAUX, donc cumulables sans conflit :

     1. le FILET latéral gauche (`.rs-filet-*`) porte la gravité : retard, conflit, impayé ;
     2. l'APLAT très pâle (`.rs-aplat-*`) porte la nature de la ligne : information,
        ligne à compléter, ligne archivée, ligne de total.

   Une ligne peut donc porter un filet rouge ET un aplat ambre pâle sans qu'aucun des deux
   ne se perde. Plus jamais d'aplat jaune vif où tout a l'air d'une alerte.
   ------------------------------------------------------------------------------------------ */
.rs-filet-alerte    > *:first-child { box-shadow: inset var(--rs-filet-lateral) 0 0 var(--rs-rouge-700); }
.rs-filet-attention > *:first-child { box-shadow: inset var(--rs-filet-lateral) 0 0 var(--rs-ambre-500); }
.rs-filet-info      > *:first-child { box-shadow: inset var(--rs-filet-lateral) 0 0 var(--rs-indigo-600); }
.rs-filet-succes    > *:first-child { box-shadow: inset var(--rs-filet-lateral) 0 0 var(--rs-vert-700); }
.rs-filet-neutre    > *:first-child { box-shadow: inset var(--rs-filet-lateral) 0 0 var(--rs-ardoise-600); }

/* Aplats de ligne : 3 à 5 % de teinte, pas davantage. Ils passent par `--bs-table-bg` afin
   de rester compatibles avec le survol et l'alternance de Bootstrap. */
.rs-aplat-alerte    { --bs-table-bg: var(--rs-aplat-rouge); }
.rs-aplat-attention { --bs-table-bg: var(--rs-aplat-ambre); }
.rs-aplat-info      { --bs-table-bg: var(--rs-aplat-indigo); }
.rs-aplat-succes    { --bs-table-bg: var(--rs-aplat-vert); }
.rs-aplat-neutre    { --bs-table-bg: var(--rs-aplat-papier); }

/* Ligne sélectionnée : c'est un état d'interaction, pas une qualification métier. */
.rs-ligne-selectionnee { --bs-table-bg: var(--rs-bleu-100); }

/* Ligne inactive : archivée, close, sans suite. Elle s'efface sans devenir illisible. */
/* 0,2,1, ET C'EST NÉCESSAIRE. Bootstrap pose la couleur des cellules par
   `.table > :not(caption) > * > *`, qui pèse 0,1,1 — `:not(caption)` comptant pour un
   sélecteur de type. Une classe seule (0,1,0) perdait donc TOUJOURS, quel que soit l'ordre des
   feuilles, et les trente lignes closes du produit — dossiers clôturés, brouillons jamais
   déposés — étaient rendues exactement comme les lignes vivantes, pendant qu'une légende
   annonçait le repère. */
.table > tbody > tr.rs-ligne-inactive > * { color: var(--rs-ardoise-600); }

/* Ligne de total : la somme se distingue d'une ligne de détail au premier coup d'œil. */
.rs-ligne-total > * {
    border-top: var(--rs-filet-appuye) solid var(--rs-ardoise-300);
    font-weight: var(--rs-graisse-appuyee);
    background: var(--rs-aplat-papier);
}

/* `table-warning` et `table-danger` subsistent dans quelques gabarits : on les ramène au
   traitement sobre plutôt que de laisser un aplat vif faire passer tout un tableau pour une
   alerte. La règle est de même sélectivité que celle de Bootstrap, et posée après elle. */
.table-warning {
    --bs-table-color: var(--rs-ardoise-800);
    --bs-table-bg: var(--rs-aplat-ambre);
    --bs-table-border-color: var(--rs-trait-ambre);
    --bs-table-striped-bg: var(--rs-aplat-ambre);
    --bs-table-striped-color: var(--rs-ardoise-800);
    --bs-table-active-bg: #FAECD3;
    --bs-table-active-color: var(--rs-ardoise-800);
    --bs-table-hover-bg: #FAECD3;
    --bs-table-hover-color: var(--rs-ardoise-800);
    color: var(--bs-table-color);
}

.table-warning > *:first-child { box-shadow: inset var(--rs-filet-lateral) 0 0 var(--rs-ambre-500); }

.table-danger {
    --bs-table-color: var(--rs-ardoise-800);
    --bs-table-bg: var(--rs-aplat-rouge);
    --bs-table-border-color: var(--rs-trait-rouge);
    --bs-table-striped-bg: var(--rs-aplat-rouge);
    --bs-table-striped-color: var(--rs-ardoise-800);
    --bs-table-active-bg: #F7E2E0;
    --bs-table-active-color: var(--rs-ardoise-800);
    --bs-table-hover-bg: #F7E2E0;
    --bs-table-hover-color: var(--rs-ardoise-800);
}

.table-danger > *:first-child { box-shadow: inset var(--rs-filet-lateral) 0 0 var(--rs-rouge-700); }

/* Colonne des marques : première colonne étroite, une marque par qualification. Deux
   qualifications tiennent côte à côte, chacune avec son libellé pour le lecteur d'écran. */
.rs-colonne-marques {
    width: 1%;
    white-space: nowrap;
    padding-right: var(--rs-espace-2) !important;
}

.rs-marque {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.35rem;
    height: 1.35rem;
    border-radius: var(--rs-rayon-petit);
    font-size: var(--rs-texte-100);
    line-height: 1;
}

.rs-marque + .rs-marque { margin-left: var(--rs-espace-1); }

.rs-marque--alerte    { background: var(--rs-aplat-rouge);  color: var(--rs-encre-rouge); }
.rs-marque--attention { background: var(--rs-aplat-ambre);  color: var(--rs-encre-ambre); }
.rs-marque--info      { background: var(--rs-aplat-indigo); color: var(--rs-encre-indigo); }
.rs-marque--succes    { background: var(--rs-aplat-vert);   color: var(--rs-encre-vert); }
.rs-marque--neutre    { background: var(--rs-aplat-ardoise); color: var(--rs-encre-ardoise); }

/* ------------------------------------------------------------------------------------------
   LÉGENDE D'UN TABLEAU — sous le tableau qu'elle qualifie, jamais décrochée dans un coin.
   Elle dit CE QU'ELLE QUALIFIE et montre la marque telle qu'elle apparaît dans les lignes.
   ------------------------------------------------------------------------------------------ */
.rs-legende {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--rs-espace-2) var(--rs-espace-5);
    padding: var(--rs-espace-2) var(--rs-espace-3);
    border-top: var(--rs-filet) solid var(--rs-ardoise-300);
    background: var(--rs-aplat-papier);
    font-size: var(--rs-texte-annotation);
    color: var(--rs-ardoise-600);
    margin: 0;
}

.rs-legende-titre {
    font-weight: var(--rs-graisse-appuyee);
    color: var(--rs-ardoise-800);
    letter-spacing: var(--rs-interlettre-normal);
}

.rs-legende-liste {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--rs-espace-2) var(--rs-espace-5);
    list-style: none;
    padding: 0;
    margin: 0;
}

.rs-legende-item {
    display: inline-flex;
    align-items: center;
    gap: var(--rs-espace-2);
}

/* Échantillon de filet, tel qu'il apparaît en tête de ligne. */
.rs-legende-filet {
    display: inline-block;
    width: var(--rs-filet-lateral);
    height: 1rem;
    background: var(--rs-ardoise-600);
}

.rs-legende-filet--alerte    { background: var(--rs-rouge-700); }
.rs-legende-filet--attention { background: var(--rs-ambre-500); }
.rs-legende-filet--info      { background: var(--rs-indigo-600); }
.rs-legende-filet--succes    { background: var(--rs-vert-700); }

/* ==========================================================================================
   15. PANNEAU DE FILTRES — repliable, compact, avec compteur de filtres actifs
   ========================================================================================== */

/* `<details>` natif : aucun JavaScript, pliage au clavier, ouvert d'office à l'impression. */
.rs-filtres {
    background: var(--rs-papier);
    border: var(--rs-filet) solid var(--rs-ardoise-300);
    border-radius: var(--rs-rayon);
    margin-bottom: var(--rs-rythme);
}

.rs-filtres-resume {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--rs-espace-2) var(--rs-espace-3);
    padding: var(--rs-espace-3) var(--rs-espace-4);
    cursor: pointer;
    font-size: var(--rs-texte-corps-dense);
    font-weight: var(--rs-graisse-appuyee);
    color: var(--rs-bleu-900);
    list-style: none;
    min-height: var(--rs-cible-tactile);
}

.rs-filtres-resume::-webkit-details-marker { display: none; }

/* Chevron d'ouverture, tourné par l'état du `<details>`. */
.rs-filtres-resume::before {
    content: '';
    flex: 0 0 auto;
    width: .5rem;
    height: .5rem;
    border-right: var(--rs-filet-appuye) solid var(--rs-bleu-700);
    border-bottom: var(--rs-filet-appuye) solid var(--rs-bleu-700);
    transform: rotate(-45deg);
    transition: transform .12s linear;
}

.rs-filtres[open] > .rs-filtres-resume::before { transform: rotate(45deg); }

.rs-filtres[open] > .rs-filtres-resume {
    border-bottom: var(--rs-filet) solid var(--rs-ardoise-300);
}

/* Rappel du nombre de filtres actifs : sans lui, on ne sait pas pourquoi la liste est courte. */
.rs-filtres-compteur {
    font-size: var(--rs-texte-annotation);
    font-weight: var(--rs-graisse-appuyee);
    font-variant-numeric: tabular-nums;
    padding: .1em .5em;
    border-radius: 999px;
    background: var(--rs-bleu-100);
    color: var(--rs-encre-bleu);
}

/* Rappel en clair des critères posés, à droite du résumé. */
.rs-filtres-actifs {
    flex: 1 1 auto;
    min-width: 0;
    font-size: var(--rs-texte-annotation);
    font-weight: var(--rs-graisse-normale);
    color: var(--rs-ardoise-600);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.rs-filtres-corps { padding: var(--rs-espace-4); }

/* Grille de filtres : colonnes fluides de 13 rem. Onze contrôles tiennent sur trois rangs
   au lieu d'une colonne de 500 px avant la première donnée. */
.rs-filtres-grille {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
    gap: var(--rs-rythme-interne) var(--rs-espace-4);
    align-items: stretch;
}

/* Un filtre qui a besoin de deux colonnes : recherche libre, liste multiple de statuts. */
.rs-filtre-large { grid-column: span 2; }
.rs-filtre-plein { grid-column: 1 / -1; }

@media (max-width: 575.98px) {
    .rs-filtre-large { grid-column: 1 / -1; }
}

/* Les cases à cocher d'un panneau de filtres se regroupent sur une rangée dédiée. */
.rs-filtres-cases {
    grid-column: 1 / -1;
    display: flex;
    flex-wrap: wrap;
    gap: var(--rs-espace-2) var(--rs-espace-5);
    padding-top: var(--rs-espace-1);
}

.rs-filtres-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--rs-espace-2);
    padding: var(--rs-espace-3) var(--rs-espace-4);
    border-top: var(--rs-filet) solid var(--rs-ardoise-300);
    background: var(--rs-aplat-papier);
}

/* ==========================================================================================
   16. ÉTAT VIDE, CHARGEMENT, MESSAGES
   ========================================================================================== */

/* État vide : jamais un tableau à zéro ligne sans un mot d'explication et une sortie. */
.rs-etat-vide {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: var(--rs-espace-2);
    padding: var(--rs-espace-7) var(--rs-espace-4);
    color: var(--rs-ardoise-600);
}

.rs-etat-vide-icone {
    font-size: var(--rs-texte-800);
    line-height: 1;
    color: var(--rs-ardoise-300);
}

.rs-etat-vide-titre {
    font-size: var(--rs-texte-titre-carte);
    font-weight: var(--rs-graisse-appuyee);
    color: var(--rs-ardoise-800);
    margin: 0;
}

.rs-etat-vide-texte {
    max-width: 46ch;
    font-size: var(--rs-texte-corps-dense);
    margin: 0;
}

.rs-etat-vide-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--rs-espace-2);
    margin-top: var(--rs-espace-2);
}

/* Zone de messages flash : jamais masquée automatiquement pour les erreurs. */
#zone-messages { position: relative; }

.alert {
    border-radius: var(--rs-rayon);
    border-width: var(--rs-filet);
    border-left-width: var(--rs-filet-lateral);
    font-size: var(--rs-texte-corps-dense);
    padding: var(--rs-espace-3) var(--rs-espace-4);
    margin-bottom: var(--rs-rythme-interne);
}

.alert-success { --bs-alert-bg: var(--rs-aplat-vert);   --bs-alert-color: var(--rs-encre-vert);   --bs-alert-border-color: var(--rs-vert-700); }
.alert-warning { --bs-alert-bg: var(--rs-aplat-ambre);  --bs-alert-color: var(--rs-encre-ambre);  --bs-alert-border-color: var(--rs-ambre-500); }
.alert-danger  { --bs-alert-bg: var(--rs-aplat-rouge);  --bs-alert-color: var(--rs-encre-rouge);  --bs-alert-border-color: var(--rs-rouge-700); }
.alert-info    { --bs-alert-bg: var(--rs-aplat-indigo); --bs-alert-color: var(--rs-encre-indigo); --bs-alert-border-color: var(--rs-indigo-600); }
.alert-primary { --bs-alert-bg: var(--rs-aplat-bleu);   --bs-alert-color: var(--rs-encre-bleu);   --bs-alert-border-color: var(--rs-bleu-700); }
.alert-secondary { --bs-alert-bg: var(--rs-aplat-ardoise); --bs-alert-color: var(--rs-encre-ardoise); --bs-alert-border-color: var(--rs-ardoise-600); }

.alert-heading { color: inherit; font-size: var(--rs-texte-corps); }

/* Squelette de chargement d'un Turbo Frame. */
.rs-squelette {
    background: var(--rs-ardoise-100);
    border-radius: var(--rs-rayon-petit);
    min-height: 1rem;
    animation: rs-squelette 1.4s ease-in-out infinite;
}

@keyframes rs-squelette {
    0%, 100% { opacity: 1; }
    50%      { opacity: .45; }
}

/* ==========================================================================================
   17. FICHE DE DÉTAIL — couples terme / valeur
   ========================================================================================== */
/* LA COLONNE DE VALEUR NE PEUT PLUS ÊTRE ÉCRASÉE.

   Dans un panneau étroit — « En bref » de la fiche de salle, en colonne latérale — le terme
   prenait `max-content` (« Délai minimum de réservation », ~190 px) et la valeur se contentait
   du reste. On lisait alors « 60 pers / onne / s », « Salle non acce / ssibl / e », « 7 jour /
   s » : un mot par-ci, trois lettres par-là.

   La largeur minimale des DEUX colonnes est donc bornée. Le terme peut désormais passer à la
   ligne, ce qui est sans conséquence — c'est une étiquette, on la relit rarement — là où une
   valeur coupée en plein mot est illisible. */
.rs-fiche {
    display: grid;
    grid-template-columns: minmax(6rem, max-content) minmax(7rem, 1fr);
    gap: var(--rs-rythme-serre) var(--rs-espace-4);
    margin: 0;
}

.rs-fiche-terme {
    font-size: var(--rs-texte-libelle);
    font-weight: var(--rs-graisse-appuyee);
    color: var(--rs-ardoise-600);
    line-height: var(--rs-interligne-donnee);
    margin: 0;
}

.rs-fiche-valeur {
    font-size: var(--rs-texte-corps-dense);
    color: var(--rs-ardoise-800);
    line-height: var(--rs-interligne-donnee);
    margin: 0;
    min-width: 0;
    /* `break-word` ET NON `anywhere`, ET C'EST TOUTE LA DIFFÉRENCE. Les deux coupent un mot
       trop long pour sa ligne ; mais `anywhere` ramène en outre la largeur MINIMALE INTRINSÈQUE
       de l'élément à un caractère, ce qui autorise la grille à réduire la colonne à presque
       rien — et c'est ce qui déclenchait la coupure. `break-word` laisse le mot le plus long
       fixer la largeur minimale : la colonne garde sa place, et la protection contre un jeton
       insécable (adresse électronique, référence longue) reste acquise. */
    overflow-wrap: break-word;
}

.rs-fiche--empilee { grid-template-columns: 1fr; gap: var(--rs-espace-1); }

@media (max-width: 575.98px) {
    .rs-fiche { grid-template-columns: 1fr; gap: var(--rs-espace-1); }
    .rs-fiche-valeur { margin-bottom: var(--rs-espace-3); }
}

/* Tuile de synthèse : un intitulé, une donnée, une précision. Rien d'autre. */
.rs-tuile {
    display: flex;
    flex-direction: column;
    gap: var(--rs-espace-1);
    padding: var(--rs-espace-4);
    background: var(--rs-papier);
    border: var(--rs-filet) solid var(--rs-ardoise-300);
    border-top: var(--rs-filet-lateral) solid var(--rs-bleu-700);
    border-radius: var(--rs-rayon);
    height: 100%;
    text-decoration: none;
}

.rs-tuile-intitule {
    font-size: var(--rs-texte-surtitre);
    font-weight: var(--rs-graisse-appuyee);
    letter-spacing: var(--rs-interlettre-capitales);
    text-transform: uppercase;
    color: var(--rs-ardoise-600);
}

.rs-tuile-precision { font-size: var(--rs-texte-annotation); color: var(--rs-ardoise-600); }

/* ------------------------------------------------------------------------------------------
   Le REGISTRE — une grille de chiffres-clés d'un seul tenant.

   POURQUOI PAS DES TUILES QUI FLOTTENT. Cinq panneaux séparés par des gouttières, c'est cinq
   objets ; l'œil les compte avant de les lire. Un registre, c'est UN objet à cinq colonnes :
   on lit la ligne, pas les boîtes. C'est le geste qui donne leur calme aux pages d'un grand
   livre — et il ne coûte rien d'autre que de retirer les gouttières.

   LE FILET EST LA GOUTTIÈRE. Le fond du conteneur est la couleur du trait, les cellules
   portent le fond du papier, et l'écart d'un pixel entre elles laisse voir le trait. Aucun
   `border` sur les cellules : sans quoi il faudrait neutraliser un bord sur deux à chaque
   point de rupture, et le premier changement de grille rouvrirait le sujet.
   ------------------------------------------------------------------------------------------ */
.rs-registre {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--rs-filet);
    background: var(--rs-ardoise-300);
    border: var(--rs-filet) solid var(--rs-ardoise-300);
    border-top: var(--rs-filet-lateral) solid var(--rs-bleu-700);
}

.rs-registre > * { background: var(--rs-papier); }

@media (min-width: 576px) { .rs-registre { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 992px) { .rs-registre { grid-template-columns: repeat(3, 1fr); } }
/* Le nombre de colonnes est une CLASSE, jamais un style en ligne : la CSP `style-src` ne
   porte pas de nonce, et le §10 l'interdit. */
@media (min-width: 1200px) {
    .rs-registre            { grid-template-columns: repeat(4, 1fr); }
    .rs-registre--trois     { grid-template-columns: repeat(3, 1fr); }
    .rs-registre--cinq      { grid-template-columns: repeat(5, 1fr); }
    .rs-registre--six       { grid-template-columns: repeat(6, 1fr); }
}

/* Dans un registre, la tuile n'a plus de contour : le registre le porte pour elle. */
.rs-registre .rs-tuile {
    border: 0;
    border-radius: 0;
    height: 100%;
}

/* UN CHIFFRE-CLEF S'ALIGNE SOUS SON INTITULÉ, pas sur le bord droit de sa cellule.
   `.rs-montant` cale un montant à droite — c'est la bonne règle DANS UNE COLONNE DE TABLEAU,
   où l'on compare des unités entre elles ligne après ligne. Dans une cellule de registre il n'y
   a rien à comparer verticalement : le montant s'éloignait de son intitulé et le couple se
   lisait mal. Le nombre reste en chasse fixe, donc les cellules d'une même rangée continuent
   de s'aligner entre elles. */
.rs-registre .rs-donnee-cle,
.rs-registre .rs-tuile .rs-montant,
.rs-registre .rs-tuile .rs-nombre {
    text-align: left;
}

/* ==========================================================================================
   18. FIL D'ARIANE, PAGINATION, ONGLETS
   ========================================================================================== */
.breadcrumb {
    --bs-breadcrumb-divider: '›';
    --bs-breadcrumb-item-padding-x: var(--rs-espace-2);
    font-size: var(--rs-texte-annotation);
    margin-bottom: var(--rs-espace-3);
}

.breadcrumb-item.active { color: var(--rs-ardoise-600); }

/* LE CHEVRON ORPHELIN EN TÊTE DE FIL, SOUS 768 px — quatorze écrans, trois lignes.

   `_fil_ariane.html.twig` masque les segments de tête par `d-none d-md-inline` : ils sortent
   de l'affichage mais RESTENT DANS LE DOM. Or Bootstrap dessine le séparateur en `::before` du
   segment SUIVANT (`.breadcrumb-item + .breadcrumb-item::before`), et un frère en
   `display: none` reste un frère pour le combinateur `+`. Le premier segment encore visible
   héritait donc du chevron de celui qu'on venait de cacher, et le fil s'ouvrait sur « › ».

   Les segments masqués forment toujours un préfixe CONTIGU : il suffit d'annuler le séparateur
   du segment qui suit immédiatement un segment masqué. Sélectivité 0,3,1, postérieure : gagne
   sans `!important`. */
@media (max-width: 767.98px) {
    .breadcrumb-item.d-none + .breadcrumb-item::before {
        content: none;
        padding-right: 0;
    }
}

.page-link {
    color: var(--rs-bleu-700);
    font-size: var(--rs-texte-corps-dense);
    border-color: var(--rs-ardoise-300);
}

.page-item.active .page-link {
    background: var(--rs-bleu-700);
    border-color: var(--rs-bleu-700);
}

.nav-tabs {
    --bs-nav-tabs-border-color: var(--rs-ardoise-300);
    --bs-nav-link-color: var(--rs-ardoise-600);
    --bs-nav-tabs-link-active-color: var(--rs-bleu-900);
    --bs-nav-tabs-link-active-border-color: var(--rs-ardoise-300) var(--rs-ardoise-300) var(--rs-papier);
    font-size: var(--rs-texte-corps-dense);
    font-weight: var(--rs-graisse-appuyee);
}

/* ==========================================================================================
   19. MOUVEMENT RÉDUIT ET IMPRESSION
   ========================================================================================== */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: .001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .001ms !important;
        scroll-behavior: auto !important;
    }

    .rs-squelette { animation: none; }
}

/* L'interface doit pouvoir être projetée en conseil municipal ET imprimée sans retouche. */
@media print {
    /* Sur du vrai papier, le papier est celui de l'imprimante. `--rs-papier` a cessé d'être
       blanc quand le socle est passé au registre « encre sur papier » (R8) : sans cette ligne,
       chaque panneau imprimerait un aplat beige pleine page — de l'encre dépensée pour
       reproduire une texture que la feuille porte déjà. */
    :root {
        --rs-fond: #FFFFFF;
        --rs-papier: #FFFFFF;
    }

    .rs-barre-laterale,
    .rs-hors-impression,
    .rs-filtres-actions,
    .navbar,
    .pagination,
    .btn-close {
        display: none !important;
    }

    body {
        background: var(--rs-papier);
        font-size: 10pt;
        color: #000;
    }

    a { text-decoration: none; color: #000; }

    /* La barre d'actions ne colle pas au papier : elle reprend sa place dans le flux. */
    .rs-barre-actions { position: static; }

    /* Un `<details>` de filtres s'imprime ouvert : on doit lire les critères du tirage. */
    .rs-filtres[open] .rs-filtres-corps,
    .rs-filtres .rs-filtres-corps { display: block !important; }

    /* Les ombres ne s'impriment pas : les filets de qualification deviennent des bordures. */
    .rs-filet-alerte    > *:first-child { border-left: 3pt solid #000; }
    .rs-filet-attention > *:first-child { border-left: 3pt dashed #000; }
    .rs-filet-info      > *:first-child { border-left: 3pt dotted #000; }
    .rs-filet-succes    > *:first-child { border-left: 3pt double #000; }
    .table-warning      > *:first-child { border-left: 3pt dashed #000; }
    .table-danger       > *:first-child { border-left: 3pt solid #000; }

    .rs-panneau,
    .rs-groupe-champs,
    .rs-tuile { border-color: #999; break-inside: avoid; }

    thead { display: table-header-group; }
    tr { break-inside: avoid; }

    .rs-badge-statut {
        border-color: #000 !important;
        background: transparent !important;
        color: #000 !important;
    }
}

/* ==========================================================================================
   20. PIÈCES D'OSSATURE ET DE COMPOSANTS PARTAGÉS
   Ce que rendent `templates/base*.html.twig`, `templates/partials/*` et `templates/macros/*`.
   ========================================================================================== */

/* --- Charpente du back-office : barre latérale + colonne de contenu ----------------------- */
.rs-charpente {
    display: flex;
    align-items: stretch;
    min-height: 60vh;
}

.rs-charpente-corps {
    flex: 1 1 auto;
    min-width: 0;   /* sans quoi un tableau large pousse la page en défilement horizontal */
}

/* --- Bandeau institutionnel --------------------------------------------------------------- */
.rs-bandeau-corps {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--rs-espace-2) var(--rs-espace-4);
    padding-top: var(--rs-espace-2);
    padding-bottom: var(--rs-espace-2);
}

.rs-bandeau-identite,
.rs-bandeau-agent {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--rs-espace-3);
    min-width: 0;
}

.rs-bandeau-marque,
.rs-bandeau-collectivite {
    font-weight: var(--rs-graisse-appuyee);
    letter-spacing: var(--rs-interlettre-normal);
}

.rs-bandeau-collectivite { font-weight: var(--rs-graisse-normale); opacity: .85; }
.rs-bandeau-mention { opacity: .85; }

.rs-bandeau-agent-nom {
    font-size: var(--rs-texte-corps-dense);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.rs-bandeau-agent-service { opacity: .8; }

.rs-bandeau-agent-service::before {
    content: '·';
    margin: 0 .35em;
    opacity: .6;
}

/* --- Barre de navigation du portail public ------------------------------------------------ */
.rs-navbar {
    background: var(--rs-papier);
    border-bottom: var(--rs-filet) solid var(--rs-ardoise-300);
    --bs-nav-link-color: var(--rs-ardoise-800);
    --bs-navbar-active-color: var(--rs-bleu-900);
    font-size: var(--rs-texte-corps-dense);
}

.rs-navbar .nav-link { font-weight: var(--rs-graisse-moyenne); }

/* L'entrée courante est soulignée d'un filet : la couleur seule ne suffit pas à la marquer. */
.rs-navbar .nav-link.active {
    font-weight: var(--rs-graisse-appuyee);
    box-shadow: inset 0 -3px 0 var(--rs-bleu-700);
}

.rs-marque-application {
    font-size: var(--rs-texte-titre-carte);
    font-weight: var(--rs-graisse-forte);
    letter-spacing: var(--rs-interlettre-capitales);
    color: var(--rs-bleu-900);
}

/* --- Navigation latérale ------------------------------------------------------------------ */
.rs-navigation-racine { margin-bottom: var(--rs-espace-4); }

.rs-navigation-entree {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--rs-espace-2);
    padding: var(--rs-espace-2) var(--rs-espace-4);
}

/* Le libellé prend la place restante, sinon `space-between` le CENTRERAIT entre l'icône et le
   compteur : la colonne de texte se déplacerait d'une entrée à l'autre selon qu'elle porte ou
   non un badge. */
.rs-navigation-libelle { min-width: 0; flex: 1 1 auto; }

/* L'icône tient une gouttière FIXE, quelle que soit sa largeur de glyphe : les libellés
   s'alignent alors tous sur la même verticale, et c'est cet alignement — pas le dessin — qui
   fait gagner le balayage de la liste. */
.rs-navigation-icone {
    flex: 0 0 1.15rem;
    font-size: var(--rs-texte-corps);
    line-height: 1;
    text-align: center;
    color: var(--rs-ardoise-600);
}

.rs-navigation-entree.actif .rs-navigation-icone { color: var(--rs-bleu-700); }

/* --- Compteurs de la navigation et des en-têtes ------------------------------------------- */
.rs-compteur--primary   { background: var(--rs-bleu-700);    color: var(--rs-papier); }
.rs-compteur--secondary { background: var(--rs-ardoise-600);  color: var(--rs-papier); }
.rs-compteur--success   { background: var(--rs-vert-700);     color: var(--rs-papier); }
.rs-compteur--danger    { background: var(--rs-rouge-700);    color: var(--rs-papier); }
.rs-compteur--info      { background: var(--rs-indigo-600);   color: var(--rs-papier); }
.rs-compteur--dark      { background: var(--rs-ardoise-800);  color: var(--rs-papier); }
/* L'ambre ne porte JAMAIS de texte blanc : son texte est l'encre ambre, mesurée à 9,9:1. */
.rs-compteur--warning,
.rs-compteur--attention { background: var(--rs-ambre-500);    color: #1B1B1B; }
.rs-compteur--light     { background: var(--rs-ardoise-100);  color: var(--rs-ardoise-800); }

/* --- Fil d'Ariane -------------------------------------------------------------------------- */
.rs-fil-ariane { margin-bottom: var(--rs-espace-2); }

/* --- Messages flash ------------------------------------------------------------------------ */
.rs-message {
    display: flex;
    align-items: flex-start;
    gap: var(--rs-espace-3);
    padding-right: var(--rs-espace-7);   /* place réservée au bouton de fermeture */
}

.rs-message-icone { flex: 0 0 auto; margin-top: .15em; }
.rs-message-texte { flex: 1 1 auto; min-width: 0; }

/* --- Récapitulatif d'erreurs de formulaire ------------------------------------------------ */
.rs-erreurs-globales { border-left-width: var(--rs-filet-lateral); }

.rs-erreurs-liste {
    margin: var(--rs-espace-2) 0 0;
    padding-left: var(--rs-espace-5);
    font-size: var(--rs-texte-corps-dense);
}

.rs-erreurs-liste > li + li { margin-top: var(--rs-espace-1); }

/* --- Rangée horizontale (fiche modifiable) ------------------------------------------------ */
.rs-rangee-horizontale {
    display: grid;
    grid-template-columns: minmax(10rem, 15rem) 1fr;
    gap: var(--rs-espace-2) var(--rs-espace-4);
    align-items: start;
    padding: var(--rs-espace-3) 0;
    border-bottom: var(--rs-filet) solid var(--rs-ardoise-100);
}

.rs-rangee-horizontale:last-child { border-bottom: 0; }

.rs-rangee-horizontale-intitule { padding-top: .45rem; }

@media (max-width: 575.98px) {
    .rs-rangee-horizontale { grid-template-columns: 1fr; }
    .rs-rangee-horizontale-intitule { padding-top: 0; }
}

/* --- Fiche de montants -------------------------------------------------------------------- */
.rs-fiche--montants { grid-template-columns: 1fr max-content; }

.rs-fiche-terme--total,
.rs-fiche-valeur--total {
    padding-top: var(--rs-espace-2);
    border-top: var(--rs-filet) solid var(--rs-ardoise-300);
    font-weight: var(--rs-graisse-appuyee);
    color: var(--rs-ardoise-800);
}

/* --- Lien de téléchargement ---------------------------------------------------------------- */
.rs-lien-fichier {
    display: inline-flex;
    align-items: center;
    gap: var(--rs-espace-2);
    text-decoration: none;
    font-size: var(--rs-texte-corps-dense);
    min-width: 0;
}

.rs-lien-fichier:hover .rs-lien-fichier-nom { text-decoration: underline; }

.rs-lien-fichier-nom {
    font-weight: var(--rs-graisse-moyenne);
    overflow-wrap: anywhere;
}

/* --- Chargement ---------------------------------------------------------------------------- */
.rs-chargement { padding: var(--rs-espace-3) 0; }

.rs-chargement .rs-squelette {
    height: 1.25rem;
    margin-bottom: var(--rs-espace-2);
}

/* Lignes de longueurs décroissantes : le bloc se lit comme un paragraphe en attente. */
.rs-squelette--1 { width: 100%; }
.rs-squelette--2 { width: 88%; }
.rs-squelette--3 { width: 76%; }
.rs-squelette--4 { width: 64%; }
.rs-squelette--5 { width: 52%; }

/* --- Tableau : compléments ------------------------------------------------------------------ */
.rs-entete-lien {
    display: inline-flex;
    align-items: center;
    gap: var(--rs-espace-1);
    color: inherit;
    text-decoration: none;
}

.rs-tableau-legende {
    font-size: var(--rs-texte-annotation);
    color: var(--rs-ardoise-600);
    padding-bottom: var(--rs-espace-2);
}

.rs-cellule-vide {
    text-align: center;
    padding: var(--rs-espace-5) var(--rs-espace-3) !important;
    color: var(--rs-ardoise-600);
    font-size: var(--rs-texte-corps-dense);
}

/* --- Pagination ------------------------------------------------------------------------------ */
.rs-pagination {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--rs-espace-3) var(--rs-espace-5);
    padding: var(--rs-espace-3) var(--rs-espace-4);
    border-top: var(--rs-filet) solid var(--rs-ardoise-300);
}

.rs-pagination .rs-compteur-resultats { margin: 0; }

.rs-pagination-taille {
    display: flex;
    align-items: center;
    gap: var(--rs-espace-2);
}

.rs-pagination-taille .rs-etiquette { margin-bottom: 0; white-space: nowrap; }

/* --- Modale ---------------------------------------------------------------------------------- */
.rs-modale { border-radius: var(--rs-rayon); border-color: var(--rs-ardoise-300); }
.rs-modale .modal-header { border-bottom-color: var(--rs-ardoise-300); }
.rs-modale .modal-footer { border-top-color: var(--rs-ardoise-300); }

/* --- Pied de page ------------------------------------------------------------------------------ */
.rs-pied-de-page-corps {
    padding-top: var(--rs-espace-5);
    padding-bottom: var(--rs-espace-5);
}

/* --- Pages d'erreur ------------------------------------------------------------------------------
   Le code HTTP domine visuellement mais il est `aria-hidden` : c'est le titre qui informe.
   Le code est redonné en clair sous les sorties, pour l'usager qui doit le citer.
   ------------------------------------------------------------------------------------------------ */
.rs-page-erreur {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: var(--rs-espace-3);
    padding: var(--rs-espace-8) var(--rs-espace-4);
    max-width: 46rem;
    margin: 0 auto;
}

.rs-code-erreur {
    font-size: var(--rs-texte-900);
    font-weight: var(--rs-graisse-forte);
    line-height: 1;
    font-variant-numeric: tabular-nums;
    color: var(--rs-bleu-100);
    margin: 0;
}

.rs-erreur-explication {
    font-size: var(--rs-texte-titre-carte);
    line-height: var(--rs-interligne-large);
    color: var(--rs-ardoise-600);
    max-width: 52ch;
    margin: 0;
}

.rs-erreur-code-lu { margin: var(--rs-espace-3) 0 0; }

/* ==========================================================================================
   SECTIONS RÉSERVÉES AUX LOTS — un lot n'écrit que dans SA feuille `lotN.css` (canonique §6.1)
   Les repères ci-dessous restent en place à titre d'index : ils ne portent aucune règle.
   ========================================================================================== */

/* --- LOT 1 — référentiel, planning et portail public ------------------------------------- */

/* --- LOT 2 — instruction, décision et conventions ---------------------------------------- */

/* --- LOT 3 — facturation, régie et Factur-X ---------------------------------------------- */

/* --- LOT 4 — états des lieux et incidents ------------------------------------------------ */

/* --- LOT 5 — pilotage, exports et RGPD --------------------------------------------------- */

/* --- LOT 6 — sécurité, supervision et socle monitoring ----------------------------------- */
