/* ==========================================================================
   Otemma PmBudget — feuille applicative
   ==========================================================================

   La charte de la suite (Design/README.md), comme Compta : tout ce qui suit ne
   référence QUE des variables --ot- définies par tokens.css, chargé avant ce
   fichier. Aucune couleur n'est écrite en dur ici — un test d'intégration
   (DesignTokensTests) refuse le moindre hexadécimal, ici comme dans les
   feuilles scopées. Une valeur codée en dur survit au thème sombre en
   apparence et disparaît dès que la bascule manuelle est utilisée.

   Le rail de navigation a sa propre feuille scopée (NavMenu.razor.css).
   ========================================================================== */

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

body {
    margin: 0;
    background: var(--ot-glace);
    color: var(--ot-moraine);
    font-family: var(--ot-police-texte);
    font-size: var(--ot-taille-texte);
    line-height: var(--ot-interligne);
    -webkit-font-smoothing: antialiased;
}

/* La bordure de focus est la même partout, y compris dans le rail : c'est une
   des trois dépenses d'accent autorisées par la charte. */
:focus-visible {
    outline: 2px solid var(--ot-gentiane);
    outline-offset: 2px;
    border-radius: 2px;
}

/* Blazor met le focus sur le h1 après une navigation ; un contour à chaque page
   serait du bruit, le lecteur d'écran l'annonce sans lui. */
h1:focus {
    outline: none;
}

/* --- Titres --------------------------------------------------------------- */

h1,
h2,
h3 {
    font-family: var(--ot-police-titre);
    color: var(--ot-roche);
    line-height: var(--ot-interligne-titre);
    text-wrap: balance;
    margin-block: 0 var(--ot-e4);
}

h1 {
    font-size: var(--ot-taille-titre);
    letter-spacing: -0.015em;
    font-weight: 600;
}

h2 {
    font-size: var(--ot-taille-section);
    font-weight: 600;
}

a {
    color: var(--ot-gentiane-fonce);
}

/* --- Formulaires : l'état de validation ----------------------------------- */

.valid.modified:not([type=checkbox]) {
    outline: 1px solid var(--ot-ok);
}

.invalid {
    outline: 1px solid var(--ot-critique);
}

.validation-message {
    color: var(--ot-critique);
}

/* --- L'erreur non gérée de Blazor ----------------------------------------- */

/* L'icône vient du gabarit (un triangle d'avertissement en SVG inline) ; le
   fond passe sur la sévérité critique de la charte. */
.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, var(--ot-critique);
    padding: 1rem 1rem 1rem 3.7rem;
    color: var(--ot-neige);
}

    .blazor-error-boundary::after {
        content: "Une erreur s'est produite."
    }

/* ==========================================================================
   LE VOCABULAIRE DE LA CHARTE — celui de Compta, recopié (OTEMMAPMB-49)
   ==========================================================================

   Les mêmes noms et les mêmes règles que l'app.css de Compta, pour que les
   deux modules se lisent pareil. Recopiés et non partagés : le contexte de
   build Docker de chaque module est son propre dossier.

   Comme Compta, les tableaux et les champs sont stylés par l'élément lui-même
   (table, th, td, input, select, textarea) : il n'y a plus de cadriciel CSS
   avec qui se battre depuis que Bootstrap est parti (OTEMMAPMB-49).

   UN ÉCART, VOULU : les boutons. Compta stylise button ; ici ils passent par
   leurs classes (.bouton, .bouton--principal), jamais par l'élément. Un
   button nu atteindrait « Déconnexion » dans le rail — un bouton de
   formulaire, dont la feuille scopée ne fixe pas la couleur du filet au
   survol — et toute page qui oublierait la classe aurait l'air finie sans
   l'être. */

p {
    margin-block: 0 var(--ot-e4);
    max-inline-size: var(--ot-largeur-texte);
}

section + section {
    margin-block-start: var(--ot-e12);
}

/* L'étiquette de section : petites capitales espacées, pas un titre de plus. */
.etiquette {
    font-size: var(--ot-taille-etiquette);
    text-transform: uppercase;
    letter-spacing: 0.09em;
    font-weight: 600;
    color: var(--ot-moraine-clair);
    margin: 0 0 var(--ot-e3);
}

