/* =========================================================================
   Cerbère — feuille de style unique.
   =========================================================================

   Avant elle, trois pages recopiaient chacune leur palette et leurs boutons.
   Une couleur changée en tenait deux à jour sur trois, et la troisième
   divergeait sans que rien ne le signale.

   # D'où viennent ces choix

   Des Human Interface Guidelines d'Apple, transposées au web. Ce qui se
   transpose, ce ne sont pas les pixels — c'est la grammaire :

     * une échelle typographique NOMMÉE par rôle (titre, en-tête, corps,
       légende) plutôt que des tailles choisies au cas par cas ;
     * une grille de 4 points ;
     * des surfaces empilées par teinte, pas par ombre — en particulier en
       mode sombre, où Apple élève les plans en les ÉCLAIRCISSANT ;
     * des cibles tactiles de 44 points au minimum ;
     * des séparateurs filets, en retrait du bord d'attaque, pas des traits
       pleins d'un bord à l'autre ;
     * le mouvement au service de la compréhension, et jamais imposé à qui
       demande à s'en passer.

   Les couleurs sont celles des palettes système, qui ont DEUX valeurs : une
   claire et une sombre. Le bleu système vaut #007aff sur fond clair et
   #0a84ff sur fond sombre — le second est plus lumineux parce qu'un bleu
   sombre sur fond noir se lit mal. C'est vrai de toute la palette, d'où les
   deux jeux ci-dessous plutôt qu'une inversion mécanique.

   ========================================================================= */

/* -------------------------------------------------------------------------
   1. Jetons — clair par défaut
   ------------------------------------------------------------------------- */

:root {
    color-scheme: light;

    /* Fonds. « grouped » : le fond de la page est plus SOMBRE que les cartes
       posées dessus. C'est l'inverse en mode sombre, et c'est voulu. */
    --fond:          #f2f2f7;
    --surface:       #ffffff;
    --surface-2:     #f2f2f7;
    --surface-3:     #e5e5ea;

    /* Textes. Les niveaux secondaires sont des NOIRS TRANSPARENTS, pas des
       gris opaques : posés sur une carte blanche ou sur le fond gris, ils
       gardent le même rapport au support. */
    --texte:         #1c1c1e;
    --texte-2:       rgba(60, 60, 67, .60);
    --texte-3:       rgba(60, 60, 67, .30);

    --filet:         rgba(60, 60, 67, .22);
    --filet-fort:    rgba(60, 60, 67, .36);

    --accent:        #007aff;
    --accent-doux:   rgba(0, 122, 255, .12);
    --rouge:         #d70015;
    --vert:          #248a3d;
    --orange:        #c93400;
    --ambre:         #b25000;
    /* ⚠️ Ce que le RÉSEAU accorde, pas ce que le traceur fait — CERBERE-99.
       Volontairement hors de la palette d'état (vert/ambre/rouge/gris) : ce
       sont des bandes d'une autre nature, sur leur propre ligne. Rose pour
       l'eDRX (écoute, activité), cyan pour le PSM (sommeil). */
    --m-radio-edrx:  #c2185b;
    --m-radio-psm:   #00838f;
    --sarcelle:      #0a7c86;

    /* Mesures : orange chaud, bleu, sarcelle, vert. Assez distinctes pour un
       daltonien deutéranope, ce que « rouge / vert » ne serait pas. */
    --m-temp:        var(--orange);
    --m-hum:         var(--accent);
    --m-press:       var(--sarcelle);
    --m-batt:        var(--vert);
    /* L'activité : violet. ⚠️ Pas une des quatre teintes déjà prises — la
       cinquième courbe doit se distinguer des autres au premier regard, et le
       violet reste séparable du bleu pour un deutéranope. */
    --m-act:         #8e44c9;
    --m-inc:         #8a6d3b;
    /* L'ATTENTE : indigo. ⚠️ Volontairement hors du barème vert / ambre /
       rouge de la carte Santé, qui dit « bien / attention / mal ». Un temps
       restant ne vaut ni bien ni mal, et le peindre en gris le ferait lire
       comme une jauge d'état DÉSACTIVÉE quand il voisine deux barres vertes —
       constaté en comparant les deux côte à côte le 03/10. L'indigo le place
       sur son propre axe : une autre nature de grandeur, pas un verdict.
       ⚠️ Et pas le violet `--m-act`, déjà pris par l'activité. */
    --m-attente:     #5856d2;

    --ombre-carte:   0 1px 2px rgba(0, 0, 0, .05);
    --voile:         rgba(242, 242, 247, .82);
}

