/* ===========================================================================
   THÈMES — et rien d'autre.
   ===========================================================================

   RÈGLE UNIQUE, ET ELLE N'EST PAS NÉGOCIABLE :
   un thème ne redéfinit que des JETONS. Pas un `display`, pas une largeur,
   pas un sélecteur de composant, pas une media query.

   Le jour où un thème commence à repositionner un élément, il devient une
   seconde feuille de style à maintenir en parallèle de la première — et l'on
   se retrouve avec deux portails là où il n'y en avait qu'un. La règle est
   vérifiée par `tests/css_static.php`, qui refuse toute déclaration autre
   qu'une variable dans ce fichier.

   Cette feuille est chargée EN PREMIER, avant components.css — et c'est
   volontaire. Un thème cible `body[data-theme]`, le marqueur d'environnement
   `body[data-environnement]` : même spécificité, donc c'est le dernier déclaré
   qui l'emporte. Charger les thèmes d'abord donne la seule priorité
   acceptable : savoir qu'on est en préproduction prime sur l'allure choisie.
   Les jetons de `:root` restent dominés par le thème quel que soit l'ordre,
   puisqu'ils ciblent `html` et non `body`.

   ---------------------------------------------------------------------------
   POURQUOI LE THÈME « PORTAIL » N'EST PAS ICI

   Il EST `:root`. Un compte réglé sur le thème d'origine ne pose aucun attribut
   sur `<body>`, et aucune règle de ce fichier ne s'applique. C'est ce qui rend
   le mécanisme sûr : tant que personne n'a choisi autre chose, le portail est
   exactement celui d'avant — à l'octet près.

   ---------------------------------------------------------------------------
   CE QUI NE SE THÉMATISE PAS

   Deux familles de jetons ne doivent jamais apparaître ici :

   · la SIGNALÉTIQUE du mouvement (--c-branche-*, --c-genre-*). Le jaune d'une
     meute et le vert d'une troupe sont des codes lus sans être relus ; un thème
     qui les changerait ne rendrait pas le portail plus joli, il le rendrait
     faux ;

   · le BANDEAU D'ENVIRONNEMENT (--c-env-*). Son rôle est de rester
     reconnaissable quoi qu'il arrive : c'est lui qui évite de travailler en
     préproduction en croyant être en production. Il doit survivre à n'importe
     quelle refonte, y compris à celle qui le trouverait laid.

   ---------------------------------------------------------------------------
   AJOUTER UN THÈME

   1. déclarer son code et son nom dans `ThemeCatalog::disponibles()` ;
   2. ouvrir ici un bloc `body[data-theme="<code>"] { … }` ;
   3. n'y écrire que des `--jeton: valeur;` ;
   4. redéfinir `--c-primary-rgb` si la couleur du thème change — sans quoi les
      halos de focus et les ombres teintées resteront de l'ancienne couleur.

   Le marqueur d'environnement (components.css) ne touche plus aux couleurs
   du thème : il ne pose que le fond de page de la préproduction.
   =========================================================================== */

/* ===========================================================================
   DSM — une interface claire, dense, posée à plat.
   ===========================================================================
   Quatre partis pris, et rien d'autre :

   · une BARRE CLAIRE séparée par un filet, là où le portail pose un aplat
     bleu foncé. C'est ce qui distingue le plus une interface d'aujourd'hui ;
   · des ANGLES COURTS (4 px au lieu de 8) et des ombres presque absentes :
     l'interface paraît posée à plat plutôt qu'en relief ;
   · une DENSITÉ plus forte — contrôles de 32 px au lieu de 40 ;
   · un BLEU VIF comme unique accent, qui ne sert qu'à désigner ce sur quoi
     on agit.

   Ce qui n'est PAS redéfini ici, et ne doit jamais l'être : la signalétique
   du mouvement (--c-branche-*, --c-genre-*) et le bandeau d'environnement
   (--c-env-*).
   =========================================================================== */
body[data-theme="dsm"] {
    /* L'accent. `--c-primary-rgb` suit obligatoirement : sans lui, les halos
       de focus et les ombres teintées resteraient bleu marine sur un portail
       qui ne l'est plus. */
    --c-primary: #0086e5;
    --c-primary-hover: #0072c4;
    --c-primary-rgb: 0, 134, 229;

    /* Les surfaces. Le fond tire légèrement vers le froid, ce qui fait
       ressortir le blanc des panneaux sans avoir à les cerner. */
    --c-fond-page: #eef1f5;
    --c-surface: #ffffff;
    --c-surface-alt: #f7f9fb;
    --c-border: #dfe3e8;

    /* Le texte. Trois niveaux, du plus appuyé au plus discret. */
    --c-text: #21262d;
    --c-text-doux: #6b7684;
    --c-text-appuye: #0d1117;
    --c-ghost-text: #6b7684;
    --c-ghost-border: #dfe3e8;
    --c-ghost-hover-bg: #f2f5f8;
    --c-neutral: #6b7684;
    --c-neutral-hover: #545d68;

    /* La barre d'application : ardoise foncée.
       Elle a d'abord été blanche, par cohérence avec le reste du thème. Essai
       fait, essai perdu : une barre blanche au-dessus d'un contenu blanc ne
       sépare rien, et l'interface n'a plus de haut. Un poste de travail a une
       barre qui se voit — c'est elle qui dit où finit le système et où
       commence le document. Le vert reste à l'accent, donc à ce sur quoi on
       agit ; la barre, elle, est neutre.
       Le filet vaut la couleur du fond : sur une barre foncée, un trait de
       séparation n'a plus rien à séparer. */
    --c-entete-fond: #1c2128;
    --c-entete-texte: #ffffff;
    --c-entete-filet: #1c2128;
    --c-entete-texte-doux: rgba(255, 255, 255, .72);
    --voile-entete: rgba(255, 255, 255, .12);
    --ombre-entete: none;

    /* Les angles, courts. */
    --radius-xs: 3px;
    --radius-sm: 3px;
    --radius-md: 4px;
    --radius-lg: 6px;

    /* La densité. */
    --control-h: 32px;
    --btn-pad-x: 14px;

    /* L'élévation, presque supprimée. Un dialogue garde la sienne : c'est le
       seul endroit où une interface plate a encore besoin de dire « ceci est
       au-dessus du reste ». */
    --ombre-carte: 0 1px 2px rgba(33, 38, 45, .06);
    --ombre-levee: 0 2px 8px rgba(33, 38, 45, .10);
    --ombre-flottante: 0 8px 28px rgba(33, 38, 45, .18);
    --ombre-posee: 0 1px 2px rgba(33, 38, 45, .30);
    --fond-modale: rgba(33, 38, 45, .40);

    /* Les familles sémantiques, adoucies pour s'accorder au reste. Le sens ne
       change pas : une suppression reste rouge, une réussite reste verte. */
    --c-succes: #19a974;
    --c-succes-bg: #e6f7f0;
    --c-succes-texte: #0d7a53;
    --c-danger: #e0483c;
    --c-danger-accent: #e0483c;
    --c-danger-bg: #fdecea;
    --c-danger-bg-doux: #fef7f6;
    --c-danger-texte: #b02a20;
    --c-attention: #c77c11;
    --c-attention-vif: #f0a020;
    --c-attention-bg: #fdf6e7;
    --c-attention-text: #946200;
    --c-info-bg: #eaf4fd;
}