/* --- Chiffres et identifiants -------------------------------------------- */

/* Une colonne de montants qui ne s'aligne pas est une colonne que personne ne
   peut parcourir ; un trigramme en monospace ne lit pas un 0 pour un O. */
.nombre,
td.nombre {
    font-variant-numeric: tabular-nums;
    text-align: end;
    white-space: nowrap;
}

.identifiant {
    font-family: var(--ot-police-mono);
    font-size: var(--ot-taille-petit);
}

/* Le code dans un texte d'aide (« /p/ABC ») ou un identifiant de requête : la
   police mono de la charte, à l'encre du texte qui l'entoure. */
code {
    font-family: var(--ot-police-mono);
    font-size: 0.875em;
    color: inherit;
}

/* --- Pastilles ------------------------------------------------------------ */

/* Le sens est porté par le FOND (le voile), jamais par l'encre, qui est roche
   (OTEMMAPMB-50). Un projet actif est « en ordre », d'où --ot-ok ; un projet
   archivé n'est pas une alerte, d'où un fond neutre. */
.pastille {
    display: inline-block;
    font-size: var(--ot-taille-petit);
    padding: 0.125rem var(--ot-e2);
    border-radius: 999px;
    white-space: nowrap;
}

.pastille--ok {
    background: var(--ot-ok-voile);
    color: var(--ot-roche);
}

.pastille--archive {
    background: var(--ot-glace);
    color: var(--ot-roche);
}

/* Un rôle (« Administrateur ») : une information, ni un succès ni une alerte,
   d'où le voile gentiane — le même couple que .message--information. */
.pastille--role {
    background: var(--ot-gentiane-voile);
    color: var(--ot-roche);
}

/* --- Messages ------------------------------------------------------------- */

/* Chaque gravité porte un filet de 3 px en plus de sa teinte : la couleur
   seule échoue pour environ un homme sur douze, et à l'impression. Un BLOC,
   jamais flex — Compta a mesuré ce que flex fait à une phrase. */
.message {
    display: block;
    padding: var(--ot-e3) var(--ot-e4);
    border: 1px solid var(--ot-filet);
    border-inline-start: 3px solid var(--ot-moraine-clair);
    border-radius: var(--ot-rayon);
    background: var(--ot-neige);
    margin-block-end: var(--ot-e4);
    max-inline-size: var(--ot-largeur-texte);
}

.message > :last-child {
    margin-block-end: 0;
}

.message--erreur {
    border-inline-start-color: var(--ot-critique);
    background: var(--ot-critique-voile);
    color: var(--ot-roche);
}

.message--succes {
    border-inline-start-color: var(--ot-ok);
    background: var(--ot-ok-voile);
    color: var(--ot-roche);
}

.message--information {
    border-inline-start-color: var(--ot-gentiane);
    background: var(--ot-gentiane-voile);
    color: var(--ot-roche);
}

/* --- Formulaires ---------------------------------------------------------- */

.champs {
    display: grid;
    gap: var(--ot-e4);
    max-inline-size: 34rem;
}

.champ {
    display: flex;
    flex-direction: column;
    gap: var(--ot-e1);
}

.champ label,
label.champ__intitule {
    font-size: var(--ot-taille-petit);
    font-weight: 600;
    color: var(--ot-roche);
}

/* Une case à cocher se lit DANS son libellé, sur une ligne. */
.champ--case label {
    display: flex;
    align-items: center;
    gap: var(--ot-e2);
}

.champ__aide {
    font-size: var(--ot-taille-petit);
    color: var(--ot-moraine-clair);
}

/* `input:not([type])` n'est pas défensif : InputText rend un <input> SANS
   attribut type (mesuré dans Compta). */
:is(input:not([type]),
    input[type="text"],
    input[type="email"],
    input[type="password"],
    input[type="number"],
    input[type="date"],
    select,
    textarea) {
    font: inherit;
    color: var(--ot-moraine);
    background: var(--ot-neige);
    border: 1px solid var(--ot-filet);
    border-radius: var(--ot-rayon);
    padding: var(--ot-e2) var(--ot-e3);
    inline-size: 100%;
}