/* Sombre AUTOMATIQUE. Le `:not([data-theme="light"])` est indispensable :
   sans lui, un visiteur qui force le mode clair sur un système en sombre
   verrait cette règle écraser son choix. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        color-scheme: dark;

        --fond:        #000000;
        --surface:     #1c1c1e;
        --surface-2:   #2c2c2e;
        --surface-3:   #3a3a3c;

        --texte:       #ffffff;
        --texte-2:     rgba(235, 235, 245, .60);
        --texte-3:     rgba(235, 235, 245, .30);

        --filet:       rgba(84, 84, 88, .60);
        --filet-fort:  rgba(84, 84, 88, .90);

        --accent:      #0a84ff;
        --accent-doux: rgba(10, 132, 255, .22);
        --rouge:       #ff453a;
        --vert:        #30d158;
        --orange:      #ff9f0a;
        --ambre:       #ffd60a;
        --m-radio-edrx: #ff66c4;
        --m-radio-psm:  #40e0d0;
        --sarcelle:    #40cbe0;

        --m-temp:      #ff6b35;
        --m-hum:       var(--accent);
        --m-press:     var(--sarcelle);
        --m-batt:      var(--vert);
        --m-act:       #bf5af2;
        /* Remonté en mode sombre, comme toutes les teintes : #5856d2 y
           disparaîtrait dans le fond. */
        --m-attente:   #5e5ce6;
        --m-inc:       #d9a441;

        --ombre-carte: none;
        --voile:       rgba(0, 0, 0, .72);
    }
}

/* Sombre FORCÉ. Dupliqué plutôt que factorisé : une liste de sélecteurs
   `:root[data-theme="dark"], @media …` n'est pas exprimable en CSS, et un
   préprocesseur pour sept déclarations serait un outil de plus à installer. */
:root[data-theme="dark"] {
    color-scheme: dark;

    --fond:        #000000;
    --surface:     #1c1c1e;
    --surface-2:   #2c2c2e;
    --surface-3:   #3a3a3c;

    --texte:       #ffffff;
    --texte-2:     rgba(235, 235, 245, .60);
    --texte-3:     rgba(235, 235, 245, .30);

    --filet:       rgba(84, 84, 88, .60);
    --filet-fort:  rgba(84, 84, 88, .90);

    --accent:      #0a84ff;
    --accent-doux: rgba(10, 132, 255, .22);
    --rouge:       #ff453a;
    --vert:        #30d158;
    --orange:      #ff9f0a;
    --ambre:       #ffd60a;
    --m-radio-edrx: #ff66c4;
    --m-radio-psm:  #40e0d0;
    --sarcelle:    #40cbe0;

    --m-temp:      #ff6b35;
    --m-hum:       var(--accent);
    --m-press:     var(--sarcelle);
    --m-batt:      var(--vert);
    --m-act:       #bf5af2;
    --m-inc:       #d9a441;

    --ombre-carte: none;
    --voile:       rgba(0, 0, 0, .72);
}

/* -------------------------------------------------------------------------
   2. Typographie et mesures
   ------------------------------------------------------------------------- */

:root {
    /* `system-ui` d'abord : sur un Mac ou un iPhone c'est San Francisco, la
       police pour laquelle ces proportions ont été dessinées. Ailleurs, c'est
       la police du système, ce qui reste le bon choix. */
    --police: system-ui, -apple-system, "SF Pro Text", "Segoe UI", Roboto, sans-serif;
    --police-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;

    /* Rôles, pas tailles. 17 px pour le corps : c'est la valeur d'Apple, et
       elle est plus généreuse que le 16 px habituel du web. */
    --t-grand-titre: clamp(28px, 4vw + 16px, 34px);
    --t-titre:       22px;
    --t-titre-3:     20px;
    --t-entete:      17px;
    --t-corps:       17px;
    --t-appel:       16px;
    --t-sous-titre:  15px;
    --t-note:        13px;
    --t-legende:     12px;

    /* Grille de 4 points. */
    --e1: 4px;  --e2: 8px;  --e3: 12px; --e4: 16px;
    --e5: 20px; --e6: 24px; --e7: 32px; --e8: 44px;

    --r-s: 10px; --r-m: 14px; --r-l: 20px; --r-max: 999px;

    /* 44 points : le minimum d'une cible tactile chez Apple. Un bouton plus
       petit se rate au pouce, et la souris n'excuse pas le téléphone. */
    --cible: 44px;

    --duree: .22s;
    --courbe: cubic-bezier(.32, .72, 0, 1);
}

/* Le mouvement aide à comprendre ce qui se passe ; il ne s'impose pas à qui
   demande à s'en passer, pour vertige ou par préférence. */
@media (prefers-reduced-motion: reduce) {
    :root { --duree: 0s; }
    *, *::before, *::after { animation-duration: 0s !important; transition-duration: 0s !important; }
}

/* -------------------------------------------------------------------------
   3. Socle
   ------------------------------------------------------------------------- */

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

/* `hidden` doit gagner. N'importe quelle règle d'auteur qui pose `display`
   — et il y en a, sur les grilles et les barres — l'emporte sinon sur le
   `display: none` du navigateur, et l'élément « caché » reste visible.
   Constaté sur les graphiques du tableau de bord. */
[hidden] { display: none !important; }

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

body {
    margin: 0;
    /* `env(safe-area-inset-*)` : sur un iPhone, l'encoche et la barre
       d'accueil mangent la page. Sans ça, un bouton du bas devient
       inatteignable en paysage. */
    padding:
        max(var(--e6), env(safe-area-inset-top))
        max(var(--e4), env(safe-area-inset-right))
        max(var(--e8), env(safe-area-inset-bottom))
        max(var(--e4), env(safe-area-inset-left));
    font-family: var(--police);
    font-size: var(--t-corps);
    line-height: 1.45;
    color: var(--texte);
    background: var(--fond);
    /* Le thème bascule sans clignoter d'un blanc cru. */
    transition: background-color var(--duree) var(--courbe), color var(--duree) var(--courbe);
}