/* Vérification du filet : ce bloc est le SEUL thème d'essai.
   Il ne figure pas au catalogue, donc personne ne peut le choisir ; il existe
   pour que `tests/action_smoke_e.php` prouve que la chaîne complète fonctionne
   — réglage, attribut sur <body>, feuille appliquée — avant qu'un vrai thème
   n'en dépende. */
body[data-theme="essai"] {
    --c-primary: #6b21a8;
    --c-primary-rgb: 107, 33, 168;
}

/* ===========================================================================
   ET CE QUE LES JETONS NE PEUVENT PAS FAIRE
   ===========================================================================
   Tout ce qui suit est de la MISE EN PAGE, et c'est assumé. La règle n'est
   plus « rien que des jetons » — elle était trop étroite — mais « rien qui
   ne soit rattaché à ce thème ». Aucune de ces règles ne peut atteindre un
   compte qui ne l'a pas choisi.

   Le tableau de bord passe d'une LISTE DE CARTES à un BUREAU D'ICÔNES. Le
   HTML n'a pas bougé d'une ligne : il portait déjà un lien, une icône et un
   libellé — il était écrit comme une carte, il se lit comme une application.
   =========================================================================== */

/* Le bureau : des tuiles carrées, alignées, qu'on parcourt du regard plutôt
   que de lire ligne à ligne. */
body[data-theme="dsm"] .module-grid,
body[data-theme="dsm"] .dashboard-modules {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(116px, 1fr));
    gap: 16px;
}

body[data-theme="dsm"] .module-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    gap: 8px;
    text-align: center;
    padding: 16px 8px 12px;
    min-height: 104px;
    border: 1px solid transparent;
    background: transparent;
    box-shadow: none;
    border-radius: var(--radius-md);
    font-size: var(--fs-xs);
    line-height: 1.3;
    font-weight: 500;
    color: var(--c-text);
    transition: background .12s ease, border-color .12s ease;
}

/* Au survol, la tuile se révèle — au repos, le bureau reste calme. C'est ce
   qui distingue un bureau d'une grille de boutons. */
body[data-theme="dsm"] .module-card:hover {
    background: var(--c-surface);
    border-color: var(--c-border);
    box-shadow: var(--ombre-carte);
}

/* L'icône devient le sujet : c'est elle qu'on vise, pas le mot. */
body[data-theme="dsm"] .module-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 52px;
    height: 52px;
    font-size: var(--taille-picto-tuile);
    line-height: 1;
    border-radius: 12px;
    background: var(--c-surface);
    box-shadow: var(--ombre-carte);
    margin: 0;
}

body[data-theme="dsm"] .module-card:hover .module-icon {
    box-shadow: var(--ombre-levee);
}

/* ---------------------------------------------------------------------------
   La densité d'une grille de données
   ---------------------------------------------------------------------------
   C'est l'écran où l'on passe le plus de temps, et celui où le thème gagnait le
   moins : les cellules gardaient le retrait d'une page web (8 px partout), soit
   des lignes de près de 50 px. Un outil de gestion en met deux fois plus à
   l'écran — et ce qu'on cherche vraiment, c'est de voir la ligne suivante sans
   faire défiler.

   Ce qu'on ne sacrifie PAS : la surface de touche des boutons d'action, qui
   reste au-dessus du minimum de 24 px (WCAG 2.5.8), et la lisibilité du texte,
   qui garde sa taille. On ne resserre que le vide.
   ------------------------------------------------------------------------- */
body[data-theme="dsm"] .simple-list-table th,
body[data-theme="dsm"] .simple-list-table td,
body[data-theme="dsm"] table th,
body[data-theme="dsm"] table td {
    padding-top: 4px;
    padding-bottom: 4px;
    line-height: 1.35;
}

/* Les boutons d'une colonne d'action commandent la hauteur de la ligne : à
   32 px ils annulaient à eux seuls le resserrement. 28 px reste franchement
   au-dessus du minimum exigé. */
body[data-theme="dsm"] .btn-action-column {
    width: 28px;
    min-width: 28px;
    height: 28px;
}

/* La ligne survolée se détache : dans une grille dense, c'est ce qui permet de
   suivre une ligne du regard jusqu'à sa dernière colonne. */
body[data-theme="dsm"] .simple-list-table tbody tr:hover {
    background-color: var(--c-surface-alt);
}

/* ---------------------------------------------------------------------------
   La grille défile dans son cadre, l'en-tête reste
   ---------------------------------------------------------------------------
   ⚠️ CHANGEMENT DE COMPORTEMENT ASSUMÉ, et c'est la seule façon d'y arriver.
   `position: sticky` sur un `thead` ne fonctionne pas tant que le cadre qui
   l'entoure n'a pas de hauteur : `.table-wrap` porte `overflow-x: auto`, le
   navigateur en déduit `overflow-y: auto`, et l'ancêtre de défilement de
   l'en-tête devient ce cadre — qui ne défile jamais, faute de limite. Mesuré
   avant correction : après 600 px de défilement, l'en-tête à -559.

   En bornant la hauteur, la liste défile DEDANS et son en-tête tient. C'est ce
   que fait tout outil de gestion, et cela ne coûte rien aux petites listes :
   `max-height` n'impose pas de hauteur, elle plafonne — une liste de trois
   lignes reste haute de trois lignes.

   Réservé au poste de travail : sur un téléphone, les listes deviennent des
   cartes empilées et il n'y a plus ni en-tête ni cadre à faire tenir.
   ------------------------------------------------------------------------- */