/* Les mêmes types que la règle du dessus, jamais `input` seul : une case à
   cocher serait atteinte aussi, et la bascule de thème du rail — une case
   stylée dans sa feuille scopée (0,2,0), que :not(:disabled):hover (0,3,1)
   battrait — prendrait un filet bleu au survol que celle de Compta n'a pas. */
:is(input:not([type]),
    input[type="text"],
    input[type="email"],
    input[type="password"],
    input[type="number"],
    input[type="date"],
    select,
    textarea):not(:disabled):hover {
    border-color: var(--ot-gentiane);
}

/* Un champ désactivé (le trigramme, définitif) se lit sur la glace : on voit
   qu'il n'attend rien. */
:is(input:not([type]),
    input[type="text"],
    input[type="email"],
    input[type="password"],
    input[type="number"],
    input[type="date"],
    select,
    textarea):disabled {
    background: var(--ot-glace);
    color: var(--ot-moraine);
}

/* Le trigramme se tape en minuscules et s'enregistre en majuscules : le champ
   montre déjà ce qui sera gardé. */
.majuscules {
    text-transform: uppercase;
}

/* Le résumé de validation : la liste des refus, sans puces de plus que de
   messages. */
.validation-errors {
    margin: 0 0 var(--ot-e4);
    padding-inline-start: var(--ot-e6);
}

/* --- Boutons -------------------------------------------------------------- */

.bouton {
    font: inherit;
    font-weight: 600;
    font-size: var(--ot-taille-petit);
    padding: var(--ot-e2) var(--ot-e4);
    border-radius: var(--ot-rayon);
    border: 1px solid var(--ot-filet);
    background: var(--ot-neige);
    color: var(--ot-moraine);
    cursor: pointer;
    text-decoration: none;
    display: inline-block;
}

.bouton:not(:disabled):hover {
    border-color: var(--ot-gentiane);
    color: var(--ot-roche);
}

/* Un bouton désactivé (↑ sur la première catégorie, « Enregistrer » quand rien
   n'a changé) dit qu'il n'agira pas ; WCAG exempte les contrôles inactifs. */
.bouton:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* L'action principale : la seconde dépense d'accent autorisée, une seule par
   écran. Le survol fonce vers la roche (OTEMMAPMB-50). */
.bouton--principal {
    background: var(--ot-gentiane-fonce);
    border-color: var(--ot-gentiane-fonce);
    color: var(--ot-neige);
}

.bouton--principal:not(:disabled):hover {
    background: var(--ot-roche);
    border-color: var(--ot-roche);
    color: var(--ot-neige);
}

/* Une action qui détruit. Le sens est dans le filet et l'encre critiques ; au
   survol, le voile critique, et l'encre passe à la roche comme partout. */
.bouton--danger {
    border-color: var(--ot-critique);
    color: var(--ot-critique);
}

.bouton--danger:not(:disabled):hover {
    background: var(--ot-critique-voile);
    border-color: var(--ot-critique);
    color: var(--ot-roche);
}

/* Un bouton qui doit se lire comme un lien : « Annuler » une confirmation reste
   sur la page, donc c'est un bouton, mais il ne mérite pas le poids d'un
   bouton. Le survol fonce vers la roche (OTEMMAPMB-50). */
.lien-bouton {
    font: inherit;
    padding: 0;
    border: none;
    background: none;
    color: var(--ot-gentiane-fonce);
    text-decoration: underline;
    cursor: pointer;
}

.lien-bouton:hover {
    border: none;
    color: var(--ot-roche);
}

/* --- Tableaux ------------------------------------------------------------- */

table {
    inline-size: 100%;
    border-collapse: collapse;
    background: var(--ot-neige);
    border: 1px solid var(--ot-filet);
    border-radius: var(--ot-rayon);
    overflow: hidden;
    margin-block-end: var(--ot-e4);
}

th {
    text-align: start;
    font-size: var(--ot-taille-etiquette);
    text-transform: uppercase;
    letter-spacing: 0.09em;
    color: var(--ot-moraine-clair);
    font-weight: 600;
    padding: var(--ot-e3) var(--ot-e4);
    border-block-end: 1px solid var(--ot-filet);
}