.contenu { max-width: 760px; margin: 0 auto; }
.contenu.large { max-width: 1100px; }

h1, h2, h3 { margin: 0; font-weight: 600; letter-spacing: -.01em; }
p { margin: 0; }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

/* Visible au clavier, invisible à la souris : `:focus-visible` et non
   `:focus`. Supprimer le contour rendrait le site inutilisable au clavier. */
:focus-visible {
    outline: 2.5px solid var(--accent);
    outline-offset: 2px;
    border-radius: var(--r-s);
}

.mono { font-family: var(--police-mono); font-size: .92em; }
.discret { color: var(--texte-2); }
.chiffres { font-variant-numeric: tabular-nums; }

/* -------------------------------------------------------------------------
   4. Barre de titre
   ------------------------------------------------------------------------- */

.barre {
    display: flex; align-items: flex-end; gap: var(--e3);
    flex-wrap: wrap; margin-bottom: var(--e2);
}
.barre h1 {
    font-size: var(--t-grand-titre); font-weight: 700;
    letter-spacing: -.02em; line-height: 1.1; margin-right: auto;
}
.barre nav { display: flex; gap: var(--e4); font-size: var(--t-sous-titre); padding-bottom: 3px; }
/* L'avatar ferme la barre, après la navigation.
   `align-self: center` : il est plus haut que le texte, et s'alignerait sinon
   sur sa ligne de base.
   `margin-left: auto` : il doit toucher le bord droit. À largeur téléphone la
   barre se replie, le titre prend sa ligne, et sans ça l'avatar restait collé
   à la navigation — son menu, ancré à droite de lui, débordait alors de 28 px
   hors de l'écran, à gauche. */
.barre .compte { align-self: center; margin-left: auto; }
.barre nav a { font-weight: 500; }
.sous-titre { color: var(--texte-2); font-size: var(--t-sous-titre); margin-bottom: var(--e6); }

/* -------------------------------------------------------------------------
   5. Surfaces
   ------------------------------------------------------------------------- */

.carte {
    background: var(--surface);
    border-radius: var(--r-l);
    box-shadow: var(--ombre-carte);
    padding: var(--e5);
}
.carte.serree { padding: var(--e4); }
/* Une liste occupe toute la largeur de sa carte : ses séparateurs doivent
   courir jusqu'aux bords, la marge est portée par les lignes. */
.carte.liste { padding: 0; overflow: hidden; }

.titre-section {
    font-size: var(--t-note); font-weight: 600; text-transform: uppercase;
    letter-spacing: .06em; color: var(--texte-2);
    margin: var(--e7) 0 var(--e2); padding-left: var(--e1);
}

/* -------------------------------------------------------------------------
   6. Lignes de liste
   ------------------------------------------------------------------------- */

.ligne {
    display: flex; align-items: center; gap: var(--e4);
    padding: var(--e3) var(--e5);
    min-height: var(--cible);
    position: relative;
}
/* Le filet part APRÈS l'icône, pas du bord de la carte : c'est ce retrait
   qui rattache visuellement chaque ligne à son symbole. */
.ligne + .ligne::before {
    content: ''; position: absolute; top: 0; left: var(--e5); right: 0;
    height: 1px; background: var(--filet); transform: scaleY(.5);
}
.ligne.avec-icone + .ligne.avec-icone::before { left: calc(var(--e5) + 34px + var(--e4)); }
.ligne .corps { flex: 1; min-width: 0; }
.ligne .titre { font-size: var(--t-corps); font-weight: 600; }
.ligne .titre.absent { font-weight: 400; color: var(--texte-2); font-style: italic; }
.ligne .detail {
    color: var(--texte-2); font-size: var(--t-note); margin-top: 2px;
    font-family: var(--police-mono); overflow-wrap: anywhere;
}
.ligne .actions { display: flex; gap: var(--e2); flex: none; }
.ligne svg.symbole { width: 34px; height: 34px; flex: none; }

/* À largeur téléphone, les actions passent sous l'identité. Sans ça, le nom
   se comprime à un caractère par ligne : l'IMEI est une chaîne insécable de
   quinze chiffres, et deux boutons lui prennent toute la place. */
@media (max-width: 560px) {
    .ligne { flex-wrap: wrap; row-gap: var(--e3); }
    .ligne .corps { flex-basis: calc(100% - 34px - var(--e4)); }
    .ligne .actions { width: 100%; }
    .ligne .actions .btn { flex: 1; }
    .ligne.avec-icone + .ligne.avec-icone::before { left: var(--e5); }
}

.vide { color: var(--texte-2); font-size: var(--t-appel); padding: var(--e5); text-align: center; }
/* ⚠️ Une carte qui a DÉJÀ sa marge intérieure n'en veut pas une seconde.
   `.vide` porte la sienne pour les cartes-listes (`.carte.liste`, marge nulle),
   où elle est indispensable. Posée en plus de celle d'une carte ordinaire, elle
   doublait l'épaisseur : mesuré, 20 px + 20 px de chaque côté pour une seule
   ligne de texte. */
.carte:not(.liste) > .vide { padding: var(--e3) 0; }