/* ⚠️ REVU (01/10/2026, repris par le propriétaire) : le cadre borné à 70 vh
   créait un DOUBLE défilement sur les longues listes — celui de la page et
   celui de la grille — et l'on ne pouvait pas descendre au bas de la liste
   d'un seul geste. Le cadre ne défile plus du tout (`overflow: visible`) :
   la PAGE porte le défilement, et l'en-tête collant s'accroche alors au
   haut de la fenêtre. Contrepartie : une grille plus large que la carte
   déborde au lieu de défiler à l'horizontale — les colonnes sont réparties
   à la largeur disponible (app.js), ce cas reste l'exception. */
@media (min-width: 901px) and (pointer: fine), (min-width: 901px) and (min-height: 501px) {
    body[data-theme="dsm"] .table-wrap {
        max-height: none;
        overflow: visible;
    }

    body[data-theme="dsm"] .simple-list-table thead th {
        position: sticky;
        top: 0;
        /* ⚠️ Le fond doit être OPAQUE : les lignes passent dessous en défilant,
           et un en-tête translucide les laisse transparaître. Il est déjà posé
           plus haut, on ne fait que s'assurer qu'il prime. */
        background-color: var(--c-surface-alt);
        z-index: 2;
    }

    /* Le cadre se ferme d'un filet : sans lui, rien ne dit que la liste a une
       fin et que c'est elle qui défile, pas la page. */
    body[data-theme="dsm"] .table-wrap {
        border-bottom: 1px solid var(--c-border);
    }
}

/* Les cartes ordinaires perdent leur relief : elles délimitent, elles ne
   surélèvent plus. */
body[data-theme="dsm"] .card,
body[data-theme="dsm"] .carte {
    box-shadow: var(--ombre-carte);
    border: 1px solid var(--c-border);
}

/* Une carte EN RETRAIT (filtres, choix d'un groupe, petits formulaires) prend
   le gabarit du bloc photo d'une fiche : fond gris, filet gauche épais à la
   couleur du thème, sans ombre ni autre bord (repris par le propriétaire —
   demandé écran après écran, posé ici une fois pour toutes). */
body[data-theme="dsm"] .carte.carte--discrete:not(.module-card) {
    background: var(--c-ghost-hover-bg);
    border: 0;
    border-left: 4px solid var(--c-primary);
    border-radius: var(--radius-xs);
    box-shadow: none;
}

/* ⚠️ Les ACCÈS RAPIDES d'une famille ne sont pas des cartes : ce sont des
   pictos, comme les tuiles du pilotage des chefs — ni fond, ni filet (repris
   par le propriétaire, sur téléphone comme sur grand écran). */
body[data-theme="dsm"] .carte.module-card {
    background: transparent;
    border: 0;
    box-shadow: none;
}

/* ---------------------------------------------------------------------------
   Les tuiles du tableau de bord : posées, pas flottantes
   ---------------------------------------------------------------------------
   Elles gardaient l'ombre portée, les grands angles et le SOULÈVEMENT au
   survol — le vocabulaire d'une page web, où une carte est un objet qu'on
   manipule. Dans un outil, une tuile d'indicateur est une zone de l'écran :
   elle se délimite d'un filet et se signale, quand elle mène quelque part, par
   sa bordure et son fond. Rien ne bouge sous le pointeur.
   ------------------------------------------------------------------------- */
body[data-theme="dsm"] .stat-card,
body[data-theme="dsm"] .action-card,
body[data-theme="dsm"] .module-card,
body[data-theme="dsm"] .entity-card {
    box-shadow: none;
    border-radius: var(--radius-md);
}

body[data-theme="dsm"] .stat-card--link:hover,
body[data-theme="dsm"] .action-card:hover,
body[data-theme="dsm"] .module-card:hover,
body[data-theme="dsm"] .entity-card:hover {
    transform: none;
    box-shadow: none;
    border-color: var(--c-primary);
    background-color: var(--c-surface-alt);
}

/* Une tuile d'indicateur n'a pas besoin de 104 px de haut pour porter un
   nombre et son intitulé. */
body[data-theme="dsm"] .stat-card,
body[data-theme="dsm"] .action-card,
body[data-theme="dsm"] .module-card {
    min-height: 0;
}

/* Le TITRE d'écran : un seul style partout — gras, comme sur la fiche d'un
   enfant qui le posait en <strong>, même taille, même filet couleur du thème
   (repris par le propriétaire, 01/10/2026). */
body[data-theme="dsm"] h1,
body[data-theme="dsm"] .section-header h1 {
    font-weight: 700;
    font-size: var(--fs-h1);
    letter-spacing: -.01em;
    /* La couleur des titres de section (repris par le propriétaire) : titre de
       page et titres de section se règlent ensemble. */
    color: var(--c-titre-section);
}

/* ---------------------------------------------------------------------------
   La densité d'une application de gestion
   ---------------------------------------------------------------------------
   Un portail web aère pour se faire lire ; un outil de bureau resserre pour
   se faire parcourir. C'est la différence la plus sensible entre les deux, et
   elle ne tient qu'à des marges.
   ------------------------------------------------------------------------- */

/* La page occupe l'écran au lieu d'être une colonne centrée : un outil ne
   laisse pas 300 px de vide à sa droite quand il a un tableau à montrer. */
body[data-theme="dsm"] .page-container {
    max-width: none;
    margin: 16px auto;
    padding: 0 24px;
}

/* Les panneaux respirent moins, et se suivent de plus près. */
body[data-theme="dsm"] .card {
    padding: 16px;
    margin-bottom: 16px;
    border-radius: var(--radius-md);
}

/* L'en-tête d'écran : une barre de titre, pas une bannière. */
body[data-theme="dsm"] .section-header,
body[data-theme="dsm"] .page-title-row {
    padding-bottom: 12px;
    margin-bottom: 16px;
    border-bottom: 1px solid var(--c-border);
}