/* L'en-tête d'une colonne de chiffres s'aligne sur ses chiffres : `.nombre`
   (0,1,0) l'emporte sur `th` (0,0,1) sans règle de plus — tant que bornée par
   :not(.table), la règle d'en-tête pesait 0,1,2 et en demandait une. */

td {
    padding: var(--ot-e3) var(--ot-e4);
    border-block-end: 1px solid var(--ot-filet);
    font-size: var(--ot-taille-petit);
    vertical-align: middle;
}

tbody tr:last-child td {
    border-block-end: none;
}

/* Le pied d'un tableau additionne ce qui précède : un filet au-dessus, et le
   chiffre en roche, pas en étiquette. `.pied` est le même pied posé dans un
   tbody, quand des lignes doivent encore le suivre : un tfoot ferme le
   tableau (la synthèse et ses reports, OTEMMAPMB-66). */
tfoot td,
.pied td {
    border-block-start: 1px solid var(--ot-filet);
    border-block-end: none;
    font-weight: 600;
    color: var(--ot-roche);
}

/* Un en-tête de LIGNE (th scope="row") se lit comme sa ligne, pas comme une
   étiquette de colonne : dans la grille de la synthèse (OTEMMAPMB-10), il
   nomme la catégorie de chaque chiffre pour les lecteurs d'écran, et il doit
   rester un nom, pas des petites capitales grises. */
th[scope="row"] {
    text-transform: none;
    letter-spacing: normal;
    font-size: var(--ot-taille-petit);
    font-weight: inherit;
    color: inherit;
}

tbody tr:last-child th[scope="row"] {
    border-block-end: none;
}

tfoot th[scope="row"],
.pied th[scope="row"] {
    border-block-start: 1px solid var(--ot-filet);
    border-block-end: none;
    font-weight: 600;
    color: var(--ot-roche);
}

/* Un groupe de lignes à part dans un même tableau — les reports de la
   synthèse, sous son total (OTEMMAPMB-66) : son en-tête garde le style
   d'étiquette d'une colonne, posé sous un filet pour séparer le groupe de ce
   qui l'additionne. */
.groupe th[scope="rowgroup"] {
    border-block-start: 1px solid var(--ot-filet);
}

/* Un cadre qui s'ouvre sur sa FIN : la synthèse lit les mois de gauche à
   droite et l'on vient y lire les derniers, cachés à droite sur un écran
   étroit (OTEMMAPMB-66). Un cadre de droite à gauche démarre défilé au bout ;
   le tableau, lui, reste de gauche à droite. Sans effet quand le tableau
   tient, puisqu'il prend toute la largeur. */
.tableau--fin {
    direction: rtl;
    border-radius: var(--ot-rayon);
}

/* Le tableau ne rogne plus lui-même ses coins (`overflow: hidden` en ferait le
   conteneur de défilement, et la colonne collante ci-dessous resterait collée
   à un tableau qui ne défile pas) : le cadre, qui défile, porte l'arrondi. */
.tableau--fin > table {
    direction: ltr;
    overflow: visible;
}

/* Ouvert sur sa fin, le cadre cacherait à gauche ce qui NOMME chaque ligne —
   des chiffres sans leur catégorie ni leur sponsor, vu à 375 px pendant la
   démo de R2. La première colonne reste donc à l'écran, sur le fond du
   tableau pour couvrir ce qui défile dessous ; l'intitulé d'un groupe aussi. */
.tableau--fin thead th:first-child,
.tableau--fin th[scope="row"] {
    position: sticky;
    left: 0;
    z-index: 1;
    background: var(--ot-neige);
}

/* Plus large que l'écran, l'intitulé ne pourrait pas rester entier : il passe
   à la ligne dans la largeur d'un téléphone. */
.tableau--fin th[scope="rowgroup"] > span {
    position: sticky;
    left: var(--ot-e4);
    display: inline-block;
    max-inline-size: 18rem;
    white-space: normal;
}