/* -------------------------------------------------------------------------
   7. Boutons
   ------------------------------------------------------------------------- */

.btn {
    display: inline-flex; align-items: center; justify-content: center; gap: var(--e2);
    min-height: 34px; padding: var(--e2) var(--e4);
    font: inherit; font-size: var(--t-sous-titre); font-weight: 500;
    color: var(--accent); background: transparent;
    border: none; border-radius: var(--r-max); cursor: pointer;
    transition: background-color var(--duree) var(--courbe), opacity var(--duree) var(--courbe);
    -webkit-tap-highlight-color: transparent;
}
.btn:hover { background: var(--accent-doux); }
.btn:active { opacity: .65; }
.btn:disabled { opacity: .4; cursor: default; }
.btn:disabled:hover { background: transparent; }

.btn.plein { background: var(--accent); color: #fff; font-weight: 600; min-height: var(--cible); padding-inline: var(--e6); }
.btn.plein:hover { background: var(--accent); filter: brightness(1.06); }
.btn.teinte { background: var(--accent-doux); }
.btn.contour { border: 1px solid var(--filet); color: var(--texte); }
.btn.contour:hover { background: var(--surface-2); border-color: var(--filet-fort); }
.btn.danger { color: var(--rouge); }
.btn.danger:hover { background: color-mix(in srgb, var(--rouge) 12%, transparent); }
.btn.pleine-largeur { width: 100%; }

/* -------------------------------------------------------------------------
   8. Contrôle segmenté
   ------------------------------------------------------------------------- */

.segmente {
    position: relative; display: flex; gap: 2px;
    padding: 2px; border-radius: var(--r-s);
    background: var(--surface-3);
    overflow-x: auto; scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
}
.segmente::-webkit-scrollbar { display: none; }

/* Le curseur glisse d'un segment à l'autre. Positionné en JS (`theme.js`) :
   les segments n'ont pas tous la même largeur. Si le script ne tourne pas,
   le segment choisi reste identifiable par son fond — voir plus bas. */
.segmente .curseur {
    position: absolute; top: 2px; bottom: 2px; left: 0;
    border-radius: calc(var(--r-s) - 2px);
    background: var(--surface);
    box-shadow: 0 1px 3px rgba(0, 0, 0, .12), 0 0 0 .5px rgba(0, 0, 0, .04);
    transition: transform var(--duree) var(--courbe), width var(--duree) var(--courbe);
    pointer-events: none;
}
:root[data-theme="dark"] .segmente .curseur,
:root:not([data-theme="light"]) .segmente .curseur { box-shadow: 0 1px 3px rgba(0, 0, 0, .4); }

.segmente button {
    position: relative; z-index: 1; flex: 0 0 auto;
    min-height: 30px; padding: 0 var(--e3);
    font: inherit; font-size: var(--t-note); font-weight: 500;
    color: var(--texte); background: transparent; border: none;
    border-radius: calc(var(--r-s) - 2px); cursor: pointer; white-space: nowrap;
    -webkit-tap-highlight-color: transparent;
}
.segmente button[aria-checked="true"] { font-weight: 600; }
/* Filet de sécurité : sans JS, pas de curseur, donc pas de repère. */
.segmente:not(.anime) button[aria-checked="true"] { background: var(--surface); }
.segmente button:active { opacity: .6; }

/* --- Pastilles à bascule (`pastilles()`, theme.js) ------------------------

   ⚠️ Délibérément DIFFÉRENTES du segmenté juste au-dessus : coins ronds,
   filet, fond transparent, elles ne vivent pas dans une gouttière. Un
   segmented control veut dire « un parmi N », ces pastilles « autant qu'on
   veut parmi N ». Deux sens, deux apparences — sinon on clique en croyant
   remplacer et on ajoute. */

.pastilles { display: flex; flex-wrap: wrap; gap: var(--e2); align-items: center; }

.pastilles button:not(.compteur) {
    display: inline-flex; align-items: center; gap: var(--e2);
    min-height: 30px; padding: 0 var(--e3);
    font: inherit; font-size: var(--t-note); font-weight: 500;
    color: var(--texte-2); background: transparent;
    border: 1px solid var(--filet); border-radius: 999px;
    cursor: pointer; white-space: nowrap; user-select: none;
    -webkit-tap-highlight-color: transparent;
    transition: background var(--duree) var(--courbe),
                border-color var(--duree) var(--courbe),
                opacity var(--duree) var(--courbe);
}
.pastilles .point {
    width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto;
    background: var(--texte-2); opacity: .45;
    transition: opacity var(--duree) var(--courbe);
}
.pastilles button[aria-checked="true"] {
    color: var(--texte); font-weight: 600;
    background: var(--surface); border-color: transparent;
    box-shadow: 0 1px 3px rgba(0, 0, 0, .12), 0 0 0 .5px rgba(0, 0, 0, .06);
}
:root[data-theme="dark"] .pastilles button[aria-checked="true"],
:root:not([data-theme="light"]) .pastilles button[aria-checked="true"] {
    background: var(--surface-3); box-shadow: none;
}
.pastilles button[aria-checked="true"] .point { opacity: 1; }
/* Le dernier sélectionné est celui mis en avant sur les courbes : un anneau
   discret le dit, sans lui donner une couleur qui mentirait sur son identité. */
.pastilles button.avant .point { box-shadow: 0 0 0 3px color-mix(in srgb, currentColor 18%, transparent); }

/* Au plafond. `opacity` et PAS `disabled` : le clic simple doit continuer de
   marcher — il affiche ce traceur seul. Un bouton mort n'expliquerait rien. */
.pastilles button.plein { opacity: .4; }

.pastilles .compteur {
    font: inherit; font-size: var(--t-legende); color: var(--texte-2);
    background: transparent; border: none; cursor: pointer;
    min-height: 30px; padding: 0 var(--e2);
    -webkit-tap-highlight-color: transparent;
}
.pastilles .compteur:hover { color: var(--texte); }

/* -------------------------------------------------------------------------
   9. Champs
   ------------------------------------------------------------------------- */

.champ { margin-bottom: var(--e4); }

/* Une bascule : la case et son libellé sur une ligne, cliquables ensemble.
   ⚠️ `align-items: flex-start` — un libellé qui passe à la ligne ne doit pas
   faire flotter la case au milieu de deux lignes de texte. */
.bascule {
    display: flex; align-items: flex-start; gap: var(--e2);
    cursor: pointer; font-weight: 400 !important; color: var(--texte) !important;
}
.bascule input { margin: 2px 0 0; flex: none; accent-color: var(--accent); }
/* L'explication d'un réglage vit sous lui, en petit : elle se lit une fois,
   et ne doit pas peser autant que le réglage lui-même. */
.indice {
    font-size: var(--t-note); color: var(--texte-2);
    margin: var(--e2) 0 0; line-height: 1.45;
}
.indice b { font-weight: 600; color: var(--texte); }

/* --- Les sections d'une feuille de réglages (CERBERE-75) ---------------------
   ⚠️ La frontière n'est PAS thématique : elle sépare ce qui s'applique tout de
   suite de ce qui doit voyager jusqu'à un appareil qui dort. L'étiquette de
   droite le dit, pour qu'on n'ait pas à le deviner. */
.feuille h3.section {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: var(--e3); margin: var(--e5) 0 var(--e3);
    font-size: var(--t-note); font-weight: 600; color: var(--texte-2);
    text-transform: uppercase; letter-spacing: .04em;
    padding-bottom: var(--e1); border-bottom: 1px solid var(--filet);
}
.feuille h3.section .aussitot,
.feuille h3.section .voyage {
    font-size: var(--t-note); font-weight: 400; letter-spacing: 0;
    text-transform: none; color: var(--texte-3);
}
.feuille h3.section .voyage { color: var(--ambre); }

/* L'état d'un réglage qui doit voyager : demandé, appliqué, ou refusé.
   ⚠️ Jamais vide quand un réglage existe — « on ne sait pas » se dit. */
.etat {
    font-size: var(--t-note); margin: var(--e2) 0 0;
    font-variant-numeric: tabular-nums;
}
.etat.attente { color: var(--ambre); }
.etat.applique { color: var(--texte-2); }

/* ⚠️ Un réglage présent mais NON DISPONIBLE. On le montre plutôt que de le
   cacher : savoir qu'il existe et pourquoi il ne l'est pas vaut mieux que de
   le chercher. Mais il ne doit jamais avoir l'air actionnable. */
.bascule-off { cursor: default; opacity: .72; }
.bascule-off input { cursor: default; }
select:disabled { opacity: .72; cursor: default; }
/* `.carte-connexion label` est nommée ici plutôt que restylée plus bas :
   l'écran de connexion est construit en JavaScript (app.js) et n'a pas de
   `.champ` autour de son champ. Sans cette ligne, son étiquette retombait sur
   les réglages par défaut — 17 px, centrée par la carte. */
.champ label, .etiquette, .carte-connexion label {
    display: block; font-size: var(--t-note); font-weight: 500;
    color: var(--texte-2); margin-bottom: var(--e1);
}
input[type="text"], input[type="email"], input:not([type]), select, textarea {
    width: 100%; font: inherit; font-size: var(--t-corps);
    min-height: var(--cible);
    color: var(--texte); background: var(--surface-2);
    border: 1px solid transparent; border-radius: var(--r-s);
    padding: var(--e2) var(--e3);
    transition: border-color var(--duree) var(--courbe), background-color var(--duree) var(--courbe);
}
/* La bordure teintée pour un clic ; l'anneau complet pour le clavier.
   ⚠️ Ne PAS neutraliser `:focus-visible` sur les champs : une bordure de 1 px
   ne se voit pas d'un coup d'œil, et c'est le seul repère de qui navigue au
   clavier. Constaté sur le portail — le champ e-mail était focalisé sans que
   rien ne le dise. */
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--accent); }
input:focus-visible, select:focus-visible, textarea:focus-visible {
    outline: 2.5px solid var(--accent);
    outline-offset: 2px;
}
input::placeholder { color: var(--texte-3); }
select {
    appearance: none;
    /* Le chevron est dessiné en SVG inline plutôt que chargé : une icône de
       12 octets ne vaut pas une requête. `currentColor` ne traverse pas une
       `data:` URI, d'où les deux variantes selon le thème, plus bas. */
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%233c3c43' stroke-width='1.6' stroke-linecap='round'%3E%3Cpath d='m4 6.5 4 4 4-4'/%3E%3C/svg%3E");
    background-repeat: no-repeat; background-position: right var(--e3) center;
    background-size: 16px; padding-right: var(--e7);
}
:root[data-theme="dark"] select,
:root:not([data-theme="light"]) select {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23ebebf5' stroke-width='1.6' stroke-linecap='round'%3E%3Cpath d='m4 6.5 4 4 4-4'/%3E%3C/svg%3E");
}