/* Le filet du TITRE d'écran seul prend la couleur du thème : il dit « voici
   l'écran » ; ceux des sections, plus bas, restent gris (repris par le
   propriétaire). */
body[data-theme="dsm"] .page-title-row,
body[data-theme="dsm"] .section-header.page-title-row,
body[data-theme="dsm"] .section-header:has(> h1) {
    border-bottom-color: var(--c-primary);
}

/* ⚠️ L'ÉCHELLE DES TITRES, EN ENTIER.
   Ramener le seul h2 à la taille du texte courant avait retourné la
   hiérarchie : h3 et h4 gardaient la taille que leur donne le navigateur —
   plus grande — et sur « Mon profil », « 1. Téléverser mon RIB » s'affichait
   plus gros que « Mes coordonnées bancaires » qui le contient. Un thème qui
   touche à un niveau de titre les pose donc TOUS : c'est le rapport entre eux
   qui dit ce qui contient quoi, pas leur taille absolue. */
body[data-theme="dsm"] h2 {
    font-weight: 600;
    font-size: var(--fs-titre);
}

body[data-theme="dsm"] h3 {
    font-weight: 600;
    font-size: var(--fs-base);
}

body[data-theme="dsm"] h4 {
    font-weight: 600;
    font-size: var(--fs-md);
}

/* L'intitulé d'un champ se range SOUS le dernier niveau de titre : il nomme
   une donnée, pas une section. Il était en gras à la taille du texte courant,
   ce qui le mettait au même rang qu'un sous-titre — « IBAN » pesait autant
   que « 2. Vérifier et enregistrer » qui le contient. */
body[data-theme="dsm"] label {
    font-size: var(--fs-md);
    font-weight: 600;
}

/* Les en-têtes de tableau : discrets, collants, comme une grille de données. */
body[data-theme="dsm"] thead th {
    background: var(--c-surface-alt);
    font-weight: 500;
    text-transform: none;
    letter-spacing: 0;
    border-bottom: 1px solid var(--c-border);
}

/* Les champs suivent la hauteur des boutons : dans un formulaire dense, une
   ligne qui dépasse de 8 px se voit plus qu'on ne le croit.

   ⚠️ Plus de `height` ici, et pour deux raisons mesurées. D'abord une liste
   déroulante haute de 32 px avec 8 px de marge intérieure ne laisse que 16 px
   au texte : « -- Tous les groupes -- » s'y retrouvait coupé par le bas.
   Ensuite la liste des types énumérés ci-dessous oubliait `search` : le champ
   de recherche voisin gardait sa hauteur naturelle, et les deux contrôles
   d'une même rangée ne s'alignaient pas. Le socle pose déjà
   `min-height: var(--control-h)` sur TOUS les contrôles ; le thème n'a qu'à
   valoir `--control-h`, ce qu'il fait. */
body[data-theme="dsm"] input[type="text"],
body[data-theme="dsm"] input[type="email"],
body[data-theme="dsm"] input[type="date"],
body[data-theme="dsm"] input[type="number"],
body[data-theme="dsm"] input[type="search"],
body[data-theme="dsm"] select {
    border-radius: var(--radius-sm);
}

/* L'en-tête du portail devient une barre d'application : plus basse, posée
   sur un filet plutôt que sur une ombre. */
body[data-theme="dsm"] .site-header {
    padding-top: calc(8px + env(safe-area-inset-top, 0px));
    padding-bottom: 8px;
}
/* Sous le bandeau de préproduction, c'est lui qui écarte l'encoche. */
body[data-theme="dsm"][data-environnement] .site-header { padding-top: 8px; }

/* Le pied de page reste discret — dans un outil, on ne le lit jamais — mais
   ⚠️ pas effacé : il porte les mentions légales, qui doivent rester lisibles
   et cliquables. Le premier réglage cumulait la plus petite taille ET une
   opacité de 70 % : deux atténuations l'une sur l'autre, c'était trop. */
/* ⚠️ La classe est `.app-footer` : la règle visait `.site-footer`, qui
   n'existe nulle part — elle n'a donc jamais rien changé. */
body[data-theme="dsm"] .app-footer {
    font-size: var(--fs-md);
    opacity: .9;
}

body[data-theme="dsm"] .app-footer a {
    font-weight: 600;
    color: var(--c-text-doux);
}

/* Au téléphone, les deux liens légaux tiennent sur UNE ligne (repris par le
   propriétaire) : texte plus petit, marges resserrées, pas de coupure. */
@media (max-width: 640px), (pointer: coarse) and (max-height: 500px) {
    body[data-theme="dsm"] .app-footer { font-size: var(--fs-xs); padding-inline: var(--space-2); white-space: nowrap; }
    body[data-theme="dsm"] .app-footer a { margin: 0 var(--space-1); }
}

/* ---------------------------------------------------------------------------
   Le pilotage : des panneaux, pas des zones de couleur
   ---------------------------------------------------------------------------
   Les trois blocs sont peints en bleu, orange et vert pour se distinguer d'un
   coup d'œil. C'est efficace dans une page web, et c'est exactement ce qu'un
   outil de bureau ne fait pas : là, ce qui distingue un panneau est son titre,
   pas sa couleur — laquelle reste disponible pour dire ce qui va et ce qui ne
   va pas. Trois aplats permanents la dépensent pour rien.
   ------------------------------------------------------------------------- */
body[data-theme="dsm"] .pilotage-block,
body[data-theme="dsm"] .pilotage-block--daily,
body[data-theme="dsm"] .pilotage-block--config,
body[data-theme="dsm"] .pilotage-block--super {
    /* La surbrillance très légère des encadrés (jeton `--c-fond-encadre`,
       comme les fieldsets) : le panneau se détache de la carte sans aplat. */
    background: var(--c-fond-encadre);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-md);
    box-shadow: var(--ombre-carte);
    padding: 16px;
}