/* Un tableau plus large que l'écran défile DANS son cadre, jamais la page :
   sans ce cadre, six pages défilaient horizontalement à 375 px, et
   l'administration dès 800 px (mesuré le 06.10.2026 par les captures de
   OTEMMAPMB-49). Chaque <table> est posé dans un .tableau — un test
   d'intégration le vérifie, le balayage e2e mesure la largeur des pages.

   Le cadre est une région focalisable (tabindex, role, aria-label dans le
   balisage) : un tableau sans contrôle, comme celui des sponsors du tableau
   de bord, ne se ferait défiler au clavier qu'aux navigateurs qui rendent
   d'eux-mêmes les zones défilantes focalisables, et Safari n'en est pas.

   Pas de rembourrage : un cadre qui défile rogne ce qui dépasse, mais aucun
   contrôle n'en touche le bord — les cellules ont 12 à 16 px de marge
   intérieure, l'anneau de focus en déborde de 4 —, et l'anneau du cadre
   lui-même est dessiné à l'extérieur. */
.tableau {
    overflow-x: auto;
    margin-block-end: var(--ot-e4);
}

.tableau > table {
    margin-block-end: 0;
}

/* Les boutons d'une ligne : ils passent à la ligne plutôt que de pousser le
   tableau hors de sa place (OTEMMAPMB-40), et un formulaire par bouton reste
   en ligne. */
.ligne__actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: var(--ot-e2);
}

.ligne__actions form {
    display: contents;
}

/* La même rangée, calée au début de sa cellule : un état suivi de son geste,
   lu de gauche à droite comme le reste de la ligne. */
.ligne__actions--debut {
    justify-content: flex-start;
    align-items: center;
}

/* --- Listes de définitions ------------------------------------------------ */

.fiche {
    display: grid;
    grid-template-columns: minmax(0, 12rem) minmax(0, 1fr);
    gap: 0;
    background: var(--ot-neige);
    border: 1px solid var(--ot-filet);
    border-radius: var(--ot-rayon);
    box-shadow: var(--ot-ombre);
    overflow: hidden;
    margin-block: 0 var(--ot-e6);
    max-inline-size: 46rem;
}

.fiche dt {
    padding: var(--ot-e3) var(--ot-e4);
    font-size: var(--ot-taille-etiquette);
    text-transform: uppercase;
    letter-spacing: 0.09em;
    font-weight: 600;
    color: var(--ot-moraine-clair);
    border-block-end: 1px solid var(--ot-filet);
    background: var(--ot-glace);
}

.fiche dd {
    margin: 0;
    padding: var(--ot-e3) var(--ot-e4);
    color: var(--ot-roche);
    border-block-end: 1px solid var(--ot-filet);
}

.fiche dt:last-of-type,
.fiche dd:last-of-type {
    border-block-end: none;
}

/* --- Vide ----------------------------------------------------------------- */

.vide {
    padding: var(--ot-e8);
    text-align: center;
    color: var(--ot-moraine-clair);
    background: var(--ot-neige);
    border: 1px dashed var(--ot-filet);
    border-radius: var(--ot-rayon);
    max-inline-size: none;
}

/* --- Actions -------------------------------------------------------------- */

.actions {
    display: flex;
    gap: var(--ot-e3);
    align-items: center;
    flex-wrap: wrap;
    margin-block-start: var(--ot-e6);
}

/* --- Double authentification (R4) ---------------------------------------- */

/* Le code QR garde son noir sur blanc dans les deux thèmes : ses couleurs sont
   dans le SVG lui-même (QrCodeSvg), parce qu'un scanner a besoin de ce contraste
   et que les encres de la charte changent avec le thème. Ici, seulement sa
   taille et sa marge. */
.qr {
    margin: 0 0 var(--ot-e6);
    inline-size: min(14rem, 100%);
}

.qr svg {
    display: block;
    inline-size: 100%;
    block-size: auto;
}

/* Les codes de récupération, montrés une seule fois : sur deux colonnes, en
   police mono, assez grands pour être recopiés sans erreur. */
.codes-recuperation {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
    gap: var(--ot-e2) var(--ot-e6);
    max-inline-size: 28rem;
    padding-inline-start: var(--ot-e6);
}

.codes-recuperation code {
    font-size: var(--ot-taille-texte);
    letter-spacing: 0.05em;
}

@media (prefers-reduced-motion: reduce) {
    * {
        transition: none !important;
        animation: none !important;
    }
}