.grille-2 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--e4); }
/* Autonome, et non `.grille-2 .pleine` : la page des mises à jour a sa propre
   grille, et la règle ne s'y appliquait pas — sa note restait coincée dans une
   colonne de 190 px. */
.pleine { grid-column: 1 / -1; }
@media (max-width: 560px) { .grille-2 { grid-template-columns: 1fr; } }

/* -------------------------------------------------------------------------
   10. Messages
   ------------------------------------------------------------------------- */

.message {
    font-size: var(--t-sous-titre); line-height: 1.4;
    margin-top: var(--e3); min-height: 1.2em;
    color: var(--texte-2);
}
.message.ok { color: var(--vert); }
.message.erreur { color: var(--rouge); }
/* ⚠️ Ni succès ni erreur : « c'est enregistré, et ça attend l'appareil ». Sans
   cette troisième teinte, une demande en attente s'afficherait en vert — donc
   comme faite — ou en rouge — donc comme échouée. Même vocabulaire que
   `.etat.attente`, qui dit déjà la même chose des réglages. */
.message.attente { color: var(--ambre); }

.note { color: var(--texte-2); font-size: var(--t-note); line-height: 1.5; }

.alerte {
    display: flex; gap: var(--e3); align-items: flex-start;
    background: color-mix(in srgb, var(--ambre) 14%, var(--surface));
    border-radius: var(--r-m); padding: var(--e3) var(--e4);
    font-size: var(--t-sous-titre); margin-bottom: var(--e4);
}