/* Le titre du panneau reprend la barre de titre des cartes. */
body[data-theme="dsm"] .pilotage-block .pilotage-title,
body[data-theme="dsm"] .pilotage-block--daily .pilotage-title,
body[data-theme="dsm"] .pilotage-block--config .pilotage-title,
body[data-theme="dsm"] .pilotage-block--super .pilotage-title {
    color: var(--c-text-doux);
    font-weight: 500;
    border-bottom: 1px solid var(--c-border);
}

/* Les colonnes se remplissent par hauteur plutôt que l'une après l'autre : la
   première colonne portait douze tuiles quand la troisième en avait trois. */
body[data-theme="dsm"] .pilotage-cols {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    align-items: start;
    gap: 16px;
}

/* Les tuiles posées DANS un panneau de pilotage : `.pilotage-block
   .module-card` leur impose un cadre, qui redonne à la grille l'air d'une
   liste de boutons. Sur un bureau, une icône ne porte pas de cadre tant
   qu'on ne la vise pas. */
body[data-theme="dsm"] .pilotage-block .module-card {
    border-color: transparent;
    background: transparent;
    box-shadow: none;
}

body[data-theme="dsm"] .pilotage-block .module-card:hover {
    border-color: var(--c-border);
    background: var(--c-surface-alt);
}

/* ---------------------------------------------------------------------------
   Les dialogues, en fenêtres d'application
   ------------------------------------------------------------------------- */
body[data-theme="dsm"] .app-dialog {
    border: 1px solid var(--c-border);
    border-radius: var(--radius-md);
    box-shadow: var(--ombre-flottante);
}

/* Une barre de titre, et non un titre posé sur du blanc : c'est elle qui fait
   qu'on saisit une fenêtre plutôt qu'on ne lit un encart. */
body[data-theme="dsm"] .app-dialog-head {
    background: var(--c-surface-alt);
    border-bottom: 1px solid var(--c-border);
    padding: 8px 12px;
}

body[data-theme="dsm"] .app-dialog-head strong {
    color: var(--c-text);
    font-weight: 500;
}

body[data-theme="dsm"] .export-dialog-foot,
body[data-theme="dsm"] .app-dialog-foot {
    background: var(--c-surface-alt);
    border-top: 1px solid var(--c-border);
    padding: 8px 12px;
}

/* L'écran derrière est plus discret : une fenêtre d'outil ne plonge pas le
   reste dans le noir, elle se pose dessus. */
body[data-theme="dsm"] .app-dialog::backdrop {
    background: rgba(33, 38, 45, .28);
}

/* La croix de fermeture, à la manière d'une fenêtre d'application.
   Au repos : un simple trait, sans cadre — c'est déjà le cas. Ce qui change,
   c'est le SURVOL : une zone carrée qui s'allume sous le pointeur, comme la
   case de fermeture d'une fenêtre, plutôt qu'un fond arrondi de bouton.

   ⚠️ L'anneau de focus n'est PAS touché. Il encadre la croix au clavier, et
   c'est ce qui permet de fermer une fenêtre sans souris : le retirer pour
   « faire plus propre » rendrait le dialogue infranchissable au clavier. */
body[data-theme="dsm"] .app-dialog-close {
    width: 28px;
    height: 28px;
    padding: 0;
    border-radius: var(--radius-sm);
    color: var(--c-text-doux);
}

body[data-theme="dsm"] .app-dialog-close:hover {
    background: var(--c-danger-bg);
    color: var(--c-danger-texte);
}

/* ---------------------------------------------------------------------------
   L'en-tête d'écran : une barre de titre, pas une bannière
   ---------------------------------------------------------------------------
   `.section-header` porte un cadre de 4 px, un fond gris, une ombre portée et
   un coin arrondi asymétrique. C'est un bandeau : il annonce. Une fenêtre
   d'application ne s'annonce pas, elle se nomme — un titre et un filet
   suffisent, et tout ce qui est repris ici sert la lisibilité du contenu.
   ------------------------------------------------------------------------- */
body[data-theme="dsm"] .section-header {
    border: 0;
    border-bottom: 1px solid var(--c-border);
    border-radius: 0;
    background-color: transparent;
    box-shadow: none;
    margin-top: 0;
    padding: 0 0 12px;
}

body[data-theme="dsm"] .section-header h2 {
    font-weight: 500;
    color: var(--c-text);
}

/* ⚠️ Un titre posé dans une BARRE DE TITRE n'est pas un sous-titre au fil du
   texte : « Coordonnées », « Représentants légaux », « Enfants de la famille »
   découpent l'écran et doivent se voir de loin. En `h3`, l'échelle du thème les
   ramenait à la taille du texte courant, et les trois sections d'une fiche
   famille se confondaient avec leur contenu. */
body[data-theme="dsm"] .section-header h3,
body[data-theme="dsm"] .section-subheader h3 {
    font-size: var(--fs-titre);
    font-weight: 600;
    color: var(--c-text);
}

/* Le pied de section suit le même raisonnement. */
body[data-theme="dsm"] .section-footer {
    border: 0;
    border-top: 1px solid var(--c-border);
    background-color: transparent;
    padding: 12px 0 0;
}

/* ---------------------------------------------------------------------------
   Les titres de section reprennent la couleur du texte
   ---------------------------------------------------------------------------
   Ils sont en `--c-primary`, ce qui les fait ressortir — et dépense l'accent.
   Dans un outil, l'accent désigne ce sur quoi on AGIT : un bouton, un lien, la
   ligne en cours. Un titre se repère à sa graisse et à sa place ; le peindre
   en couleur enlève à l'accent ce qui fait sa force, c'est-à-dire sa rareté.
   ------------------------------------------------------------------------- */
body[data-theme="dsm"] h2,
body[data-theme="dsm"] h3,
body[data-theme="dsm"] h4 {
    color: var(--c-text);
}

/* ---------------------------------------------------------------------------
   Les onglets : un trait sous celui qui est actif
   ---------------------------------------------------------------------------
   Faute de style propre, ils héritent du bouton générique et ressemblent à une
   rangée de boutons — on ne voit pas qu'ils désignent des vues d'un même
   écran. Aplatis et soulignés, ils redeviennent des onglets.
   ------------------------------------------------------------------------- */
body[data-theme="dsm"] .onglet {
    background: transparent;
    border: 0;
    border-bottom: 2px solid transparent;
    border-radius: 0;
    color: var(--c-text-doux);
    font-weight: 500;
    padding: 0 12px;
    box-shadow: none;
}