.pastille-etat {
    display: inline-block; width: 8px; height: 8px; border-radius: var(--r-max);
    background: var(--vert); flex: none;
}
.pastille-etat.muet { background: var(--texte-3); }

.etiquette-pilule {
    display: inline-block; font-size: var(--t-legende); font-weight: 600;
    text-transform: uppercase; letter-spacing: .05em;
    color: var(--texte-2); background: var(--surface-2);
    border-radius: var(--r-max); padding: 3px var(--e2);
}

/* -------------------------------------------------------------------------
   11. Feuille modale
   ------------------------------------------------------------------------- */

/* ⚠️ La feuille ÉPOUSE la fenêtre, au lieu de rester un timbre-poste.
   Mesuré le 02/10 : 420 px de large et 720 px de haut au maximum, pour
   1932 px de contenu — trois écrans de défilement dans une fenêtre qui en
   offrait largement la place. Le défilement n'était pas une fatalité, c'était
   une largeur mal choisie.

   ⚠️ Mais pas de largeur infinie non plus : au-delà d'environ 70 caractères,
   une ligne de texte devient pénible à suivre, et un formulaire étalé sur
   1400 px fait voyager l'œil pour rien.

   ⚠️ Ce plafond se compte PAR COLONNE, pas sur la feuille. La version à
   820 px l'appliquait à la feuille entière alors que le formulaire est déjà
   en deux colonnes : chaque colonne ne faisait que ~60 caractères, soit bien
   en dessous du confort — on se privait de largeur pour respecter une limite
   déjà respectée deux fois. 960 px donne des colonnes de ~460 px, environ
   72 caractères : la limite est atteinte là où elle veut dire quelque chose. */
dialog.feuille {
    color: var(--texte); background: var(--surface);
    border: none; border-radius: var(--r-l);
    padding: var(--e5); width: min(960px, calc(100vw - var(--e7)));
    max-height: calc(100vh - var(--e7)); overflow-y: auto;
    box-shadow: 0 20px 60px rgba(0, 0, 0, .25);
}

/* ⚠️ DEUX COLONNES dès qu'il y a la place, et c'est ce qui supprime vraiment
   le défilement : ce sont les explications sous chaque réglage qui mangent la
   hauteur, pas les contrôles.

   La grille est posée sur le FORMULAIRE, pas sur la feuille : les titres de
   section, les rangées d'icônes et le pied doivent traverser les deux
   colonnes, et un `grid-column: 1 / -1` ne vaut que dans sa propre grille. */
@media (min-width: 720px) {
    dialog.feuille form {
        display: grid; grid-template-columns: 1fr 1fr;
        column-gap: var(--e5); align-items: start;
    }
    dialog.feuille form > h2,
    dialog.feuille form > h3.section,
    dialog.feuille form > .message,
    dialog.feuille form > .pied,
    dialog.feuille form > .champ-large { grid-column: 1 / -1; }
}

/* ⚠️ Le rythme vertical d'une FEUILLE DE RÉGLAGES n'est pas celui d'une page.
   Mesuré le 02/10 à 1280×900 : sur 1262 px de contenu, 212 px ne sont que des
   marges entre champs. Dans une page qu'on parcourt, cet air aide à lire ;
   dans un formulaire qu'on veut embrasser d'un coup d'œil, il ajoute un écran
   de défilement pour rien. On resserre ici, et nulle part ailleurs. */