body[data-theme="dsm"] .onglet:hover {
    color: var(--c-text);
    background: var(--c-surface-alt);
}

body[data-theme="dsm"] .onglet.is-active {
    color: var(--c-primary);
    border-bottom-color: var(--c-primary);
    background: transparent;
}

/* Le trait qui court sous la rangée, et que l'onglet actif interrompt. */
body[data-theme="dsm"] .document-tabs-bar,
body[data-theme="dsm"] .onglets {
    border-bottom: 1px solid var(--c-border);
}

/* ---------------------------------------------------------------------------
   Les liens au fil du texte prennent l'accent
   ---------------------------------------------------------------------------
   Aucune feuille ne colore un `<a>` ordinaire : faute de règle, il gardait le
   bleu par défaut du navigateur, souligné plein — « Mon profil » dans le
   prérequis des invitations, « page de la campagne », les manuels de l'aide,
   le nom d'une famille dans une liste. Sur un thème dont l'accent désigne ce
   sur quoi on agit, c'était la seule chose cliquable qui ne le portait pas.

   Le soulignement est GARDÉ, adouci : un lien dans une phrase ne se distingue
   pas par sa seule couleur (WCAG 1.4.1). `:where()` et `:not([class])` bornent
   la règle aux liens nus : un bouton-lien, un onglet, une carte ont leur classe
   et leur couleur, et la spécificité reste celle du seul `body[data-theme]` —
   toute règle de composant chargée ensuite l'emporte. */
body[data-theme="dsm"] :where(a:not([class])) {
    color: var(--c-primary);
    text-decoration-color: rgba(var(--c-primary-rgb), .45);
    text-underline-offset: 2px;
}

body[data-theme="dsm"] :where(a:not([class])):hover {
    text-decoration-color: currentColor;
}

/* ===========================================================================
   DSM SUR TÉLÉPHONE
   ===========================================================================
   ⚠️ Les règles de thème ciblent `body[data-theme="dsm"] .x` : elles sont plus
   spécifiques que le `.x` d'une media query, et l'emportaient donc SUR la
   couche mobile. Un thème qui élargit la page et impose des colonnes de 280 px
   faisait déborder l'écran d'un téléphone, sans que rien ne le signale.

   Tout ce qui suit répare cela, et pousse dans la direction voulue : moins une
   page web réduite qu'une application. Les bornes reprennent celles de
   mobile.css, avec la même spécificité de thème, pour reprendre la main.
   ========================================================================= */
@media (max-width: 900px), (pointer: coarse) and (max-height: 500px) {
    /* La page reprend les marges du mobile : 24 px de chaque côté sur un écran
       de 390 en mangent un huitième. */
    body[data-theme="dsm"] .page-container {
        max-width: 100%;
        padding: 0 12px;
        margin: 12px auto;
    }

    /* Les colonnes du pilotage s'empilent : `minmax(280px, 1fr)` ne tient pas
       dans 366 px utiles, et poussait la page hors de l'écran. */
    body[data-theme="dsm"] .pilotage-cols {
        grid-template-columns: 1fr;
        gap: 12px;
    }

    /* Les tuiles : quatre par ligne sur un téléphone, comme un écran d'accueil
       d'application. Des icônes plus petites, mais toutes visibles d'un coup. */
    body[data-theme="dsm"] .module-grid,
    body[data-theme="dsm"] .dashboard-modules {
        grid-template-columns: repeat(auto-fill, minmax(78px, 1fr));
        gap: 8px;
    }
    body[data-theme="dsm"] .module-card {
        min-height: 82px;
        padding: 8px 4px;
        font-size: var(--fs-xs);
    }
    body[data-theme="dsm"] .module-icon {
        width: 40px;
        height: 40px;
        font-size: var(--taille-picto-tuile-mobile);
        border-radius: 8px;
    }

    /* Les panneaux touchent les bords : une application n'encadre pas ses
       listes, elle les pose à plat.
       ⚠️ Fait en retirant le padding du CONTENEUR, et non par des marges
       négatives sur les cartes. Ces dernières décalaient le panneau vers la
       gauche en laissant une bande vide à droite — visible seulement sur un
       vrai téléphone, jamais sur une capture sans interface. */
    /* ⚠️ Pleine largeur SANS marge négative.
       Étendre les cartes par `margin-inline: -12px` marchait pour une carte
       seule, mais un bloc de pilotage POSÉ DANS une carte cumulait les deux
       débordements : la page devenait plus large que l'écran, et il restait à
       droite une bande de fond que l'on prend pour une marge — c'est en
       réalité le défilement horizontal. On retire donc les marges du
       conteneur, et chaque carte donne au texte la sienne par son padding. */
    body[data-theme="dsm"] .page-container {
        padding-inline: 0;
    }

    /* ⚠️ Tout ce qui n'est PAS une carte garde sa marge de lecture. Vider le
       conteneur sans cette règle collait les titres et les paragraphes au bord
       de l'écran — le défaut exact qu'on avait voulu corriger la fois d'avant
       en gardant les marges du conteneur. */
    body[data-theme="dsm"] .page-container > *:not(.card) {
        padding-inline: 12px;
    }
    body[data-theme="dsm"] .card {
        border-left: 0;
        border-right: 0;
        border-radius: 0;
        margin-inline: 0;
        padding-inline: 16px;
        box-shadow: none;
    }

    /* Un bloc imbriqué ne s'étend pas : il est déjà dans une carte pleine
       largeur, et son padding lui suffit. */
    body[data-theme="dsm"] .pilotage-block {
        border-left: 0;
        border-right: 0;
        border-radius: 0;
        margin-inline: 0;
        box-shadow: none;
    }

    /* Les onglets défilent à l'horizontale plutôt que de se casser sur deux
       lignes en désordre. C'est le comportement des applications : la barre
       reste UNE barre, et l'on fait glisser pour atteindre le reste.
       L'onglet actif est amené dans le champ de vision par le script. */
    body[data-theme="dsm"] .onglets,
    body[data-theme="dsm"] .document-tabs-bar {
        flex-wrap: nowrap;
        overflow-x: auto;
        overflow-y: hidden;
        scrollbar-width: none;
        -webkit-overflow-scrolling: touch;
    }
    body[data-theme="dsm"] .onglets::-webkit-scrollbar,
    body[data-theme="dsm"] .document-tabs-bar::-webkit-scrollbar {
        display: none;
    }
    body[data-theme="dsm"] .onglet {
        flex: 0 0 auto;
        white-space: nowrap;
        display: inline-flex;
        align-items: center;
        gap: 8px;
    }

    /* ⚠️ Essai : le libellé ne reste que sur l'onglet ACTIF.
       Un onglet n'a besoin de se nommer que si l'on hésite à y aller ; celui
       où l'on est doit, lui, se lire. Les autres tiennent par leur picto — et
       c'est ce qui fait tenir cinq onglets sur une ligne là où deux libellés
       entiers la remplissaient déjà. Le libellé reste dans le HTML et dans
       l'`aria-label` : rien ne disparaît pour un lecteur d'écran.
       Réserve : ne vaut que pour des onglets dont le picto se devine. Sur des
       onglets qui désignent des populations ou des vues (« Vue d'ensemble » /
       « Vue par branches »), le dessin ne dit rien et le libellé doit rester. */
    /* ⚠️ Seulement à partir de QUATRE onglets. À deux ou trois, les libellés
       tiennent sur la ligne : les retirer ne gagnerait rien et laisserait un
       onglet nommé à côté d'un picto muet, ce qui se lit comme un défaut. */
    body[data-theme="dsm"] .onglets:has(> :nth-child(4)) .onglet:not(.is-active) .onglet-libelle {
        display: none;
    }
    body[data-theme="dsm"] .onglet .picto {
        flex: 0 0 auto;
        /* L'écart est porté par le `gap` du bouton : garder aussi la marge du
           picto le doublerait. */
        margin-right: 0;
    }
    body[data-theme="dsm"] .onglets:has(> :nth-child(4)) .onglet:not(.is-active) {
        padding-inline: 12px;
    }

    /* Un bouton de navigation n'a pas à occuper toute la largeur : il n'est pas
       l'action principale de l'écran. */
    body[data-theme="dsm"] .section-header .btn--secondary {
        width: auto;
    }

    /* Le menu suit l'en-tête. Il était bleu sous une barre blanche : la couleur
       venait de `--c-primary`, que ce thème n'emploie plus pour l'en-tête. */
    body[data-theme="dsm"] .mobile-nav {
        background: var(--c-surface);
        border: 1px solid var(--c-border);
        box-shadow: var(--ombre-flottante);
    }
    body[data-theme="dsm"] .mobile-nav-link {
        color: var(--c-text);
    }
    /* Rubriques du menu des familles : un bloc teinté, le titre à la couleur
       du thème — elles se détachent sur le panneau blanc. */
    body[data-theme="dsm"] .mobile-nav-groupe {
        background: var(--c-surface-alt);
        border: 1px solid var(--c-border);
    }
    body[data-theme="dsm"] .mobile-nav-rubrique {
        color: var(--c-primary);
    }
    body[data-theme="dsm"] .mobile-nav-picto {
        color: var(--c-primary);
    }
    body[data-theme="dsm"] .mobile-nav-link:hover,
    body[data-theme="dsm"] .mobile-nav-link:active {
        background: var(--c-surface-alt);
    }
    body[data-theme="dsm"] .mobile-nav-logout-btn {
        color: var(--c-danger-texte);
    }

    /* Le bouton du menu : son voile et ses barres viennent des jetons
       d'en-tête (cf. `mobile.css`), donc rien à redéfinir ici. */

    /* Les indicateurs : deux par ligne SANS hauteur perdue. Sur un téléphone,
       chaque tuile qui respire trop repousse le reste sous la ligne de
       flottaison — et le pilotage est ce qu'on vient voir. */
    body[data-theme="dsm"] .stat-card {
        padding: 12px 8px;
    }
    body[data-theme="dsm"] .stat-value {
        font-size: var(--taille-picto-tuile);
        line-height: 1.1;
    }

    /* Le bonjour prend deux lignes en très gros et mange un tiers de l'écran :
       un titre d'application se lit, il ne s'annonce pas. */
    body[data-theme="dsm"] .page-container h1 {
        font-size: var(--fs-titre);
        line-height: 1.2;
    }

    /* Les tuiles de chiffres : deux par ligne plutôt qu'une file qui déborde. */
    body[data-theme="dsm"] .kpi-grid,
    body[data-theme="dsm"] .stats-grid {
        display: grid;
        /* ⚠️ `.kpi-grid` porte `grid-auto-flow: column` : redéfinir les colonnes
           sans redéfinir le FLUX laisse les tuiles se ranger en colonnes, et
           cinq indicateurs poussent la page hors de l'écran. C'est ce qui
           débordait encore après les deux premières corrections. */
        grid-auto-flow: row;
        grid-template-columns: repeat(2, 1fr);
        gap: 8px;
    }

    /* L'en-tête se resserre encore : sur un téléphone, chaque pixel vertical
       pris par la barre est un pixel de moins pour le contenu. */
    body[data-theme="dsm"] .site-header {
        padding-top: calc(8px + env(safe-area-inset-top, 0px));
        padding-bottom: 8px;
        padding-left: 12px;
        padding-right: 12px;
    }
    body[data-theme="dsm"][data-environnement] .site-header { padding-top: 8px; }

    /* Rien ne doit dépasser. `overflow-x` sur `body` seul ne suffit pas :
       c'est l'élément racine qui porte le défilement sur la plupart des
       navigateurs mobiles.

       ⚠️ `clip` ET NON `hidden`, et c'est tout sauf un détail. `hidden` fait de
       l'élément un conteneur de défilement : l'en-tête du portail, en
       `position: sticky`, s'accroche alors à ce conteneur — qui ne défile
       jamais — et part avec la page. Mesuré ici : après 500 px de défilement,
       la barre se retrouvait à -489. `mobile.css` avait déjà tranché le 22/09
       et posé `clip` ; cette règle-ci, écrite ensuite et plus spécifique,
       remettait `hidden` et annulait la correction. */
    html:has(body[data-theme="dsm"]),
    body[data-theme="dsm"] {
        overflow-x: clip;
        max-width: 100%;
    }

    /* Un texte long sans espace — un nom de district entre parenthèses, une
       adresse — élargit sa colonne et pousse la page. Il doit céder. */
    body[data-theme="dsm"] .page-container,
    body[data-theme="dsm"] .card {
        overflow-wrap: anywhere;
    }
}