dialog.feuille .champ { margin-bottom: var(--e3); }
dialog.feuille h3.section { margin-top: var(--e4); }
/* Les gestes qu'on ne peut pas défaire portent la couleur de leurs boutons :
   un titre neutre au-dessus de deux boutons rouges se lirait comme une section
   ordinaire qu'on parcourt sans s'arrêter. */
.feuille h3.section.danger-titre { color: var(--rouge); }

/* ⚠️ Un conteneur d'erreur VIDE occupe quand même sa ligne. Celui-ci ne porte
   un message qu'après un échec d'enregistrement : le reste du temps il poussait
   le pied de 18 px vers le bas sans que rien ne soit affiché. */
dialog.feuille .message:empty { display: none; }

/* ⚠️ Ce qui RESTE, mesuré le 02/10, pour que personne ne recommence l'enquête :
   la feuille complète faisait 1244 px de contenu, et 1457 px depuis que la
   section « Voyant lumineux » s'y est ajoutée (CERBERE-78). Elle ne défile donc
   plus à partir d'environ 1490 px de hauteur de fenêtre, et défile encore en
   dessous. ⚠️ Ce nombre MONTE à chaque réglage ajouté — il se remesure, il ne
   se recopie pas. Ce reliquat n'est PAS de la mise en page perdue : c'est le texte
   explicatif lui-même, dont les trois paragraphes de « Fréquence des relevés »
   font 271 px à eux seuls. Mesuré : passer la colonne de 390 à 450 px ne lui a
   pas fait perdre un pixel — sa hauteur vient du nombre de phrases, pas du
   repli. Élargir encore ne gagnerait donc rien, et raccourcir les explications
   reviendrait à retirer ce pour quoi la feuille existe.

   ⚠️ Et trois colonnes ne gagnent rien non plus : les titres de section
   coupent les rangées, si bien que la plus haute reste dictée par le même
   champ. Vérifié avant d'ajouter la complexité, pas après.

   C'est pourquoi le pied ÉPINGLÉ ci-dessous reste nécessaire : sous 1276 px,
   « Enregistrer » doit rester atteignable sans avoir à chercher le bas. */
/* ⚠️ Une feuille ÉTROITE pour un formulaire court. La feuille de réglages fait
   960 px et deux colonnes parce qu'elle porte une quinzaine de champs ; imposer
   la même largeur à un menu déroulant et deux boutons ferait un vide absurde,
   et la grille à deux colonnes y poserait le champ d'un côté et rien de
   l'autre. */
dialog.feuille.feuille-etroite { width: min(520px, calc(100vw - var(--e7))); }
@media (min-width: 720px) {
    dialog.feuille.feuille-etroite form { display: block; }
}

dialog.feuille::backdrop { background: rgba(0, 0, 0, .42); backdrop-filter: blur(3px); }
dialog.feuille h2 { font-size: var(--t-titre-3); margin-bottom: var(--e4); }
/* ⚠️ Le pied est ÉPINGLÉ, et ce n'est pas un raffinement.
   Mesuré le 02/10 : avec les sections de réglages, la feuille fait 1932 px de
   contenu dans 720 px de hauteur. Le bouton « Enregistrer » se retrouvait à
   trois écrans de défilement — invisible au moment où l'on croit avoir fini,
   donc un formulaire qu'on abandonne sans l'avoir enregistré.

   `position: sticky` à l'intérieur d'un conteneur qui défile : il reste collé
   en bas tant qu'il y a du contenu dessous. ⚠️ Un fond OPAQUE est obligatoire —
   sans lui, le texte défilerait visiblement dessous. */
dialog.feuille .pied {
    position: sticky; bottom: calc(-1 * var(--e5));
    display: flex; justify-content: flex-end; gap: var(--e2);
    margin-top: var(--e5); padding: var(--e4) 0 var(--e5);
    border-top: 1px solid var(--filet);
    background: var(--surface);
}

/* -------------------------------------------------------------------------
   12. Sélecteurs d'icône et de couleur
   ------------------------------------------------------------------------- */

.choix { display: flex; flex-wrap: wrap; gap: var(--e2); }
.choix button {
    width: 44px; height: 44px; padding: 0;
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--surface-2); border: 2px solid transparent;
    border-radius: var(--r-m); cursor: pointer;
    transition: border-color var(--duree) var(--courbe), transform var(--duree) var(--courbe);
    -webkit-tap-highlight-color: transparent;
}
.choix button[aria-pressed="true"] { border-color: var(--texte); }
.choix button:active { transform: scale(.92); }
.choix button svg { width: 24px; height: 24px; }
/* Sélecteur composé : `.choix button` porte déjà une classe et un type, donc
   une classe seule ne pourrait pas le surcharger. */
.choix button.teinte-ronde { width: 34px; height: 34px; border-radius: var(--r-max); }

/* -------------------------------------------------------------------------
   13. Tableaux
   ------------------------------------------------------------------------- */

table { width: 100%; border-collapse: collapse; font-size: var(--t-sous-titre); }
th {
    text-align: left; font-size: var(--t-legende); font-weight: 600;
    text-transform: uppercase; letter-spacing: .05em; color: var(--texte-2);
    padding: 0 var(--e3) var(--e2) 0;
}
td { padding: var(--e3) var(--e3) var(--e3) 0; border-top: 1px solid var(--filet); vertical-align: middle; }
tr:first-child td { border-top: none; }

/* -------------------------------------------------------------------------
   13 bis. Compte — l'avatar et son menu
   ------------------------------------------------------------------------- */

.compte { position: relative; flex: none; }

/* L'initiale dans un rond. Pas de photo : il faudrait demander la portée
   `profile` au fournisseur, donc collecter un nom et une image pour un gain
   purement décoratif. Une initiale suffit à distinguer deux comptes. */
.avatar {
    width: 36px; height: 36px; padding: 0;
    display: inline-flex; align-items: center; justify-content: center;
    font: inherit; font-size: var(--t-sous-titre); font-weight: 600;
    color: #fff; background: var(--accent);
    border: none; border-radius: var(--r-max); cursor: pointer;
    transition: filter var(--duree) var(--courbe);
    -webkit-tap-highlight-color: transparent;
}
.avatar:hover { filter: brightness(1.08); }
.avatar:active { opacity: .7; }

.menu-compte {
    position: absolute; top: calc(100% + var(--e2)); right: 0; z-index: 50;
    /* Deux marges d'écran, pas une : le menu est ancré au bord droit de
       l'avatar, qui est lui-même à une marge du bord. */
    width: min(320px, calc(100vw - 2 * var(--e4)));
    background: var(--surface); border-radius: var(--r-m);
    box-shadow: 0 8px 28px rgba(0, 0, 0, .18), 0 0 0 1px var(--filet);
    padding: var(--e3) 0;
    /* Le menu s'ouvre depuis son coin, comme un menu système. */
    transform-origin: top right;
    animation: surgir var(--duree) var(--courbe);
}
@keyframes surgir { from { opacity: 0; transform: scale(.96); } }

.menu-compte .qui { padding: var(--e2) var(--e4) var(--e3); }
.menu-compte .qui .adresse { font-weight: 600; overflow-wrap: anywhere; }
.menu-compte .qui .role { color: var(--texte-2); font-size: var(--t-note); margin-top: 2px; }

.menu-compte hr {
    border: none; height: 1px; background: var(--filet);
    transform: scaleY(.5); margin: var(--e2) 0;
}

/* `>` et non un descendant quelconque : sans ça, la règle attrapait aussi les
   boutons « Fermer » des sessions, qui héritaient de `width: 100%` et
   écrasaient le libellé à un mot par ligne. */
.menu-compte > button, .menu-compte > a {
    display: flex; align-items: center; width: 100%;
    min-height: var(--cible); padding: 0 var(--e4);
    font: inherit; font-size: var(--t-sous-titre); text-align: left;
    color: var(--texte); background: transparent; border: none;
    border-radius: 0; cursor: pointer; text-decoration: none;
}
.menu-compte > button:hover, .menu-compte > a:hover { background: var(--surface-2); }
.menu-compte > button.danger { color: var(--rouge); }

.menu-compte .entete {
    font-size: var(--t-legende); font-weight: 600; text-transform: uppercase;
    letter-spacing: .06em; color: var(--texte-2);
    padding: var(--e2) var(--e4) var(--e1);
}

/* Une session ouverte. `align-items: flex-start` : le libellé peut tenir sur
   deux lignes, le bouton doit rester en haut. */
.session {
    display: flex; align-items: flex-start; gap: var(--e2);
    padding: var(--e2) var(--e4);
}
.session .quoi { flex: 1; min-width: 0; font-size: var(--t-note); }
.session .quoi b { font-weight: 600; }
.session .ou { color: var(--texte-2); font-size: var(--t-legende); margin-top: 1px; }
.session .btn { flex: none; min-height: 28px; padding: 0 var(--e2); font-size: var(--t-legende); }
.session.ici .quoi b::after {
    content: ' · cet appareil';
    color: var(--vert); font-weight: 600;
}

/* -------------------------------------------------------------------------
   14. Apparence — le réglage, en pied de page
   ------------------------------------------------------------------------- */

.pied-page {
    display: flex; align-items: center; gap: var(--e3); flex-wrap: wrap;
    margin-top: var(--e8); padding-top: var(--e5);
    border-top: 1px solid var(--filet);
    font-size: var(--t-note); color: var(--texte-2);
}
.pied-page .segmente { margin-left: auto; }

/* -------------------------------------------------------------------------
   15. Connexion
   ------------------------------------------------------------------------- */

.carte-connexion {
    max-width: 400px; margin: 10vh auto 0; text-align: center;
    background: var(--surface); border-radius: var(--r-l);
    padding: var(--e7) var(--e6); box-shadow: var(--ombre-carte);
}
.carte-connexion h1 { font-size: var(--t-titre); margin-bottom: var(--e3); }
.carte-connexion p { font-size: var(--t-sous-titre); color: var(--texte-2); }
.carte-connexion label { text-align: left; margin-top: var(--e6); }
.carte-connexion button { width: 100%; margin-top: var(--e4); }