/* ---------------------------------------------------------------------------
   Les pictos d'action : un dessin, pas un bouton
   ---------------------------------------------------------------------------
   Chaque picto portait son cadre et son fond, si bien qu'une rangée d'actions
   devenait une rangée de petites boîtes — quatre carrés avant même d'arriver
   au titre de l'écran. Un outil de bureau les pose nus : c'est la COULEUR qui
   dit l'état, gris au repos, accentuée sous le pointeur, et le dessin gagne la
   place que le cadre lui prenait.

   La surface de touche, elle, ne bouge pas : le bouton garde ses dimensions,
   seul son habillage disparaît.
   ------------------------------------------------------------------------- */
body[data-theme="dsm"] .btn-picto,
body[data-theme="dsm"] .champ-fichier-action {
    border-color: transparent;
    background-color: transparent;
    color: var(--c-text-doux);
}

body[data-theme="dsm"] .btn-picto:hover,
body[data-theme="dsm"] .champ-fichier-action:hover {
    border-color: transparent;
    background-color: transparent;
    color: var(--c-primary);
}

/* Le geste qui détruit vire au rouge sous le pointeur, et seulement là. */
body[data-theme="dsm"] .champ-fichier-action--danger:hover {
    color: var(--c-danger-texte);
}

/* Le dessin reprend les pixels rendus au cadre. */
body[data-theme="dsm"] .btn-picto svg,
body[data-theme="dsm"] .champ-fichier-action svg {
    width: 20px;
    height: 20px;
}

/* Les pictos des colonnes d'action suivent la même règle que les autres : un
   dessin, pas une petite boîte. Une colonne « Actions » alignait des carrés
   bleus et rouges qui, de ligne en ligne, pesaient plus lourd que les données
   qu'on vient lire. Au repos ils sont gris ; la couleur n'apparaît que sous le
   pointeur, et elle dit alors ce que le geste fait — l'accent pour agir, le
   rouge pour détruire. */
body[data-theme="dsm"] .btn-action-column {
    border-color: transparent;
    background-color: transparent;
    color: var(--c-text-doux);
    width: 32px;
    min-width: 32px;
    height: 32px;
}

body[data-theme="dsm"] .btn-action-column:hover {
    border-color: transparent;
    background-color: transparent;
    color: var(--c-primary);
}

body[data-theme="dsm"] .btn-action-delete:hover {
    background-color: transparent;
    color: var(--c-danger-texte);
}

body[data-theme="dsm"] .btn-action-column svg {
    width: 19px;
    height: 19px;
}

/* Un bouton d'action désactivé reste lisible comme tel : pâle, sans survol. */
body[data-theme="dsm"] .btn-action-column.btn-action-disabled,
body[data-theme="dsm"] .btn-action-column[disabled] {
    color: var(--c-ghost-border);
}

/* ⚠️ Le picto d'aide posé AU FIL d'un titre — « Ma fiche sanitaire ? » — est un
   cas à part : sans son cadre, la boîte de 32 px qui le portait le laissait
   flotter sous la ligne du titre. Il se réduit à sa cible de touche minimale
   (24 px, WCAG 2.5.8 — on ne descend pas en dessous, même pour un dessin) et
   se cale sur le milieu de la ligne. */
body[data-theme="dsm"] h1 > .help-picto,
body[data-theme="dsm"] h2 > .help-picto,
body[data-theme="dsm"] h3 > .help-picto {
    width: 24px;
    min-width: 24px;
    height: 24px;
    vertical-align: middle;
    /* Le milieu d'une majuscule n'est pas le milieu de la ligne : un pixel de
       remontée suffit à ce que l'œil les voie alignés. */
    position: relative;
    top: -1px;
}

body[data-theme="dsm"] h1 > .help-picto svg,
body[data-theme="dsm"] h2 > .help-picto svg,
body[data-theme="dsm"] h3 > .help-picto svg {
    width: 18px;
    height: 18px;
}

/* Dans le bandeau de TITRE, le picto d'aide jouxte la flèche « Retour » :
   nu, son rond de 15 px paraissait deux fois plus petit que le carré plein de
   32 px d'à côté. Il y reprend donc un cadre, de la même taille et du même
   gris que la flèche — l'un cerné, l'autre plein (repris par le propriétaire). */
body[data-theme="dsm"] .page-title-row .help-picto {
    border-color: var(--c-neutral);
    border-radius: var(--btn-radius);
}

body[data-theme="dsm"] .page-title-row .help-picto svg {
    width: 22px;
    height: 22px;
}

body[data-theme="dsm"] .page-title-row .help-picto:hover,
body[data-theme="dsm"] .page-title-row .help-picto:focus-visible {
    border-color: var(--c-primary);
}

/* Au téléphone, la flèche « Retour » disparaît (le menu en tient lieu) : le
   picto d'aide n'a plus de voisin à qui répondre, il perd son cadre et ne
   garde que son rond (repris par le propriétaire). */
@media (max-width: 640px), (pointer: coarse) and (max-height: 500px) {
    body[data-theme="dsm"] .page-title-row .help-picto,
    body[data-theme="dsm"] .page-title-row .help-picto:hover,
    body[data-theme="dsm"] .page-title-row .help-picto:focus-visible {
        border-color: transparent;
    }
}

/* Le survol épaissit un filet que ce thème a retiré : c'est la couleur qui
   prend le relais, comme pour les autres pictos. */
body[data-theme="dsm"] .help-picto:hover,
body[data-theme="dsm"] .help-picto:focus-visible {
    border-width: 1px;
    border-color: transparent;
    color: var(--c-primary);
}

/* Le trombone du champ de fichier suit la même règle : il ouvre le sélecteur,
   il n'a pas à se présenter comme un bouton de formulaire. */
body[data-theme="dsm"] input[type="file"]::file-selector-button {
    border-color: transparent;
    background-color: transparent;
    background-size: 20px 20px;
}
