/* Styles de La Vigie — direction artistique « console de passerelle ».

   L'app se lit comme un IDE croisé avec un pupitre de vaisseau : barre
   d'activité verticale à gauche, fil d'Ariane en chemin de fichier, barre
   d'état en bas — mais chaque tuile est un MODULE D'AFFICHAGE :
   crochets lumineux aux coins, ligne de balayage sur l'arête supérieure,
   liseré interne fin, séparateurs pointillés. Le vocabulaire reste technique :
   surfaces opaques, bordures 1 px franches, angles quasi vifs, monospace pour
   toute donnée. Ce qui est coloré porte du SENS (niveau d'un état, gravité
   d'un incident) — et depuis le lot des FACTIONS, plus rien d'autre n'est
   coloré du tout, à un tube près (le journal de bord, voir la règle plus bas).

   DEUX QUARTS, UN SEUL VAISSEAU — ET DEUX ÉQUIPAGES. Les deux thèmes ne sont
   pas « le sombre » et « son inverse » : ce sont deux TOURS DE GARDE du même
   poste, et chacun a désormais une IDENTITÉ.

   LA RÈGLE, EN UNE PHRASE : les deux accents chromatiques ont disparu, et le
   ROUGE DE L'EMBLÈME est la seule couleur d'identité de l'application, dans
   les deux quarts. Tout ce qui portait une teinte de signal — jauges, crochets
   de cartouche, réglettes, puces, focus, tracés de graphe — est passé en GRIS.
   Ce qui reste coloré tient sur les doigts d'une main : l'emblème, et la
   triade d'états vert/ambre/rouge. Un écran où seules trois choses ont une
   couleur est un écran où ces trois choses se voient.
   UNE EXCEPTION, ET UNE SEULE : le JOURNAL DE BORD. La console CRT garde son
   phosphore vert (et sa palette --log-*) dans les deux quarts — c'est un ÉCRAN
   DANS L'ÉCRAN, un tube qui émet sa propre lumière, pas une surface de la
   charte. Éteindre son phosphore au nom de l'achromatique reviendrait à
   peindre un moniteur en gris : la règle « emblème + états » vaut pour la
   planche de bord, jamais pour ce que ses écrans affichent.
   Corollaire assumé : la hiérarchie est TONALE. Un lien accentué ne se
   distingue plus par sa teinte mais par sa valeur et son soulignement — le
   soulignement n'est donc plus décoratif, il porte l'information.

   · :root porte le QUART DE JOUR : l'IMMACULÉ IMPÉRIAL. Gris froids montant
     au blanc pur, angles courts, arêtes d'acier, ombre courte, console
     sombre encastrée. Ce qui code l'Empire n'est pas une couleur, c'est un
     ÉTAT DE SURFACE : standardisé, dur, sans usure et sans grain. D'où la
     disparition des scanlines de ce côté — sur une surface immaculée, une
     trame se lit comme une salissure, et c'est exactement ce que ce quart
     refuse. La matière y vient du RELIEF (ombre, rehaut, encastrement) et
     non de la texture.
   · :root.vigie-sombre porte le QUART DE NUIT : le MONOCHROME rebelle. Gris
     pur du sol au plafond, pas de noir pur, élévation par éclaircissement,
     blanc de tête atténué. Le champ d'étoiles ne survit que sur l'écran de
     connexion : partout ailleurs, l'espace derrière la coque est un aplat, et
     c'est le bezel — plus sombre que l'habitacle — qui dit où finit l'écran.
     Un monochrome tire sa force du vide.

   LE NOMMAGE NE DIT JAMAIS LA FACTION. L'interface ne connaît que « quart de
   nuit » et « quart de jour » : une identité de design se voit, elle ne se
   nomme pas à l'écran. Et ce qui est un ÉTAT (vert / ambre / rouge) n'est
   jamais une couleur d'équipe — le rouge dit une panne, pas un camp. Le rouge
   de l'emblème et le rouge d'état sont deux tokens distincts, et un test le
   verrouille dans les deux quarts.
   La classe vigie-sombre est posée sur <html> par theme.js avant le premier
   paint, puis pilotée par ServiceTheme — MudBlazor 9 ne pose lui-même aucune
   classe sur le DOM (vérifié : il ne réécrit que ses --mud-palette-*).
   Les déclinaisons translucides d'un accent passent par color-mix() : une
   seule source de vérité par couleur. */

:root {
    /* LES SURFACES DU QUART DE JOUR — L'IMMACULÉ IMPÉRIAL. Une rampe de gris
       froids qui monte jusqu'au BLANC PUR, et le blanc pur est ici le sujet et
       non un défaut de goût : ce qui code l'Empire n'est pas la couleur, c'est
       l'ÉTAT DE SURFACE — standardisé, dur, sans usure, sans grain. Le
       parchemin chaud qui occupait cette place disait l'inverse (une matière
       habitée, vieillie, unique) ; c'était un beau thème clair, ce n'était pas
       ce quart-ci.
       Trois choses se lisent dans l'ordre des valeurs, et elles n'ont pas
       changé de rôle :
       · la tôle (chrome) est plus SOUTENUE que la dalle (fond) — sans cet
         écart la coque disparaît et l'écran ne se lit plus comme encastré ;
       · la dalle est plus soutenue que les panneaux posés dessus (surface) ;
       · le panneau, lui, EST blanc — c'est le seul palier qui l'est, et c'est
         ce qui donne à la carte son tranchant de plaque émaillée.
       chrome-2 et chrome-actif portent deux rôles opposés qui s'inversent d'un
       thème à l'autre, exactement comme dans VS Code : de JOUR l'onglet inactif
       est plus SOMBRE que l'éditeur et l'item de rail sélectionné plus CLAIR
       que la barre ; de NUIT, les deux sont plus clairs. Deux variables, donc,
       plutôt qu'une qui mentirait dans un des deux quarts. */
    --vigie-fond: #eef0f2;
    --vigie-chrome: #eaedf0;
    --vigie-chrome-2: #eaedf0;
    --vigie-chrome-actif: #ffffff;
    --vigie-surface-opaque: #ffffff;
    --vigie-surface-2: #f6f7f9;
    --vigie-bordure: #cdd2d8;
    --vigie-bordure-forte: #b6bcc3;
    --vigie-bordure-vive: #2a3138;

    /* La rampe de texte, FROIDE elle aussi : sur du gris neutre, un gris chaud
       se voit comme une tache — c'est la règle du parchemin, prise à l'envers.
       Trois paliers, mêmes RÔLES qu'ailleurs : plein, secondaire, atone.
       L'atone tient Lc 62,5 au pire, sous le secondaire (81,2) partout et sous
       le rouge critique (76,1) partout : il s'efface sans disparaître. */
    --vigie-texte: #14181d;
    --vigie-texte-2: #3f464e;

    /* L'atone du jour a été RAMENÉ de #6b737c à #646b74. Le lot d'origine
       raisonnait en Lc (contraste perceptuel APCA), qui n'est pas le critère
       WCAG : mesuré à la porte e2e, #6b737c rendait 4,21:1 sur la surface
       #eef0f2 et 4,14:1 sur #eceef1 — sous les 4,5:1 de WCAG 1.4.3. C'était le
       défaut le plus répandu de l'application (une quarantaine de nœuds sur
       vingt-six écrans), et un seul jeton le portait tout entier.
       #646b74 rend 4,72:1 et 4,64:1 : la marge tient un changement de surface,
       là où #666d76 (4,50:1 au pire) aurait basculé au premier ajustement de
       fond. Le RÔLE ne change pas — l'atone reste sous le secondaire (#3f464e),
       il s'efface toujours sans disparaître. */
    --vigie-texte-3: #646b74;

    /* L'ACCENT DU JOUR EST ACHROMATIQUE, et c'est la signature du lot.
       Les deux quarts avaient chacun leur teinte de signal — turquoise la nuit,
       bleu le jour. Les deux disparaissent : le ROUGE DE L'EMBLÈME devient la
       seule couleur d'identité de l'application, dans les deux tours de garde.
       Tout le reste (jauges, crochets de cartouche, réglettes, puces, focus)
       passe en gris, et la hiérarchie devient purement TONALE.
       #2a3138 tient Lc 88,6 au pire et 11,20:1 : très au-dessus du plancher.
       Il est volontairement plus SOMBRE que le texte secondaire (81,2) et non
       plus clair — dans une charte achromatique, l'accent et le texte
       secondaire vivent forcément dans le même voisinage tonal, et le seul
       moyen que l'accent se distingue est qu'il aille vers le texte plein
       plutôt que vers l'atone. C'est le coût assumé du parti : un lien accentué
       se lit désormais comme du texte appuyé, pas comme une autre couleur —
       il garde donc son soulignement, qui n'est plus décoratif mais porteur. */
    --vigie-accent: #2a3138;
    /* Le second accent ne sert qu'à un tracé de graphe. En achromatique il
       devient l'acier moyen : distinct du premier sans introduire de teinte. */
    --vigie-accent-2: #6b737c;
    --vigie-accent-contraste: #ffffff;
    --vigie-ok: #126832;
    --vigie-ko: #9a191c;
    /* L'ambre ne bouge pas d'un cheveu : c'est un ÉTAT, et les états sont les
       seuls objets colorés que la charte conserve. Le rouge repasse au-dessus
       de lui sur les six surfaces (77,9 contre 74,2 sur la dalle). */
    --vigie-attention: #7d5200;
    --vigie-hors-contrat: #4a5259;

    /* Les GRADUATIONS des instruments (cercles de radar, réticules, arcs de
       cadran, segments éteints). De nuit c'est un trait qui s'allume ; de jour
       c'est un trait GRAVÉ dans la laque, donc plus sombre que sa dalle. */
    --vigie-grille: #dde1e5;
    --vigie-pastille-neutre: #b6bcc3;
    --vigie-ombre: rgba(20, 24, 29, 0.08);
    --vigie-ombre-forte: rgba(20, 24, 29, 0.16);
    --vigie-badge-fond: #f2f4f6;
    --vigie-champ-fond: #ffffff;
    --vigie-scrollbar: #c3c9cf;
    --vigie-ttfb-ombre: none;

    /* Terminal des logs CI. De jour il est SOMBRE — et c'est un renversement
       assumé par rapport au parchemin, qui en faisait une feuille de listing.
       Sur une charte immaculée, la console est le seul objet encastré de
       l'écran : c'est elle qui donne sa profondeur à une page sans matière,
       exactement comme un écran noir serti dans une console blanche. */
    --vigie-terminal-fond: #23282e;
    --vigie-terminal-texte: #ccd3da;
    --vigie-terminal-gouttiere: #2b3138;
    --vigie-terminal-numero: #8b939c;
    --vigie-terminal-survol: rgba(226, 232, 240, 0.06);
    --vigie-terminal-surlignage: #6d5a1f;
    --vigie-terminal-surlignage-actif: #f97316;

    /* Habillage « module de console » des tuiles. De jour l'ambiance est
       LUMINEUSE et DURE : cadres nets, pas de lueur, pas de grain. Les
       scanlines ne deviennent pas une texture de papier — elles s'ÉTEIGNENT.
       Sur une surface immaculée, toute trame se lit comme une salissure, et
       c'est précisément ce que ce quart refuse. Ce qui les remplace en
       fonction, c'est l'ombre courte et le rehaut blanc du panneau : la
       matière vient du relief, plus de la texture. */
    --vigie-coin: color-mix(in srgb, var(--vigie-accent) 55%, transparent);
    --vigie-liseret-interne: color-mix(in srgb, var(--vigie-bordure) 80%, transparent);
    --vigie-arete: color-mix(in srgb, var(--vigie-accent) 34%, transparent);
    --vigie-scanlines: none;

    /* LA PLAQUE TEINTÉE : le fond d'une pastille ou d'un cartouche qui porte du
       texte de SA PROPRE teinte. De jour la teinte est SOMBRE sur un fond
       clair, et un taux trop haut tire la plaque vers le texte deux fois plus
       vite qu'en sombre. Sept pour cent de jour, dix de nuit. */
    --vigie-plaque: 7%;

    /* Coque du vaisseau : LAQUE BLANCHE, pas de la tôle peinte. Le montant est
       un dégradé très serré (le blanc au centre, un gris de bord à peine plus
       soutenu), l'arête est un filet d'acier franc, les rivets sont des vis
       affleurantes. Rien ne brille, rien ne patine : une pièce moulée. */
    --vigie-metal-1: #e4e7ea;
    --vigie-metal-2: #fafbfc;
    --vigie-metal-3: #dce0e4;
    --vigie-coque-arete: #c3c9cf;
    --vigie-reglette: color-mix(in srgb, var(--vigie-accent) 28%, transparent);
    --vigie-rivet: rgba(20, 24, 29, 0.14);
    --vigie-vignette: rgba(20, 24, 29, 0.035);

    /* Biseau : le filet clair posé sur l'arête HAUTE d'une touche. Franc de
       jour (du blanc éclairé de face), très retenu de nuit. */
    --vigie-biseau: rgba(255, 255, 255, 0.95);
    --vigie-hud-coin: color-mix(in srgb, var(--vigie-accent) 30%, transparent);

    /* Voyant analogique du rail. De JOUR, PAS DE LUEUR : le témoin se lit au
       contraste — pastille pleine, anneau franc, aucune ombre diffuse. */
    --vigie-voyant-diode: 0 0 0 1px color-mix(in srgb, var(--vigie-accent) 30%, transparent);
    --vigie-voyant-halo: inset 0 0 0 1px color-mix(in srgb, var(--vigie-accent) 16%, transparent);
    --vigie-voyant-icone: none;

    /* Encastrement de l'écran dans la coque. Franc dans les deux quarts :
       c'est lui qui dit « la dalle est en retrait dans la coque ». */
    --vigie-encastrement: rgba(20, 24, 29, 0.16);
    --vigie-encastrement-arete: rgba(255, 255, 255, 0.85);

    /* LA VERRIÈRE, DE JOUR. Un dégradé vertical très court, du presque-blanc en
       haut vers le gris de dalle en bas : la lumière tombe d'en haut, point.
       Aucune nébuleuse, aucun grain, aucune étoile — sur une charte immaculée
       le ciel n'est pas un décor, c'est une SURFACE. Ce calque reste le plus
       soutenu de la rampe claire (avec la tôle) parce que la coque est
       transparente : c'est lui qu'on voit dans toutes les gouttières, et s'il
       passait au-dessus des panneaux, la page perdrait son relief d'un coup. */
    --vigie-espace-fond: #eceef1;
    --vigie-fond-nebuleuse: linear-gradient(180deg, #fbfcfd 0%, #eceef1 62%);
    --vigie-fond-etoiles: none;
    --vigie-fond-etoiles-taille: auto;
    --vigie-fond-poussiere: none;
    --vigie-fond-poussiere-taille: auto;
}

:root.vigie-sombre {
    /* LES SURFACES DU QUART DE NUIT — LE MONOCHROME. Gris pur du sol au
       plafond, et AUCUN accent chromatique : les seuls objets colorés de
       l'écran sont l'emblème rouge et la triade d'états — plus l'exception
       actée en tête de feuille, le phosphore vert du journal de bord, qui est
       un tube et non une surface. Ce n'est pas un thème
       sombre de plus, c'est une règle — la hiérarchie devient TONALE, et une
       hiérarchie tonale ne peut pas mentir, parce qu'il n'y a plus de teinte
       pour porter un sens qu'elle n'a pas.
       Pas de noir pur : l'habitacle est à #151719 et l'élévation se fait en
       ÉCLAIRCISSANT (la carte est plus claire que la dalle), jamais en posant
       une ombre — une ombre sur du noir ne se voit pas. Seule la console
       descend sous l'habitacle, parce qu'elle est encastrée. */
    --vigie-fond: #151719;
    --vigie-chrome: #1a1c1f;
    --vigie-chrome-2: #1d1f22;
    --vigie-chrome-actif: #1d1f22;
    --vigie-surface-opaque: #1c1e21;
    --vigie-surface-2: #212427;
    --vigie-bordure: #2a2d31;
    --vigie-bordure-forte: #3a3e43;
    --vigie-bordure-vive: #aeb4ba;

    /* LA RAMPE DE GRIS. Le blanc de tête est ATTÉNUÉ (#e3e6e8 et non #ffffff) :
       du blanc pur sur du gris sombre éblouit à la longue, et ce quart est
       celui qu'on regarde longtemps.
       Les trois paliers gardent leur contrat : plein 88,7 / secondaire 66,6 /
       atone 47,8 sur la surface la moins favorable, l'atone restant sous le
       rouge critique (62,4) et au-dessus du plancher de lisibilité (45).
       ATTENTION, PIÈGE DE LA MAQUETTE : l'échantillon donnait #9aa0a6 pour le
       texte secondaire et descendait jusqu'à #5f646a pour les gravures. Mesuré,
       #5f646a tient Lc 19 — trois fois sous le plancher — et #9aa0a6 tient
       47,8, ce qui n'est pas un secondaire mais exactement un ATONE. Les trois
       valeurs ont donc été remontées d'un cran chacune : #9aa0a6 prend le rôle
       qui lui correspond vraiment, et le secondaire monte à #bcc2c8 pour que
       les trois niveaux de lecture existent encore. Une maquette se dessine à
       l'œil sur deux cartes ; une charte se mesure sur six surfaces. */
    --vigie-texte: #e3e6e8;
    --vigie-texte-2: #bcc2c8;
    --vigie-texte-3: #9aa0a6;

    /* L'ACCENT DE NUIT EST ACHROMATIQUE — le turquoise a disparu. C'est le gris
       clair des jauges et des graphes de l'échantillon (#aeb4ba, Lc 58,6), qui
       se pose entre le texte secondaire et l'atone : assez présent pour tenir
       un tracé, assez discret pour ne jamais se lire comme un état. */
    --vigie-accent: #aeb4ba;
    --vigie-accent-2: #8d949b;
    --vigie-accent-contraste: #151719;
    /* Les états adoucis : ce sont les trois SEULS objets colorés de l'écran
       avec l'emblème (et le phosphore du journal, qui vit hors charte), et sur
       un fond de gris pur ils n'ont plus besoin de
       crier pour se voir. Le vert et l'ambre descendent donc d'un cran de
       saturation par rapport aux valeurs Tokyo Night. */
    --vigie-ok: #4fc383;
    /* Le rouge d'état ne bouge pas : il est le seul token dont la valeur ait
       été défendue par une mesure au fil de trois lots (l'échelle de gravité se
       lisait à l'envers avant la PR #57), et le monochrome ne change rien à ce
       raisonnement. Il repasse au-dessus de l'ambre sur les six surfaces
       (62,4 contre 56,9) et tient le plancher de 60. */
    --vigie-ko: #ff9eae;
    --vigie-attention: #d8a856;
    --vigie-hors-contrat: #8d949b;

    --vigie-grille: #2a2d31;
    --vigie-pastille-neutre: #4a4f55;
    --vigie-ombre: rgba(0, 0, 0, 0.4);
    --vigie-ombre-forte: rgba(0, 0, 0, 0.6);
    --vigie-badge-fond: #212427;
    --vigie-champ-fond: #17191c;
    --vigie-scrollbar: #35393e;
    --vigie-ttfb-ombre: none;

    /* En sombre, le terminal est PLUS sombre que la surface qui le porte : il
       se lit comme un vrai terminal encastré dans l'éditeur. */
    --vigie-terminal-fond: #121416;
    --vigie-terminal-texte: #ccd0d4;
    --vigie-terminal-gouttiere: #1d1f22;
    --vigie-terminal-numero: #7e848a;
    --vigie-terminal-survol: rgba(227, 230, 232, 0.055);
    --vigie-terminal-surlignage: #6d5a1f;
    --vigie-terminal-surlignage-actif: #f97316;

    /* Le module s'allume, mais en GRIS. Les crochets de coin prennent le blanc
       atténué de la maquette (#dfe3e7) plutôt qu'une déclinaison de l'accent :
       ce sont eux qui donnent son tranchant à la carte, et à 55 % de l'accent
       ils s'affadissaient. Les scanlines restent — sur un tube sombre une
       trame est une matière, pas une salissure — mais elles n'ont plus de
       teinte à emprunter, donc elles passent au blanc à 3,5 %. */
    --vigie-coin: color-mix(in srgb, #dfe3e7 68%, transparent);
    --vigie-liseret-interne: color-mix(in srgb, #dfe3e7 9%, transparent);
    --vigie-arete: color-mix(in srgb, #dfe3e7 42%, transparent);
    --vigie-scanlines: repeating-linear-gradient(
        0deg,
        rgba(227, 230, 232, 0.035) 0 1px,
        transparent 1px 4px);
    --vigie-plaque: 10%;

    /* Coque du vaisseau : le BEZEL est plus sombre que l'habitacle (#0e1012
       contre #151719), et c'est ce qui remplace le champ d'étoiles comme
       séparateur. Un monochrome tire sa force du vide : s'il n'y a plus rien
       derrière la coque, il faut que la coque elle-même dise où finit l'écran.
       Plus aucune dominante colorée — la réglette n'est plus une diode cyan,
       c'est un filet d'acier, et les rivets accrochent une lumière blanche. */
    --vigie-metal-1: #0b0d0f;
    --vigie-metal-2: #17191c;
    --vigie-metal-3: #0e1012;
    --vigie-coque-arete: #2a2d31;
    --vigie-reglette: color-mix(in srgb, #aeb4ba 45%, transparent);
    --vigie-rivet: rgba(227, 230, 232, 0.12);
    --vigie-vignette: rgba(0, 0, 0, 0.45);
    --vigie-biseau: rgba(227, 230, 232, 0.07);
    --vigie-hud-coin: color-mix(in srgb, #dfe3e7 45%, transparent);

    /* LE VOYANT NE BAIGNE PLUS DANS SA PROPRE COULEUR. En turquoise, la diode
       du rail portait un halo de 12 px : c'était une source lumineuse teintée,
       et c'est exactement ce qu'un monochrome ne peut pas garder — un halo
       gris ne se lit pas comme une lueur, il se lit comme un flou.
       Ce qui le remplace tient la même FONCTION (dire que le témoin est actif)
       par le seul moyen qui marche en achromatique : un anneau net et un
       rétroéclairage très court. La diode s'affirme par son contour, plus par
       son rayonnement. */
    --vigie-voyant-diode: 0 0 0 1px color-mix(in srgb, #dfe3e7 55%, transparent),
        0 0 4px color-mix(in srgb, #dfe3e7 22%, transparent);
    --vigie-voyant-halo: inset 0 0 0 1px color-mix(in srgb, #aeb4ba 18%, transparent);
    --vigie-voyant-icone: none;

    --vigie-encastrement: rgba(0, 0, 0, 0.72);
    --vigie-encastrement-arete: color-mix(in srgb, #dfe3e7 10%, transparent);

    /* L'ESPACE DERRIÈRE LA COQUE EST VIDE, et c'est l'arbitrage central du
       monochrome. Le champ d'étoiles — nébuleuse, poussière, étoiles franches,
       et leur dérive sur deux minutes — ne survit QUE sur l'écran de connexion,
       où il est le décor d'une arrivée. Partout ailleurs il s'éteint au profit
       d'un aplat.
       Ce n'est pas une amputation : trois calques qui respirent derrière une
       charte sans couleur, c'est la seule chose qui bouge à l'écran, donc
       c'est la seule chose qu'on regarde. Le vide, ici, est ce qui rend le
       rouge de l'emblème et les trois états lisibles d'un coup d'œil.
       Les tokens gardent leurs valeurs (elles ne coûtent rien tant qu'elles ne
       sont pas peintes) et la règle qui les rallume vit plus bas, accrochée à
       l'écran de connexion. */
    --vigie-espace-fond: #151719;
    --vigie-etoile: rgba(227, 230, 232, 0.85);
    --vigie-poussiere: rgba(200, 206, 212, 0.34);

    --vigie-fond-nebuleuse: none;
    --vigie-fond-poussiere: none;
    --vigie-fond-poussiere-taille: 340px 340px;
    --vigie-fond-etoiles: none;
    --vigie-fond-etoiles-taille: 760px 760px;
}

:root {
    /* Géométrie de la coque IDE. Une seule source pour les hauteurs : les
       barres collantes et leurs décalages doivent rester au pixel, sinon des
       lignes de contenu défilent dans un interstice. */
    --vigie-l-activite: 3.8rem;

    /* La verrière tient dans les 2 rem que la barre de chemin occupait AVANT
       elle, alors qu'elle porte quatre choses de plus (verdict, palette
       promue, contextuel, thème). C'est le budget de châssis qui l'impose :
       chaque dixième de rem ici se paie sur toutes les pages et à toutes les
       largeurs. */
    --vigie-h-verriere: 2rem;

    /* Barre d'état à 2,1 rem (elle valait 1,7) : ses pastilles d'état ont
       grossi de 25 % — c'est le pouls de la supervision, et il se lisait mal
       au doigt. La hauteur suit, sinon les icônes débordent de la bande. */
    --vigie-h-etat: 2.1rem;

    /* Montant droit de l'habitacle : le QUATRIÈME bord du bezel. Il existe à
       toutes les largeurs (10 px au doigt, 16 px au-delà de 1100 px) parce que
       sans lui le champ étoilé filait jusqu'au bord du viewport et l'écran
       n'était plus encastré dans quoi que ce soit. Sa largeur est RÉSERVÉE par
       la coque (padding), jamais superposée : un montant qui masque une colonne
       de tableau n'habille plus, il gêne. */
    --vigie-l-montant: 10px;

    /* Hauteur du rail replié en bandeau horizontal sous 900 px. Fixée et non
       plus « auto » : c'est elle qui donne au bord HAUT du bezel une géométrie
       connue, donc une ombre d'encastrement posée au bon endroit. */
    --vigie-h-rail-mobile: 4rem;

    /* Angles quasi vifs : les crochets de coin épousent la bordure, un rayon
       plus rond laisserait un jour entre le crochet droit et l'arc.
       CE QUE CES DEUX JETONS COUVRENT, et ce qu'ils ne couvrent pas. Ils disent
       l'angle du CHÂSSIS : une surface, une plaque, une touche, un cartouche —
       tout ce qui est un rectangle de l'interface. Ils ne disent pas l'angle
       d'une FORME dont le rayon découle de sa propre taille : la diode de 3 px
       du rail, les segments de jauge de 5 px, les cases du calendrier, la
       marque de légende. Ces derniers gardent une valeur littérale, et c'est
       délibéré — les faire suivre le châssis les déformerait le jour où le
       châssis s'arrondit. */
    --vigie-rayon: 3px;
    --vigie-rayon-petit: 2px;
    --vigie-police: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
    --vigie-police-mono: ui-monospace, 'Cascadia Mono', 'JetBrains Mono', 'SF Mono', Consolas, monospace;
    --vigie-transition: 140ms ease;

    /* TROIS pas d'espacement, et trois seulement. La feuille en comptait une
       quinzaine (0,25 / 0,4 / 0,45 / 0,5 / 0,6 / 0,7 / 0,75 / 0,8 / 0,9 / 1 /
       1,2 / 1,3 / 1,4 / 1,75…), choisis un par un et donc jamais deux fois le
       même : c'est ainsi qu'un titre de section finit à 0,7 rem de son contenu
       ici et à 0,8 là, sans que personne ne l'ait décidé.
       Trois, pas cinq : au-delà, on ne choisit plus, on pioche — et on retombe
       exactement dans l'ad hoc qu'on vient de retirer.
       · pas-1 : à l'intérieur d'un bloc (un libellé et sa valeur) ;
       · pas-2 : entre deux éléments d'une même section (titre → contenu) ;
       · pas-3 : entre deux sections.
       Les zones non reprises par ce lot gardent leurs valeurs littérales : une
       passe aveugle sur 5 900 lignes coûterait plus en régressions visuelles
       qu'elle ne rapporterait. */
    --vigie-pas-1: 0.5rem;
    --vigie-pas-2: 0.75rem;
    --vigie-pas-3: 1.75rem;
}

/* Échelle typographique globale : 17 px de base au lieu de 16. Tout est en rem
   (textes, icônes, gouttières), donc l'ensemble gagne ~6 % — un cran de clarté,
   pas un zoom : les densités relatives ne bougent pas. */
html {
    font-size: 106.25%;
}

/* C'est <html> qui porte le fond de l'espace, et <body> qui est TRANSPARENT.
   Ce partage n'est pas cosmétique, il est imposé par l'ordre de peinture : les
   calques d'étoiles sont en z-index négatif pour passer sous le contenu en flux
   (une page comme /connexion n'a aucun conteneur positionné), et un z-index
   négatif se peint APRÈS le fond de l'élément racine mais AVANT le fond de
   <body>. Un fond opaque sur <body> masquerait donc entièrement le ciel. */
html {
    background: var(--vigie-espace-fond);
}

html, body {
    font-family: var(--vigie-police);
    color: var(--vigie-texte);
    line-height: 1.55;
}

body {
    background: transparent;
}

/* MudBlazor fige sa propre pile de polices : on aligne tout sur la nôtre. */
.mud-typography, .mud-button-root, .mud-input, .mud-table, .mud-chip, .mud-breadcrumbs {
    font-family: var(--vigie-police) !important;
}

h1:focus {
    outline: none;
}

/* Scrollbar fine assortie au mode, façon éditeur. Le !important prend le pas
   sur la couleur que MudBlazor pose globalement. */
* {
    scrollbar-width: thin;
    scrollbar-color: var(--vigie-scrollbar) transparent !important;
}

::-webkit-scrollbar {
    width: 10px;
    height: 10px;
}

::-webkit-scrollbar-thumb {
    background: var(--vigie-scrollbar);
    border-radius: 0;
    border: 3px solid transparent;
    background-clip: padding-box;
}

::-webkit-scrollbar-track {
    background: transparent;
}

/* Anneau de focus net et uniforme : au clavier, on doit voir où l'on est. Deux
   couches — un trait franc de 2 px posé à 2 px du bord (il occupe donc la bande
   2 → 4 px) et un halo délavé qui s'étale jusqu'à 6 px, soit 2 px visibles
   au-delà du trait. Sur un pupitre métallique où tout est en relief, un simple
   liseré se perd dans les biseaux ; le halo, lui, se voit sur n'importe quel
   fond. Les trois distances sont liées : décaler le trait sans étendre le halo
   d'autant ferait disparaître l'une des deux couches. */
:focus-visible {
    outline: 2px solid var(--vigie-accent);
    outline-offset: 2px;
    box-shadow: 0 0 0 6px color-mix(in srgb, var(--vigie-accent) 22%, transparent);
}

/* MudBlazor pose un « button:focus { outline: none } » qui, à spécificité (0,1,1)
   contre (0,1,0), EFFAÇAIT le trait de l'anneau ci-dessus sur tous les boutons de
   l'application — il ne restait que le halo, soit la couche délavée sans la
   couche franche. Trouvé au clavier en vérifiant la verrière : le bouton de thème
   et celui de la palette n'avaient pas le même anneau que les liens voisins.
   Même spécificité, plus loin dans la feuille (app.css est chargée après
   MudBlazor) : le trait revient sans !important. */
button:focus-visible {
    outline: 2px solid var(--vigie-accent);
    outline-offset: 2px;
}

/* ET LE MÊME PIÈGE SUR LES LIENS, resté ouvert (trouvé au lot 11 en éprouvant
   au clavier les onglets de /configuration, qui sont des <a>). MudBlazor pose
   aussi un « a:focus-visible { outline: none } » à (0,1,1), et il effaçait le
   trait sur TOUS les liens de l'application : le cap de la verrière, les entrées
   du rail, les onglets de site d'/analytics et d'/usage. Un lien dont on ne voit
   pas qu'il a le focus est un échec WCAG 2.4.7 pour qui navigue au clavier. */
a:focus-visible {
    outline: 2px solid var(--vigie-accent);
    outline-offset: 2px;
}

/* --- Commandes de console --------------------------------------------------- */

/* La touche de pupitre : corps métallique, biseau haut, appui « enfoncé » net,
   liseré lumineux au survol. Sert aux commandes maison (sas de secours, retour
   flotte) et de socle aux boutons MudBlazor juste en dessous — une seule
   grammaire, deux implémentations à habiller. */
.vigie-commande {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    min-height: 2.2rem;
    padding: 0.35rem 0.9rem;
    font-family: var(--vigie-police-mono);
    font-size: 0.76rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--vigie-texte-2);
    text-decoration: none;
    background-color: var(--vigie-chrome-2);
    background-image: linear-gradient(180deg, var(--vigie-metal-2) 0%, var(--vigie-metal-1) 100%);
    border: 1px solid var(--vigie-coque-arete);
    border-radius: var(--vigie-rayon-petit);
    box-shadow: inset 0 1px 0 var(--vigie-biseau), 0 1px 2px var(--vigie-ombre);
    cursor: pointer;
    white-space: nowrap;
    transition: color var(--vigie-transition), border-color var(--vigie-transition),
        box-shadow var(--vigie-transition), transform var(--vigie-transition);
}

.vigie-commande:hover {
    color: var(--vigie-texte);
    border-color: var(--vigie-bordure-vive);
    box-shadow: inset 0 1px 0 var(--vigie-biseau), 0 0 0 1px var(--vigie-reglette),
        0 1px 3px var(--vigie-ombre);
}

/* Appui : la touche s'enfonce d'un pixel et son biseau s'inverse. C'est ce
   micro-mouvement qui fait la différence entre « cliquer sur un rectangle » et
   « appuyer sur une commande ». */
.vigie-commande:active {
    transform: translateY(1px);
    box-shadow: inset 0 2px 4px var(--vigie-ombre-forte);
}

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

/* Les boutons pleins gardent leur couleur (elle porte du sens : primaire pour
   « enregistrer », erreur pour « purger ») et reçoivent le RELIEF : biseau
   haut, ombre portée courte, appui enfoncé. On ne touche pas au fond, donc les
   contrastes de texte calculés par le thème restent exacts. */
.mud-button-root.mud-button-filled {
    box-shadow: inset 0 1px 0 color-mix(in srgb, #fff 28%, transparent),
        0 1px 2px var(--vigie-ombre-forte);
    text-transform: none;
    letter-spacing: 0.03em;
}

.mud-button-root.mud-button-filled:hover:not(:disabled) {
    box-shadow: inset 0 1px 0 color-mix(in srgb, #fff 34%, transparent),
        0 0 0 1px color-mix(in srgb, var(--vigie-accent) 45%, transparent),
        0 2px 4px var(--vigie-ombre-forte);
}

.mud-button-root.mud-button-filled:active:not(:disabled) {
    transform: translateY(1px);
    box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.32);
}

/* Boutons « outlined » et « text » : ce sont eux qui deviennent de vraies
   touches métalliques, puisqu'ils n'ont pas de couleur propre à préserver. */
.mud-button-root.mud-button-outlined,
.mud-button-root.mud-button-text {
    background-image: linear-gradient(180deg, var(--vigie-metal-2) 0%, var(--vigie-metal-1) 100%);
    border: 1px solid var(--vigie-coque-arete);
    box-shadow: inset 0 1px 0 var(--vigie-biseau), 0 1px 2px var(--vigie-ombre);
    text-transform: none;
}

.mud-button-root.mud-button-outlined:hover:not(:disabled),
.mud-button-root.mud-button-text:hover:not(:disabled) {
    border-color: var(--vigie-bordure-vive);
    box-shadow: inset 0 1px 0 var(--vigie-biseau), 0 0 0 1px var(--vigie-reglette),
        0 1px 3px var(--vigie-ombre);
}

.mud-button-root.mud-button-outlined:active:not(:disabled),
.mud-button-root.mud-button-text:active:not(:disabled) {
    transform: translateY(1px);
    box-shadow: inset 0 2px 4px var(--vigie-ombre-forte);
}

/* Boutons-icônes : ils restent nus au repos (une rangée de plaques métalliques
   dans le rail serait illisible) et prennent la touche au survol et au focus. */
.mud-icon-button:hover:not(:disabled) {
    background-image: linear-gradient(180deg, var(--vigie-metal-2) 0%, var(--vigie-metal-1) 100%);
    box-shadow: inset 0 1px 0 var(--vigie-biseau), 0 0 0 1px var(--vigie-coque-arete);
}

.mud-icon-button:active:not(:disabled) {
    transform: translateY(1px);
}

/* --- Commutateurs de tableau de bord (MudSwitch) ----------------------------- */

/* Les interrupteurs de /configuration doivent se lire comme des commutateurs de
   pupitre : une glissière ENCASTRÉE (fond de champ, ombre interne, angles
   quasi vifs) et un capuchon métallique carré qui s'ALLUME quand la commande
   est engagée. La couleur d'allumage est celle que MudBlazor pose déjà sur le
   composant (primaire, ambre pour la maintenance) : le sens reste le sien, on
   ne fait que le mettre en relief. */
.mud-switch-span .mud-switch-track {
    border-radius: var(--vigie-rayon-petit);
    opacity: 1;
    background-color: var(--vigie-champ-fond);
    box-shadow: inset 0 1px 3px var(--vigie-ombre-forte),
        inset 0 0 0 1px var(--vigie-coque-arete);
}

.mud-switch-base.mud-checked + .mud-switch-track {
    opacity: 1;
}

/* Le sélecteur d'attribut couvre les trois tailles de MudBlazor
   (thumb-small / medium / large) sans les répéter. */
.mud-switch-button [class*="mud-switch-thumb"] {
    border-radius: var(--vigie-rayon-petit);
    /* Le capuchon part de la bordure FORTE et non de la tôle : posé sur une
       glissière encastrée (déjà très sombre en thème sombre), une tôle sombre
       aurait disparu dans son propre logement. Il doit se voir éteint. */
    background-image: linear-gradient(180deg, var(--vigie-bordure-forte) 0%, var(--vigie-metal-2) 100%);
    box-shadow: inset 0 1px 0 var(--vigie-biseau), 0 1px 2px var(--vigie-ombre-forte),
        0 0 0 1px var(--vigie-coque-arete);
}

/* Engagé : le capuchon prend un liseré et un halo dans SA couleur. */
.mud-switch-base.mud-checked .mud-switch-button [class*="mud-switch-thumb"] {
    box-shadow: inset 0 1px 0 var(--vigie-biseau), 0 0 0 1px currentColor,
        0 0 8px -1px currentColor;
}

/* --- Champs encastrés -------------------------------------------------------- */

/* Un champ de saisie est un écran encastré dans la tôle : fond de champ,
   ombre interne courte, angle quasi vif. */
.mud-input.mud-input-outlined,
.mud-input.mud-input-filled {
    background-color: var(--vigie-champ-fond);
    border-radius: var(--vigie-rayon-petit);
    box-shadow: inset 0 1px 3px var(--vigie-ombre);
}

/* --- Cibles tactiles -------------------------------------------------------- */

/* LES LIENS D'ACTION, à toute largeur. Un MudLink fait la hauteur de sa ligne —
   20 à 22,7 px mesurés sur les cinq vues qui en posent — donc sous le plancher
   AA de 24 px. Or ce ne sont pas des liens de lecture : « Vue site → » au pied
   d'une carte, « logs » dans une cellule du tableau des runs, « ← Retour à la
   flotte » sur une page vide sont des BOUTONS déguisés en liens, seuls dans
   leur rangée. Ils prennent donc le plancher — dans de l'espace qui existait
   déjà, aucune de ces rangées ne s'est allongée d'un pixel. */
.mud-link,
.vigie-pastille-panneau-lien {
    display: inline-flex;
    align-items: center;
    min-height: 24px;
}

/* L'EXEMPTION « EN LIGNE », et elle est dans la norme, pas dans un arrangement :
   un lien dont la taille est fixée par l'interligne du texte qui le porte n'est
   pas tenu au plancher. C'est le cas de « Voir le second facteur » au milieu du
   bandeau de la soupape, et des « logs · voir sur GitHub » de la ligne de méta
   d'un run. Les rendre inline-flex les rendrait ATOMIQUES : un lien un peu long
   ne saurait plus se couper en fin de ligne et sortirait de sa colonne. */
p .mud-link,
.mud-alert-message .mud-link {
    display: inline;
    min-height: 0;
}

/* Sous 900 px on est au doigt : toute commande passe à 44 px de haut. Au-dessus
   la souris est précise et la densité prime — un pupitre à 44 px partout sur un
   écran de 1600 px n'est plus un pupitre, c'est une télécommande. */
@media (max-width: 900px) {
    .vigie-commande,
    .mud-button-root.mud-button,
    .mud-icon-button {
        min-height: 44px;
        min-width: 44px;
    }

    /* L'EXCEPTION QUI SE CACHAIT DERRIÈRE LA RÈGLE CI-DESSUS. Les flèches d'un
       MudNumericField sont bien des .mud-button-root.mud-button, et elles n'ont
       jamais fait 44 px : MudBlazor leur pose min-height:unset sur un sélecteur
       plus spécifique, et elles restaient à 23,3 × 20,6 px — les seize du
       formulaire de configuration. Empilées à 20 px l'une de l'autre, elles ne
       bénéficient d'aucune tolérance d'espacement : ce sont deux cibles sous le
       plancher, collées.
       PAS 44 NON PLUS : deux flèches de 44 px, c'est un champ de 88 px de haut
       pour saisir un entier. Elles prennent le plancher AA de 24, et le champ
       passe de 41,2 à 48 px pour les loger — mesuré, +61 px sur les 4 390 px de
       /configuration au doigt. La saisie directe, elle, était déjà là :
       inputmode="numeric" ouvre le pavé du téléphone. */
    .mud-input.mud-input-outlined {
        min-height: 48px;
    }

    .mud-input-control.mud-input-number-control .mud-input-numeric-spin button.mud-button-root {
        min-height: 24px;
        min-width: 24px;
    }
}

/* --- L'espace, derrière la coque -------------------------------------------- */

/* Le hublot du vaisseau : trois calques fixés au viewport, sous tout le reste,
   qui ne reçoivent aucun événement. Ils DÉRIVENT en `transform` (composité par
   le GPU, aucun repaint) plutôt qu'en `background-position` (qui repeindrait
   la totalité de l'écran à chaque frame, soixante fois par seconde, pour un
   déplacement de trois pixels). Cycles très longs et amplitudes de dix à vingt
   pixels : sur deux minutes le ciel bouge, sur deux secondes il ne bouge pas —
   c'est un fond de salle de contrôle, pas un économiseur d'écran.

   Le bloc est rendu par App.razor, donc présent sur TOUTES les pages : la
   coque authentifiée, l'écran de connexion (qui a sa propre mise en page) et
   les pages d'erreur. z-index négatif : il doit passer sous le contenu EN FLUX
   des pages sans conteneur positionné — un z-index 0 le peindrait par-dessus la
   carte de connexion (les boîtes de bloc en flux se peignent avant les
   éléments positionnés, quel que soit leur ordre dans le DOM). */
.vigie-espace {
    position: fixed;
    inset: 0;
    z-index: -1;
    overflow: hidden;
    pointer-events: none;
}

/* Les calques débordent de 8 % : ils doivent rester couvrants aux quatre bords
   pendant tout le cycle de dérive, sinon un liseré nu apparaît en bout de course. */
.vigie-espace > span {
    position: absolute;
    inset: -8%;
    display: block;
}

.vigie-espace-nebuleuse {
    background-image: var(--vigie-fond-nebuleuse);
}

.vigie-espace-poussiere {
    background-image: var(--vigie-fond-poussiere);
    background-size: var(--vigie-fond-poussiere-taille);
}

.vigie-espace-etoiles {
    background-image: var(--vigie-fond-etoiles);
    background-size: var(--vigie-fond-etoiles-taille);
}

/* ══ LE CIEL, RALLUMÉ SUR LE SEUL ÉCRAN DE CONNEXION ═════════════════════════

   Le monochrome a éteint le champ d'étoiles partout (voir « L'ESPACE DERRIÈRE
   LA COQUE »). Il reste ici, et ici seulement : l'écluse est le seul écran où
   l'on ARRIVE, et un décor y raconte quelque chose qu'un tableau de supervision
   ne peut pas se permettre — sur une page qu'on regarde huit heures, la seule
   chose qui bouge devient la seule chose qu'on voit.

   Le décor est posé par App.razor au niveau du DOCUMENT, au-dessus de toutes
   les mises en page : aucun sélecteur descendant ne pouvait donc l'atteindre
   depuis une page. D'où :has(), qui lit un marqueur posé par LayoutConnexion et
   fait remonter l'information d'un frère à l'autre. C'est le seul :has() de la
   feuille, sur un sélecteur ancré au body et non en descendant libre : le
   moteur n'a qu'un sous-arbre à surveiller.
   Les trois calques et leur dérive ne vivent qu'en NUIT — de jour la verrière
   est un dégradé immobile, et une trame qui glisse serait un tic. */
:root.vigie-sombre body:has(.connexion-ecran) {
    --vigie-fond-nebuleuse:
        radial-gradient(58% 44% at 16% 14%, rgba(46, 52, 58, 0.55) 0%, transparent 70%),
        radial-gradient(46% 38% at 84% 74%, rgba(38, 43, 49, 0.5) 0%, transparent 72%),
        radial-gradient(72% 58% at 52% 112%, rgba(42, 47, 53, 0.45) 0%, transparent 66%);
    --vigie-fond-poussiere:
        radial-gradient(circle at 6% 12%, var(--vigie-poussiere) 0 0.6px, transparent 1.1px),
        radial-gradient(circle at 23% 41%, var(--vigie-poussiere) 0 0.6px, transparent 1.1px),
        radial-gradient(circle at 38% 8%, var(--vigie-poussiere) 0 0.5px, transparent 1px),
        radial-gradient(circle at 55% 27%, var(--vigie-poussiere) 0 0.6px, transparent 1.1px),
        radial-gradient(circle at 71% 15%, var(--vigie-poussiere) 0 0.5px, transparent 1px),
        radial-gradient(circle at 88% 36%, var(--vigie-poussiere) 0 0.6px, transparent 1.1px),
        radial-gradient(circle at 13% 63%, var(--vigie-poussiere) 0 0.5px, transparent 1px),
        radial-gradient(circle at 31% 79%, var(--vigie-poussiere) 0 0.6px, transparent 1.1px),
        radial-gradient(circle at 47% 58%, var(--vigie-poussiere) 0 0.5px, transparent 1px),
        radial-gradient(circle at 63% 88%, var(--vigie-poussiere) 0 0.6px, transparent 1.1px),
        radial-gradient(circle at 79% 66%, var(--vigie-poussiere) 0 0.5px, transparent 1px),
        radial-gradient(circle at 94% 93%, var(--vigie-poussiere) 0 0.6px, transparent 1.1px),
        radial-gradient(circle at 17% 33%, var(--vigie-poussiere) 0 0.4px, transparent 0.9px),
        radial-gradient(circle at 44% 71%, var(--vigie-poussiere) 0 0.4px, transparent 0.9px),
        radial-gradient(circle at 68% 46%, var(--vigie-poussiere) 0 0.4px, transparent 0.9px),
        radial-gradient(circle at 84% 6%, var(--vigie-poussiere) 0 0.4px, transparent 0.9px);
    --vigie-fond-etoiles:
        radial-gradient(circle at 11% 22%, var(--vigie-etoile) 0 1px, transparent 2.4px),
        radial-gradient(circle at 34% 67%, var(--vigie-etoile) 0 1.2px, transparent 2.8px),
        radial-gradient(circle at 57% 9%, var(--vigie-etoile) 0 0.9px, transparent 2.2px),
        radial-gradient(circle at 73% 51%, var(--vigie-etoile) 0 1.3px, transparent 3px),
        radial-gradient(circle at 90% 84%, var(--vigie-etoile) 0 1px, transparent 2.4px),
        radial-gradient(circle at 26% 92%, var(--vigie-etoile) 0 1.1px, transparent 2.6px),
        radial-gradient(circle at 64% 76%, var(--vigie-etoile) 0 0.9px, transparent 2.2px);
    --vigie-espace-fond: #0e1012;
}

:root.vigie-sombre body:has(.connexion-ecran) .vigie-espace-nebuleuse {
    animation: vigie-derive-nebuleuse 190s ease-in-out infinite alternate;
}

:root.vigie-sombre body:has(.connexion-ecran) .vigie-espace-poussiere {
    animation: vigie-derive-poussiere 96s linear infinite alternate;
}

:root.vigie-sombre body:has(.connexion-ecran) .vigie-espace-etoiles {
    animation: vigie-derive-etoiles 148s linear infinite alternate;
}

/* Trois vitesses, trois directions : c'est l'écart entre les calques qui fait
   la profondeur, pas leur vitesse absolue. */
@keyframes vigie-derive-poussiere {
    from {
        transform: translate3d(0, 0, 0);
    }

    to {
        transform: translate3d(-18px, 9px, 0);
    }
}

@keyframes vigie-derive-etoiles {
    from {
        transform: translate3d(0, 0, 0);
    }

    to {
        transform: translate3d(9px, -6px, 0);
    }
}

@keyframes vigie-derive-nebuleuse {
    from {
        transform: translate3d(0, 0, 0) scale(1);
    }

    to {
        transform: translate3d(14px, -10px, 0) scale(1.04);
    }
}

/* --- Habitacle : montants, vignettage, crochets de verrière ------------------ */

/* Ce qui donne le sentiment d'être ASSIS DANS quelque chose. Purement
   décoratif, entièrement `pointer-events: none`, posé au-dessus du contenu
   mais sous la barre d'état — donc rien ne peut devenir incliquable. */
.vigie-habitacle {
    position: fixed;
    inset: 0;
    z-index: 55;
    pointer-events: none;
}

/* Vignettage : les bords de la verrière s'assombrissent, comme un écran
   incurvé. Très faible en clair (4,5 %) — un vignettage sombre sur fond blanc
   ferait BAISSER le contraste du texte au lieu de le tenir. */
.vigie-vignettage {
    position: absolute;
    inset: 0;
    background: radial-gradient(125% 105% at 50% 48%, transparent 52%, var(--vigie-vignette) 100%);
}

/* Montant droit : le QUATRIÈME bord du bezel, OPAQUE comme les trois autres.
   Sa tôle est peinte par-dessus une couleur de fond pleine — on ne doit pas
   voir le ciel au travers, sans quoi l'écran flotte au lieu d'être encastré.
   Sa largeur est réservée par la coque. */
.vigie-montant-droit {
    position: absolute;
    top: 0;
    bottom: 0;
    right: 0;
    width: var(--vigie-l-montant);
    border-left: 1px solid var(--vigie-coque-arete);
    background-color: var(--vigie-chrome);
    background-image:
        linear-gradient(180deg, transparent, var(--vigie-reglette) 14%, var(--vigie-reglette) 86%, transparent),
        radial-gradient(circle, var(--vigie-rivet) 0 1.4px, transparent 2px),
        linear-gradient(90deg, var(--vigie-metal-3) 0%, var(--vigie-metal-2) 45%, var(--vigie-metal-1) 100%);
    background-size: 1px 100%, 9px 78px, 100% 100%;
    background-position: left 4px top, left 5px top, 0 0;
    background-repeat: no-repeat, repeat-y, no-repeat;
}

/* --- Encastrement de l'écran ------------------------------------------------ */

/* LE détail qui fait basculer la lecture de « fond étoilé posé derrière une
   interface » à « on regarde DANS un écran ». Un cadre fixe, calé exactement
   sur les bords du bezel (rail à gauche, montant à droite, barre d'état en
   bas ; en haut la dalle affleure, la traverse d'onglets ayant disparu avec les
   onglets d'éditeur), qui porte :
   · une ombre interne franche sur tout le pourtour — la dalle est en retrait
     dans la tôle, elle n'est pas collée dessus ;
   · un liseré clair de 1 px sur l'arête interne, le reflet du bord de la
     découpe.
   Un élément fixe et non un box-shadow sur le contenu : le contenu défile et
   mesure trois écrans de haut, son ombre interne serait à trois écrans d'ici.
   Entièrement décoratif, pointer-events neutralisés par le parent. */
.vigie-encastrement {
    position: absolute;
    top: 0;
    right: var(--vigie-l-montant);
    bottom: var(--vigie-h-etat);
    left: var(--vigie-l-activite);
    box-shadow:
        inset 0 0 0 1px var(--vigie-encastrement-arete),
        inset 0 0 22px 3px var(--vigie-encastrement);
}

/* Crochets de verrière : quatre angles d'accent DANS l'écran, calés sur le
   cadre d'encastrement — ils marquent les coins de la dalle, pas ceux du
   viseur. 20 px, jamais plus : au-delà ils entrent dans le contenu. */
.vigie-encastrement::before,
.vigie-encastrement::after {
    content: "";
    position: absolute;
    width: 20px;
    height: 20px;
    border: 1px solid var(--vigie-hud-coin);
}

.vigie-encastrement::before {
    top: 5px;
    left: 5px;
    border-right: none;
    border-bottom: none;
}

.vigie-encastrement::after {
    bottom: 5px;
    right: 5px;
    border-left: none;
    border-top: none;
}

/* --- Coque IDE -------------------------------------------------------------- */

/* Grille à deux colonnes : la barre d'activité (fixe) réserve sa gouttière, la
   zone d'édition prend le reste. Le padding bas dégage la barre d'état, qui
   est fixée au viewport ; le padding droit réserve le montant.
   Fond TRANSPARENT et non plus opaque : c'est ce qui laisse voir l'espace dans
   les gouttières. Le `z-index: 1` la pose au-dessus du hublot. */
.vigie-coque {
    position: relative;
    z-index: 1;
    min-height: 100dvh;
    display: grid;
    grid-template-columns: var(--vigie-l-activite) minmax(0, 1fr);
    padding-bottom: var(--vigie-h-etat);
    padding-right: var(--vigie-l-montant);
    background: transparent;
    color: var(--vigie-texte);
}

/* Au-delà de 1100 px, le montant droit s'élargit : le bezel a la place d'être
   un vrai profilé plutôt qu'un simple liseré. */
@media (min-width: 1100px) {
    :root {
        --vigie-l-montant: 16px;
    }
}

.vigie-zone {
    grid-column: 2;
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.vigie-conteneur {
    flex: 1 1 auto;
    width: 100%;
    min-width: 0;
}

/* --- Barre d'activité ------------------------------------------------------- */

.vigie-activite {
    position: fixed;
    top: 0;
    left: 0;
    bottom: var(--vigie-h-etat);
    width: var(--vigie-l-activite);
    z-index: 40;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0.1rem;
    padding: 0.3rem 0 0.5rem;
    border-right: 1px solid var(--vigie-coque-arete);
    overflow-y: auto;
    overflow-x: hidden;

    /* Montant gauche de l'habitacle : tôle profilée (sombre sur l'arête
       extérieure, éclairée au centre), réglette lumineuse le long de l'arête
       interne, rivets tous les 78 px. Les calques sont des BACKGROUNDS et pas
       des pseudo-éléments : le rail défile (dix entrées en 700 px de haut) et
       un pseudo-élément absolu partirait avec le défilement, la réglette
       s'arrêterait au milieu de la coursive. */
    background-color: var(--vigie-chrome);
    background-image:
        linear-gradient(180deg, transparent, var(--vigie-reglette) 12%, var(--vigie-reglette) 88%, transparent),
        radial-gradient(circle, var(--vigie-rivet) 0 1.4px, transparent 2px),
        linear-gradient(90deg, var(--vigie-metal-1) 0%, var(--vigie-metal-2) 55%, var(--vigie-metal-3) 100%);
    background-size: 1px 100%, 9px 78px, 100% 100%;
    background-position: right 4px top, right 5px top, 0 0;
    background-repeat: no-repeat, repeat-y, no-repeat;
}

/* La marque : tour + wordmark réduit à ses initiales en mono, façon logo de
   barre latérale. Le nom complet vit dans le title et l'aria-label. */
.vigie-appbar-titre {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.1rem;
    padding: 0.35rem 0 0.5rem;
    color: var(--vigie-texte);
    text-decoration: none;
    line-height: 1;
}

.vigie-appbar-titre span[aria-hidden] {
    font-size: 1.3rem;
}

.vigie-marque {
    font-family: var(--vigie-police-mono);
    font-size: 0.62rem;
    letter-spacing: 0.1em;
    color: var(--vigie-accent);
    text-transform: uppercase;
}

.vigie-activite-sep {
    height: 1px;
    margin: 0.35rem 0.55rem;
    background: var(--vigie-bordure);
    flex-shrink: 0;
}

.vigie-activite-groupe-titre {
    font-family: var(--vigie-police-mono);
    font-size: 0.56rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--vigie-texte-3);
    text-align: center;
    padding: 0.1rem 0 0.3rem;
}

/* Une entrée : icône au-dessus, micro-libellé dessous. Le libellé est ce qui
   sépare une barre d'activité utilisable d'une rangée d'icônes à deviner. */
.vigie-activite-lien {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.15rem;
    padding: 0.45rem 0.15rem;
    font: inherit;
    font-size: 0.6rem;
    letter-spacing: 0.03em;
    color: var(--vigie-texte-3);
    text-decoration: none;
    background: none;
    border: none;
    cursor: pointer;
    transition: color var(--vigie-transition), background-color var(--vigie-transition),
        box-shadow var(--vigie-transition);
}

/* --- Voyant analogique de l'entrée courante ---------------------------------- */

/* La DIODE, collée à l'arête interne du montant. Elle existe sur TOUTES les
   entrées (éteinte par défaut) plutôt que sur la seule entrée active : c'est ce
   qui permet au survol de la « pré-allumer » et à la transition de couleur de
   jouer, au lieu de faire apparaître un élément d'un coup. */
.vigie-activite-lien::before {
    content: "";
    position: absolute;
    left: 3px;
    top: 24%;
    bottom: 24%;
    width: 3px;
    border-radius: 2px;
    background: var(--vigie-accent);
    opacity: 0;
    transition: opacity var(--vigie-transition), box-shadow var(--vigie-transition);
    pointer-events: none;
}

/* Survol : pré-allumage. La diode s'allume au tiers, sans halo — on approche la
   main de la commande, on ne l'enclenche pas. */
.vigie-activite-lien:hover {
    color: var(--vigie-texte);
    background-color: var(--vigie-chrome-actif);
}

.vigie-activite-lien:hover::before {
    opacity: 0.35;
}

/* Icônes du rail à 1,55 rem (elles valaient 1,3) : sur un téléphone, une icône
   de 22 px se devine plus qu'elle ne se lit. Le micro-libellé dessous reste la
   vraie identification, l'icône est ce qu'on VISE. */
.vigie-activite-lien .mud-icon-root {
    font-size: 1.55rem !important;
    width: 1.55rem;
    height: 1.55rem;
}

.vigie-activite-libelle {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Entrée courante : le voyant passe au plein régime. Trois choses ensemble, et
   c'est leur conjonction qui fait « témoin sous tension » plutôt qu'« aplat
   gris sélectionné » :
   · la DIODE turquoise à pleine opacité, avec son halo serré ;
   · un RÉTROÉCLAIRAGE qui déborde de l'entrée — dégradé teinté depuis l'arête
     de la diode, plus un halo diffus autour du bloc ;
   · un POULS très lent (3,8 s) et de faible amplitude, quasi subliminal : le
     voyant respire, il ne clignote pas.
   L'icône s'éclaire elle aussi (couleur d'accent + lueur), sinon on n'aurait
   allumé qu'un fond. */
.vigie-activite-actif {
    color: var(--vigie-accent);
    background-color: var(--vigie-chrome-actif);
    background-image: linear-gradient(90deg,
        color-mix(in srgb, var(--vigie-accent) 22%, transparent) 0%,
        color-mix(in srgb, var(--vigie-accent) 6%, transparent) 55%,
        transparent 100%);
    box-shadow: var(--vigie-voyant-halo);
}

.vigie-activite-actif::before {
    opacity: 1;
    box-shadow: var(--vigie-voyant-diode);
    animation: vigie-voyant 3.8s ease-in-out infinite;
}

.vigie-activite-actif .mud-icon-root {
    color: var(--vigie-accent);
    filter: var(--vigie-voyant-icone);
}

/* Amplitude volontairement faible : de 1 à 0,74. Un voyant analogique de rack
   respire, il ne fait pas de morse. */
@keyframes vigie-voyant {
    0%, 100% {
        opacity: 1;
    }

    50% {
        opacity: 0.74;
    }
}

/* Entrée de site : le CODE en mono, précédé de sa pastille de disponibilité. */
.vigie-activite-site .vigie-activite-libelle {
    font-family: var(--vigie-police-mono);
    font-size: 0.72rem;
    letter-spacing: 0.05em;
}

/* Pied du rail : thème et déconnexion, CENTRÉS dans la colonne comme toutes les
   entrées au-dessus. Ils étaient posés en flux, donc décalés à gauche du filet
   des icônes — deux boutons de travers en bas d'une colonne alignée. */
/* Épinglé au BAS du rail, même quand le rail défile. Sur une fenêtre courte
   (620 px de haut, dix entrées de navigation), le groupe partait sous la ligne
   de flottaison du rail : la bascule de thème et la déconnexion n'étaient
   atteignables qu'en faisant défiler une colonne dont rien n'indique qu'elle
   défile. Le fond opaque et le filet du haut évitent que les entrées passent
   visuellement dessous. */
.vigie-activite-bas {
    position: sticky;
    bottom: 0;
    z-index: 2;
    margin-top: auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.15rem;
    padding-top: 0.35rem;
    background-color: var(--vigie-chrome);
    background-image: linear-gradient(90deg, var(--vigie-metal-1) 0%, var(--vigie-metal-2) 55%, var(--vigie-metal-3) 100%);
    border-top: 1px solid var(--vigie-coque-arete);
}

.vigie-activite-bas .vigie-deconnexion {
    display: flex;
    justify-content: center;
}

.vigie-activite .mud-icon-button {
    color: var(--vigie-texte-3);
    border-radius: var(--vigie-rayon-petit);
    display: flex;
    min-width: 2.4rem;
    min-height: 2.4rem;
}

/* Les deux icônes du pied suivent l'agrandissement du rail. */
.vigie-activite-bas .mud-icon-root {
    font-size: 1.4rem !important;
    width: 1.4rem;
    height: 1.4rem;
}

.vigie-activite .mud-icon-button:hover {
    color: var(--vigie-texte);
    background-color: var(--vigie-chrome-actif);
}

/* --- La verrière ------------------------------------------------------------- */

/* L'unique bande haute. Elle occupe EXACTEMENT la place de l'ancienne barre de
   chemin (même hauteur, même position collante, même filet bas) : elle a
   remplacé une bande, elle n'en a pas ajouté une — c'est la seule façon de
   loger quatre choses de plus sans reprendre un pixel à l'information.

   TROIS COLONNES, 1fr / auto / 1fr, et non un flex : c'est ce qui centre la
   palette sur la BANDE plutôt qu'entre ses voisines. Les deux colonnes
   latérales sont égales par construction, donc l'axe ne bouge pas quand un
   incident apparaît à gauche ou qu'une commande contextuelle arrive à droite.
   Le contenu des zones, lui, se tronque (minmax(0, 1fr) + overflow) : dans une
   bande de 34 px, ce qui ne rentre pas doit disparaître proprement, jamais
   pousser l'axe. */
.vigie-verriere {
    position: sticky;
    top: 0;
    z-index: 29;
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
    align-items: center;

    /* Les gouttières de la bande sortent des trois pas du lot 1 : pas-2 entre
       les trois colonnes (ce sont des blocs distincts), pas-1 entre les zones
       d'une même colonne. Seuls les maillons du cap descendent en dessous — un
       chemin de fichier s'écrit serré, « flotte  /  bw » n'est plus un chemin. */
    gap: var(--vigie-pas-2);
    min-height: var(--vigie-h-verriere);
    padding: 0 clamp(0.5rem, 1.6vw, 1.4rem);
    background: var(--vigie-fond);
    border-bottom: 1px solid var(--vigie-bordure);
    font-family: var(--vigie-police-mono);
    font-size: 0.76rem;
    color: var(--vigie-texte-3);
}

/* Bâbord : le verdict (zone A) puis le cap (zone B). Tribord : le contextuel
   (zone C) puis les réglages (zone D), tassés contre le montant droit.
   Le rognage vit ICI et pas sur la bande : le panneau du verdict pend de la
   verrière en absolu, et un overflow:hidden posé sur elle le trancherait au ras
   du filet bas. Bâbord ne rogne pas non plus pour la même raison — c'est le cap
   lui-même qui se tronque, et c'est bien le seul qui en ait besoin. */
.vigie-verriere-babord,
.vigie-verriere-tribord {
    display: flex;
    align-items: center;
    gap: var(--vigie-pas-1);
    min-width: 0;
}

.vigie-verriere-tribord {
    justify-content: flex-end;
}

/* Zone A — le verdict. Il ne rétrécit jamais : c'est la réponse à « qu'est-ce
   qui est cassé », et une bande étroite doit sacrifier le chemin avant lui.
   Le badge lui-même garde les classes .vigie-etat-alertes-* de la barre d'état
   d'où il vient : ce sont elles qui portent la correspondance teinte → gravité
   (vert / ambre / rouge), et il n'y a aucune raison d'en écrire un second jeu
   parce que l'élément a changé de bande. */
.vigie-verriere-verdict {
    display: flex;
    align-items: center;
    flex-shrink: 0;
}

/* Cible tactile du verdict : 44 px de large, comme toute commande de la
   verrière. */
.vigie-verriere-verdict .vigie-etat-alertes {
    min-width: 44px;
    justify-content: center;
    padding: 0.1rem 0.3rem;
}

/* Zone B — le cap. Quand la place manque, ce sont les maillons AMONT qui
   rétrécissent : ils restent identifiables à leur première lettre et à leur
   lien, alors que la feuille — la vue où l'on est — doit rester lisible en
   entier aussi longtemps que possible. */
.vigie-verriere-cap {
    display: flex;
    align-items: center;
    gap: 0.3rem;
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
}

/* L'invite de l'ancien kicker, récupérée telle quelle : elle ouvrait chaque
   page, elle ouvre maintenant le cap. */
.vigie-verriere-invite {
    color: var(--vigie-accent);
    flex-shrink: 0;
}

.vigie-verriere-cap a {
    color: var(--vigie-texte-3);
    text-decoration: none;
    flex-shrink: 3;
    min-width: 1.2rem;
    overflow: hidden;
    text-overflow: ellipsis;
}

.vigie-verriere-cap a:hover {
    color: var(--vigie-accent);
    text-decoration: underline;
}

/* Le séparateur reste au niveau de gris des maillons cliquables : plus pâle,
   il tombait sous AA et le chemin se lisait comme des mots détachés. */
.vigie-verriere-sep {
    color: var(--vigie-texte-3);
    flex-shrink: 0;
}

/* La feuille — la vue courante — est le maillon qu'on protège : elle ne cède
   qu'après les autres, et se coupe alors proprement plutôt que d'être tranchée
   au bord de la zone. */
.vigie-verriere-feuille {
    color: var(--vigie-texte);
    flex-shrink: 1;
    min-width: 2.5rem;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Le rappel du chemin AMONT, réservé aux lecteurs d'écran. Muet ici : les
   maillons sont affichés, ils sont lus, il serait dit deux fois. Il ne parle
   qu'au doigt, quand ils tombent — voir la règle de dégradation plus bas. */
.vigie-verriere-cap-amont {
    display: none;
}

/* Zone C — le contextuel. Aucune décoration propre : ce que la vue dépose
   s'habille avec la grammaire commune (chips, boutons de pupitre), la zone ne
   fait que l'aligner et le tronquer si besoin. */
.vigie-verriere-contextuel {
    display: flex;
    align-items: center;

    /* MÊME GOUTTIÈRE QUE LA ZONE D (0,3 rem, et non le pas 1 générique) : ce
       sont les deux rangées de commandes de la bande, elles se lisent comme une
       seule. Mesuré au lot 12, quand /logs y a posé deux commandes : au pas 1,
       les 44 + 8,5 + 44 px faisaient 97 et rognaient la feuille du cap d'un
       pixel à 380 px — 94 px la laissent entière. */
    gap: 0.3rem;
    min-width: 0;
    overflow: hidden;
}

/* Zone D — les réglages. Jamais tronquée : un réglage à demi visible ne se
   manipule pas. */
.vigie-verriere-reglages {
    display: flex;
    align-items: center;
    gap: 0.3rem;
    flex-shrink: 0;
}

/* Une commande de la verrière : plate au repos comme les boutons-icônes du
   rail, elle ne s'allume qu'au survol et au focus. 44 px de LARGE au minimum —
   la règle du doigt s'applique ici comme partout, en largeur.

   ET 24 PX DE HAUT, le minimum AA, ce qui ne coûte RIEN. La règle disait
   jusqu'ici que « la hauteur est celle de la bande » et s'arrêtait là ; mesurée
   au lot 14, elle valait 22,2 px pour une bande de 34 — le rembourrage seul
   décidait, et il tombait sous le plancher. Les 24 px se prennent donc dans les
   11,6 px que la bande n'utilisait pas : la verrière fait toujours 34 px, en
   desktop comme au doigt, et le châssis reste à 7,74 %. */
.vigie-verriere-commande {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    min-width: 44px;
    min-height: 24px;
    justify-content: center;
    padding: 0.1rem 0.4rem;
    font: inherit;
    font-size: 0.72rem;
    color: var(--vigie-texte-3);
    background: none;
    border: 1px solid transparent;
    border-radius: var(--vigie-rayon-petit);
    cursor: pointer;
    transition: color var(--vigie-transition), border-color var(--vigie-transition),
        background-color var(--vigie-transition);
}

.vigie-verriere-commande:hover,
.vigie-verriere-commande:focus-visible {
    color: var(--vigie-texte);
    background: var(--vigie-champ-fond);
    border-color: var(--vigie-bordure);
}

.vigie-verriere-commande .mud-icon-root {
    font-size: 1rem !important;
    width: 1rem;
    height: 1rem;
    flex-shrink: 0;
}

/* LE SELECT DE LA BANDE — la chrome partagée des trois <select> de la verrière :
   la PÉRIODE d'observation et la CADENCE de veille en zone D, le FILTRE DE
   GRAVITÉ de /etat en zone C (lot 9).

   Un <select> NATIF habillé en commande de la bande, et non une rangée de
   touches : cinq libellés tiennent sur un bandeau pleine largeur, pas dans une
   bande de 34 px qui doit encore loger le cap sur un écran de 380 px. C'est
   aussi la forme juste pour un réglage — le thème, à côté, est un contrôle
   unique lui aussi — et le natif apporte gratuitement le clavier, l'annonce par
   le lecteur d'écran et la liste déroulante du système.

   La règle s'appelait .vigie-verriere-periode alors que la cadence l'empruntait
   déjà : elle décrit une FORME, pas un réglage, et un troisième emprunteur
   rendait le nom franchement faux.

   RÈGLE DE LIBELLÉ, valable pour les trois : c'est le PLUS LONG des choix qui
   fixe la largeur du contrôle FERMÉ, et cette largeur se prend sur le cap. La
   période est donc abrégée (« 30 j », voir PeriodesAnalytics.LibelleCourt) —
   avec « aujourd'hui », la commande passait de 60 à 105 px et mangeait la moitié
   de ce que le lot 4 avait gagné pour le cap au doigt. */
.vigie-verriere-select {
    /* .vigie-verriere-commande est en inline-flex, ce qui n'a pas de sens pour
       un select : ses options ne sont pas des enfants de flux. */
    display: inline-block;
    padding: 0.1rem 1.15rem 0.1rem 0.45rem;
    font-variant-numeric: tabular-nums;
    text-align: left;

    /* Sans appearance:none, le select garde le fond gris clair de l'OS — un
       rectangle blanc dans une bande de tôle, illisible en thème sombre.
       Le chevron est alors REDESSINÉ en deux dégradés : aucune image, aucun
       asset externe, rien d'inline — compatible CSP script-src 'self'. */
    appearance: none;
    background-image:
        linear-gradient(45deg, transparent 50%, currentColor 50%),
        linear-gradient(135deg, currentColor 50%, transparent 50%);
    background-position: right 0.52rem center, right 0.32rem center;
    background-size: 4px 4px, 4px 4px;
    background-repeat: no-repeat;
}

/* La liste déroulante est rendue par le SYSTÈME, hors de la coque : sans ces
   deux déclarations elle hérite du texte clair de la bande sur le fond clair de
   l'OS, et les options deviennent illisibles en thème sombre. */
.vigie-verriere-select option {
    color: var(--vigie-texte);
    background-color: var(--vigie-fond);
}

/* Zone D — LES RÉGLAGES RAPIDES (lot 6).

   Le bouton n'ajoute AUCUN pixel de hauteur : il vit dans la bande existante,
   aux dimensions d'une commande de verrière, et son panneau est en absolu — donc
   à zéro châssis au repos. C'est la raison d'être du panneau : loger deux
   réglages GLOBAUX (qu'on ne peut pas tronquer sans mentir) sans reprendre un
   pixel au contenu ni un caractère au cap. */
.vigie-verriere-reglages-bouton {
    position: relative;
}

/* ALERTES COUPÉES : la seule commande de la bande qui change de teinte, et elle
   ne le fait que pour ça. Rouge plein, bordure assortie — la même couleur que le
   verdict de la zone A parce que c'est le même fait, vu de l'autre bout de la
   bande : là-bas « le parc n'est plus couvert », ici « l'interrupteur est
   baissé, clique pour le relever ». */
.vigie-verriere-commande-coupe,
.vigie-verriere-commande-coupe:hover,
.vigie-verriere-commande-coupe:focus-visible {
    color: var(--vigie-ko);
    border-color: color-mix(in srgb, var(--vigie-ko) 55%, transparent);
    background: color-mix(in srgb, var(--vigie-ko) var(--vigie-plaque), transparent);
}

/* Le mot « coupées » est mis en avant là où il s'affiche : c'est le seul libellé
   de commande de la bande qui annonce une avarie, pas un état de confort. */
.vigie-verriere-reglages-coupe {
    font-weight: 600;
    letter-spacing: 0.02em;
}

/* Le panneau des réglages : même matière et même sens que celui du verdict, mais
   ancré à DROITE — son bouton est le dernier de la bande, et un panneau qui
   s'ouvre à l'opposé de ce qu'on a cliqué se cherche.
   Sélecteur DOUBLÉ (.vigie-verdict-panneau.vigie-reglages-panneau) et non simple :
   .vigie-verdict-panneau pose « left: 0 » et vit plus bas dans la feuille, donc à
   spécificité égale c'est LUI qui gagnerait — le panneau s'ouvrait à gauche,
   sous le verdict, c'est-à-dire à l'opposé du bouton qu'on venait de cliquer. */
.vigie-verdict-panneau.vigie-reglages-panneau {
    left: auto;
    right: 0;
    width: min(23rem, 100%);
    gap: var(--vigie-pas-2);
}

/* Un réglage et son explication. La bordure gauche est ce que .vigie-avarie
   reprend en rouge quand l'alerting est coupé : le bloc entier bascule alors,
   pas seulement une phrase. */
.vigie-reglages-bloc {
    display: flex;
    flex-direction: column;
    gap: var(--vigie-pas-1);
    padding: var(--vigie-pas-1) var(--vigie-pas-2);
    border: 1px solid var(--vigie-bordure);
    border-left: 3px solid var(--vigie-bordure-forte);
    border-radius: var(--vigie-rayon);
}

.vigie-reglages-ligne {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--vigie-pas-2);
}

.vigie-reglages-intitule {
    font-size: 0.86rem;
    font-weight: 600;
    color: var(--vigie-texte);
}

/* Un bouton qui dit ce qu'il VA FAIRE (« Couper », « Réarmer ») plutôt qu'une
   bascule dont on lit la position à l'envers. 44 px de large comme toute cible
   de la coque. */
.vigie-reglages-action {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.35rem;
    min-width: 44px;
    padding: 0.22rem 0.7rem;
    font: inherit;
    font-size: 0.78rem;
    font-family: var(--vigie-police-mono);
    color: var(--vigie-texte-2);
    background: var(--vigie-champ-fond);
    border: 1px solid var(--vigie-bordure);
    border-radius: var(--vigie-rayon-petit);
    cursor: pointer;
    white-space: nowrap;
    transition: color var(--vigie-transition), border-color var(--vigie-transition);
}

.vigie-reglages-action:hover:not(:disabled),
.vigie-reglages-action:focus-visible {
    color: var(--vigie-texte);
    border-color: var(--vigie-bordure-vive);
}

.vigie-reglages-action:disabled {
    cursor: progress;
    opacity: 0.6;
}

/* RÉARMER est la seule action de tout le panneau qu'on veut voir en premier :
   elle est en accent plein, alors que « Couper » reste sobre. Couper est un
   geste qu'on fait sciemment ; réarmer est celui qu'on doit RETROUVER. */
.vigie-reglages-action-rearmer {
    color: var(--vigie-accent);
    border-color: color-mix(in srgb, var(--vigie-accent) 55%, transparent);
    font-weight: 600;
}

.vigie-reglages-aide {
    margin: 0;
    font-size: 0.78rem;
    line-height: 1.45;
}

.vigie-reglages-avarie {
    margin: 0;
    font-size: 0.78rem;
    line-height: 1.45;
    color: var(--vigie-ko);
}

/* LA RELANCE DE LA COLLECTE (lot 7) — la ligne de retour et le voyant.

   Le retour est une région vivante (role="status") VIDE au repos : un <p> sans
   contenu ne crée aucune boîte de ligne, il ne coûte donc rien tant qu'il n'a
   rien à dire, mais il doit exister dès le premier rendu pour qu'un lecteur
   d'écran annonce son remplissage. Aucune teinte propre — un refus poli
   (« trop tôt, réessayez dans 7 s ») n'est pas une avarie, et le rouge de la
   coque garde son rôle unique : ce qui est cassé. */
.vigie-reglages-retour {
    margin: 0;
    font-size: 0.78rem;
    line-height: 1.45;
    color: var(--vigie-texte-2);
}

/* Le VOYANT : une ligne par collecteur. Tout ce qu'il montre vient des cycles
   eux-mêmes (voir DemandesCollecte) — il n'existe aucun état d'interface à côté
   qui pourrait afficher « en cours » sans qu'un cycle tourne vraiment. */
.vigie-reglages-collecte {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: 0.74rem;
}

.vigie-reglages-collecte li {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--vigie-pas-1);
}

.vigie-reglages-collecte-nom {
    color: var(--vigie-texte-2);
    white-space: nowrap;
}

/* Chiffres tabulaires : les « il y a N s » d'une ligne à l'autre ne doivent pas
   danser à chaque battement du filet. */
.vigie-reglages-collecte-etat {
    font-family: var(--vigie-police-mono);
    font-variant-numeric: tabular-nums;
    color: var(--vigie-texte-3);
    text-align: right;
}

/* AMBRE et non rouge : le cas de loin le plus fréquent est « token GitHub
   manquant », que le catalogue range en INFO (« Configuration absente : un
   collecteur attend poliment son secret, ce n'est pas une panne »). Le rouge
   de la coque n'a qu'un rôle — ce qui est cassé — et le lui emprunter ici
   userait le signal partout ailleurs. Ce qu'il faut dire, l'ambre le dit :
   « ce collecteur n'a rien rapporté, va voir pourquoi ». */
.vigie-reglages-collecte-muet {
    color: var(--vigie-attention);
}

/* Le bloc du THÈME : liseré en pointillés là où les deux autres l'ont plein.
   Ce n'est pas de la décoration — il dit que ce réglage-là ne vaut QUE pour ce
   navigateur, alors que les deux autres sont en base et valent pour tous les
   onglets. La phrase d'aide le dit aussi ; la matière le fait voir avant. */
.vigie-reglages-bloc-local {
    border-left-style: dashed;
    border-left-color: var(--vigie-bordure);
}

/* Bouton d'ouverture rapide : il ressemble à un champ de recherche parce que
   c'est exactement ce qu'il ouvre. Le raccourci est affiché, pas deviné. */
.vigie-quickopen {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    min-height: 24px;
    padding: 0.12rem 0.5rem;
    font: inherit;
    font-size: 0.74rem;
    color: var(--vigie-texte-3);
    background: var(--vigie-champ-fond);
    border: 1px solid var(--vigie-bordure);
    border-radius: var(--vigie-rayon-petit);
    cursor: pointer;
    flex-shrink: 0;
    transition: border-color var(--vigie-transition), color var(--vigie-transition);
}

/* La palette PROMUE : au centre, et assez large pour se lire comme le champ de
   commande de l'outil plutôt que comme un bouton de plus. C'est le seul point
   d'entrée vers les logs et les liens externes — un raccourci qu'il faut
   connaître ne sert qu'à ceux qui le connaissent déjà. */
.vigie-verriere-palette {
    grid-column: 2;
    width: clamp(13rem, 30vw, 26rem);
    justify-content: flex-start;
}

.vigie-verriere-palette-libelle {
    margin-right: auto;
}

.vigie-quickopen:hover {
    color: var(--vigie-texte);
    border-color: var(--vigie-bordure-vive);
}

.vigie-quickopen .mud-icon-root {
    font-size: 0.95rem !important;
    width: 0.95rem;
    height: 0.95rem;
}

/* Le capuchon de touche du rappel « Ctrl K ». Il était en ATONE, et c'était une
   erreur de rôle : l'atone habille ce que l'œil doit sauter (une valeur absente,
   un horodatage relatif), alors qu'un raccourci clavier est une COMMANDE — la
   seule chose à lire dans cette barre après le mot « rechercher ». Le lire
   supposait de le chercher : à 11,2 px, l'atone tenait Lc 38,4. Il passe au
   texte secondaire, soit Lc 57,8 / 7,32:1 pour le même dessin de touche. */
.vigie-touche {
    font-family: var(--vigie-police-mono);
    font-size: 0.66rem;
    line-height: 1.4;
    padding: 0 0.3rem;
    color: var(--vigie-texte-2);
    background: var(--vigie-badge-fond);
    border: 1px solid var(--vigie-bordure);
    border-radius: var(--vigie-rayon);
    white-space: nowrap;
}

/* PAS DE CLAVIER, PAS DE RAPPEL DE TOUCHE. Un « Ctrl K » sur un écran tactile
   n'est pas une aide, c'est une commande qu'on ne peut pas exécuter — et le
   panneau des réglages écrit ailleurs dans cette feuille qu'un voyant qui ne
   reflète rien est un mensonge ; un raccourci indisponible en est un aussi.
   LA LARGEUR NE SUFFISAIT PAS. Le repli mobile masque déjà le capuchon de la
   palette sous 900 px, mais c'est une règle de PLACE : une tablette tactile de
   1024 px continuait de proposer Ctrl+K, et la palette ouverte à 380 px
   affichait encore « Échap » en tête et « ↑ ↓ naviguer · Entrée ouvrir » au
   pied — trois rappels de clavier sur un téléphone. La question n'est pas
   combien de pixels, c'est s'il y a un clavier : c'est « hover: none » qui la
   pose.
   CE QU'ON NE PERD PAS : l'aria-label de la palette continue d'annoncer le
   raccourci pour qui navigue au clavier sur un écran tactile, la palette se
   ferme au tap sur son voile, et les commandes s'ouvrent au tap. */
@media (hover: none) {
    .vigie-touche,
    .vigie-aide-clavier {
        display: none;
    }
}

/* --- Barre d'état ----------------------------------------------------------- */

/* C'est elle qui donne immédiatement le sentiment « IDE » : branche et build à
   gauche, santé des collecteurs au centre, alertes et horloge UTC à droite.
   Fixée au viewport : elle ne quitte jamais l'écran. */
/* La barre d'état EST le bord bas du bezel : elle court d'un bord à l'autre du
   viewport (right: 0 et non plus jusqu'au montant), pour que le cadre se ferme
   sans rupture dans les deux angles inférieurs. Elle passe donc devant le pied
   du montant droit, qu'elle prolonge exactement dans la même tôle. */
.vigie-etat-barre {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 60;
    display: flex;
    align-items: center;
    gap: 0.75rem;
    height: var(--vigie-h-etat);
    padding: 0 0.5rem;
    border-top: 1px solid var(--vigie-coque-arete);
    font-family: var(--vigie-police-mono);
    font-size: 0.78rem;
    color: var(--vigie-texte-2);
    white-space: nowrap;
    overflow: hidden;

    /* Le bord inférieur du pupitre : tôle profilée horizontale (éclairée au
       centre, sombre sur les arêtes) et rangée de rivets sous les pastilles. */
    background-color: var(--vigie-chrome);
    background-image:
        radial-gradient(circle, var(--vigie-rivet) 0 1.3px, transparent 1.9px),
        linear-gradient(180deg, var(--vigie-metal-2) 0%, var(--vigie-metal-1) 100%);
    background-size: 96px 9px, 100% 100%;
    background-position: left top 4px, 0 0;
    background-repeat: repeat-x, no-repeat;
}

/* Balayage d'accent sur l'arête haute : la barre d'état se lit comme le bord
   inférieur d'un pupitre sous tension. */
.vigie-etat-barre::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 1px;
    pointer-events: none;
    background: linear-gradient(90deg, transparent, var(--vigie-arete) 30%, var(--vigie-arete) 70%, transparent);
}

.vigie-etat-bloc {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    min-width: 0;
}

.vigie-etat-droite {
    margin-left: auto;
}

/* Même arbitrage que les commandes de la verrière : la barre fait 35,7 px en
   desktop et 34 au doigt, ses éléments n'en occupaient que 20. Les 24 px du
   plancher AA se prennent dans ce qui restait — la barre, elle, ne bouge pas. */
.vigie-etat-element {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    min-height: 24px;
    color: var(--vigie-texte-2);
    text-decoration: none;
    padding: 0 0.15rem;
    border-radius: var(--vigie-rayon);
}

a.vigie-etat-element:hover {
    color: var(--vigie-accent);
}

/* Même facteur +25 % que les pastilles : le compteur d'incidents et l'horloge
   sont des ÉTATS eux aussi. */
.vigie-etat-element .mud-icon-root {
    font-size: 1.12rem !important;
    width: 1.12rem;
    height: 1.12rem;
    flex-shrink: 0;
}

/* Badges qui OUVRENT un panneau : « À propos », resté dans la barre d'état, et
   le VERDICT, parti en zone A de la verrière. De vrais boutons, remis à plat et
   alignés sur les autres éléments de leur bande, avec fond au survol/focus comme
   eux. Les deux partagent ces règles parce qu'ils partagent leur matière — c'est
   la bande qui a changé, pas le composant. */
.vigie-etat-alertes,
.vigie-etat-apropos {
    font: inherit;
    background: none;
    border: none;
    cursor: pointer;
    line-height: inherit;
}

.vigie-etat-alertes:hover,
.vigie-etat-alertes:focus-visible,
.vigie-etat-apropos:hover,
.vigie-etat-apropos:focus-visible {
    background: var(--vigie-surface-2);
    color: var(--vigie-accent);
}

.vigie-etat-alertes-ok {
    color: var(--vigie-ok);
}

.vigie-etat-alertes-attention {
    color: var(--vigie-attention);
}

/* Rouge pour le critique ET pour le coupe-circuit : dans les deux cas, le
   superviseur doit s'arrêter dessus. La cloche barrée distingue « alerting
   coupé » d'un simple incident. */
.vigie-etat-alertes-ko,
.vigie-etat-alertes-coupe {
    color: var(--vigie-ko);
}

/* Survol du badge : le fond ne doit pas effacer la couleur d'état — on garde la
   teinte de gravité plutôt que de repasser à l'accent. */
.vigie-etat-alertes-attention:hover,
.vigie-etat-alertes-attention:focus-visible {
    color: var(--vigie-attention);
}

.vigie-etat-alertes-ko:hover,
.vigie-etat-alertes-ko:focus-visible,
.vigie-etat-alertes-coupe:hover,
.vigie-etat-alertes-coupe:focus-visible {
    color: var(--vigie-ko);
}

.vigie-etat-horloge {
    font-variant-numeric: tabular-nums;
    color: var(--vigie-texte-2);
}

/* --- Indicateur de réception ------------------------------------------------- */

/* Quatre barres montantes, comme un indicateur de signal. Le nombre allumé est
   la part de collecteurs opérationnels — c'est une MESURE, pas un ornement (voir
   IndicateurSignal.razor). Aligné sur la ligne de base des pastilles voisines. */
.vigie-signal {
    display: inline-flex;
    align-items: flex-end;
    gap: 2px;
    height: 0.95rem;
    flex: none;
    padding: 0 0.15rem;
}

.vigie-signal-barre {
    width: 3px;
    border-radius: 1px;
    background: currentColor;
}

/* Une hauteur par rang : c'est l'escalier qui fait lire « réception ».
   CINQ rangs depuis le 28/08/2026, un par famille surveillée : le compte est
   exact, l'arrondi a disparu. Les paliers sont répartis régulièrement — un
   escalier irrégulier se lirait comme une échelle de valeurs, ce que cet
   instrument n'est pas. */
.vigie-signal-barre[data-barre="1"] {
    height: 28%;
}

.vigie-signal-barre[data-barre="2"] {
    height: 46%;
}

.vigie-signal-barre[data-barre="3"] {
    height: 64%;
}

.vigie-signal-barre[data-barre="4"] {
    height: 82%;
}

.vigie-signal-barre[data-barre="5"] {
    height: 100%;
}

/* Éteinte : la barre reste VISIBLE en creux, sinon on ne peut pas lire « trois
   sur quatre » — on lirait seulement « trois ». */
.vigie-signal-off {
    background: var(--vigie-bordure-forte);
    opacity: 0.55;
}

/* Micro-pastilles des collecteurs : une par famille, réduite à l'essentiel.
   Elles restent VISIBLES en 380 px — c'est le pouls de la supervision. */
.vigie-pastilles-etat {
    display: flex;
    align-items: center;
    gap: 0.1rem;
    min-width: 0;
}

/* +25 % sur toute la pastille (police 0,7 → 0,875 rem, icône 0,95 → 1,19 rem) :
   c'est la demande, et elle est juste — ces cinq voyants sont le pouls de la
   supervision et ils se lisaient à la loupe sur un téléphone. La hauteur de la
   barre (--vigie-h-etat) a suivi le même facteur. */
.vigie-pastille-etat {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font: inherit;
    font-family: var(--vigie-police-mono);
    font-size: 0.875rem;
    line-height: 1;
    padding: 0.2rem 0.38rem;
    border: 1px solid transparent;
    border-radius: var(--vigie-rayon);
    background: none;
    color: var(--vigie-texte-3);
    white-space: nowrap;
    cursor: pointer;
    transition: background-color var(--vigie-transition);
}

.vigie-pastille-etat:hover {
    background: var(--vigie-chrome-actif);
}

.vigie-pastille-etat .mud-icon-root {
    font-size: 1.19rem !important;
    width: 1.19rem;
    height: 1.19rem;
    flex-shrink: 0;
}

.vigie-pastille-etat-ok {
    color: var(--vigie-ok);
}

.vigie-pastille-etat-attention {
    color: var(--vigie-attention);
}

.vigie-pastille-etat-critique {
    color: var(--vigie-ko);
    animation: vigie-pastille-alerte 2.4s ease-in-out infinite;
}

.vigie-pastille-etat-attente {
    color: var(--vigie-texte-3);
}

@keyframes vigie-pastille-alerte {
    0%, 100% {
        opacity: 1;
    }

    50% {
        opacity: 0.45;
    }
}

/* Panneau de détail d'une pastille : il s'ouvre VERS LE HAUT depuis la barre
   d'état, à la manière d'un panneau d'IDE. Surface opaque — le texte technique
   doit rester net. */
.vigie-pastille-voile {
    position: fixed;
    inset: 0;
    z-index: 70;
}

.vigie-pastille-panneau {
    position: fixed;
    bottom: var(--vigie-h-etat);
    right: 0.4rem;
    width: min(24rem, calc(100vw - 0.8rem));
    z-index: 71;
    padding: 0.9rem 1.05rem;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    background: var(--vigie-surface-opaque);
    border: 1px solid var(--vigie-bordure-forte);
    border-radius: var(--vigie-rayon) var(--vigie-rayon) 0 0;
    color: var(--vigie-texte);
    white-space: normal;
    box-shadow: 0 -8px 28px var(--vigie-ombre-forte);
}

/* Le panneau du VERDICT, lui, pend de la verrière : mêmes matières que celui des
   pastilles, sens inverse. Il s'ancre à GAUCHE parce que c'est là qu'est son
   badge — un panneau qui s'ouvre à l'opposé de ce qu'on a cliqué se cherche.

   ABSOLU DANS LA VERRIÈRE, et pas fixe au viewport comme celui de la barre
   d'état : la verrière est COLLANTE, donc son ordonnée dépend du défilement (au
   doigt, elle démarre sous le rail puis remonte au ras du bord). Un panneau posé
   à « top: 34 px » du viewport était juste une fois sur deux — il tombait au
   milieu du rail tant qu'on n'avait pas fait défiler. « top: 100 % » du bloc
   collant est vrai dans les deux cas. */
.vigie-verdict-panneau {
    position: absolute;
    top: 100%;
    left: 0;
    width: min(24rem, 100%);
    max-height: calc(100dvh - var(--vigie-h-verriere) - var(--vigie-h-etat) - 0.8rem);
    overflow-y: auto;
    z-index: 71;
    padding: 0.9rem 1.05rem;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    background: var(--vigie-surface-opaque);
    border: 1px solid var(--vigie-bordure-forte);
    border-radius: 0 0 var(--vigie-rayon) var(--vigie-rayon);
    color: var(--vigie-texte);
    white-space: normal;
    box-shadow: 0 8px 28px var(--vigie-ombre-forte);
}

.vigie-pastille-panneau-tete {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.vigie-pastille-panneau-titre {
    font-weight: 600;
    font-size: 0.98rem;
}

.vigie-pastille-panneau-incidents {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.vigie-pastille-panneau-incidents li {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.3rem;
    font-size: 0.84rem;
    line-height: 1.5;
    overflow-wrap: anywhere;
}

.vigie-pastille-panneau-pied {
    margin: 0;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.3rem;
}

.vigie-pastille-panneau-lien {
    margin-left: auto;
    color: var(--vigie-accent);
    text-decoration: none;
    font-size: 0.8rem;
}

.vigie-pastille-panneau-lien:hover {
    text-decoration: underline;
}

/* --- Sas de secours (pages d'état HTTP) ------------------------------------- */

/* La coque dégradée des pages 404 et erreur : une traverse métallique portant
   la marque et QUATRE commandes qui fonctionnent sans circuit Blazor (liens
   href, POST de déconnexion, bascule de thème déléguée à theme.js). Voir
   LayoutSecours.razor pour le pourquoi. */
.vigie-sas {
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
}

.vigie-sas-barre {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.6rem 1rem;
    padding: 0.5rem clamp(0.7rem, 1.6vw, 1.4rem);
    border-bottom: 1px solid var(--vigie-coque-arete);
    background-color: var(--vigie-chrome);
    background-image: linear-gradient(180deg, var(--vigie-metal-1) 0%, var(--vigie-metal-2) 100%);
}

.vigie-sas-marque {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    color: var(--vigie-texte);
    text-decoration: none;
    line-height: 1;
}

.vigie-sas-mention {
    font-family: var(--vigie-police-mono);
    font-size: 0.66rem;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    color: var(--vigie-attention);
}

/* Les commandes se collent à droite en large, et repassent à la ligne en
   étroit : sur un téléphone elles doivent TOUTES rester visibles, pas défiler. */
.vigie-sas-commandes {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-left: auto;
}

.vigie-sas-corps {
    flex: 1 1 auto;
    min-width: 0;
}

/* --- Palette de commandes (Ctrl+K) ------------------------------------------ */

.vigie-palette-voile {
    position: fixed;
    inset: 0;
    z-index: 80;
    background: color-mix(in srgb, var(--vigie-chrome) 62%, transparent);
}

.vigie-palette {
    position: fixed;
    top: 12vh;
    left: 50%;
    transform: translateX(-50%);
    z-index: 81;
    width: min(38rem, calc(100vw - 1.5rem));
    display: flex;
    flex-direction: column;
    background: var(--vigie-surface-opaque);
    background-image: var(--vigie-scanlines);
    border: 1px solid var(--vigie-bordure-forte);
    border-radius: var(--vigie-rayon);
    box-shadow: 0 20px 48px var(--vigie-ombre-forte);
    overflow: hidden;
}

.vigie-palette-tete {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.55rem 0.7rem;
    border-bottom: 1px solid var(--vigie-bordure);
}

.vigie-palette-tete .mud-icon-root {
    font-size: 1.1rem !important;
    width: 1.1rem;
    height: 1.1rem;
    color: var(--vigie-texte-3);
    flex-shrink: 0;
}

.vigie-palette-champ {
    flex: 1 1 auto;
    min-width: 0;
    font-family: var(--vigie-police-mono);
    font-size: 0.95rem;
    color: var(--vigie-texte);
    background: none;
    border: none;
    outline: none;
    padding: 0;
}

.vigie-palette-champ::placeholder {
    color: var(--vigie-texte-3);
}

.vigie-palette-liste {
    list-style: none;
    margin: 0;
    padding: 0.25rem 0;
    max-height: min(24rem, 55vh);
    overflow-y: auto;
}

.vigie-palette-item {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    width: 100%;
    padding: 0.45rem 0.75rem;
    font: inherit;
    font-size: 0.9rem;
    text-align: left;
    color: var(--vigie-texte-2);
    background: none;
    border: none;
    border-left: 2px solid transparent;
    cursor: pointer;
}

.vigie-palette-item .mud-icon-root {
    font-size: 1.1rem !important;
    width: 1.1rem;
    height: 1.1rem;
    color: var(--vigie-texte-3);
    flex-shrink: 0;
}

/* Ligne survolée au clavier : c'est la SEULE mise en avant — pas de survol
   souris concurrent qui ferait croire à deux sélections. */
.vigie-palette-item-actif {
    color: var(--vigie-texte);
    background: var(--vigie-surface-2);
    border-left-color: var(--vigie-accent);
}

.vigie-palette-item-actif .mud-icon-root {
    color: var(--vigie-accent);
}

.vigie-palette-libelle {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.vigie-palette-detail {
    font-family: var(--vigie-police-mono);
    font-size: 0.74rem;
    color: var(--vigie-texte-3);
    flex-shrink: 0;
    max-width: 45%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.vigie-palette-vide {
    padding: 1rem 0.75rem;
    font-size: 0.9rem;
    color: var(--vigie-texte-3);
}

.vigie-palette-pied {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    padding: 0.4rem 0.7rem;
    border-top: 1px dashed var(--vigie-bordure);
    background: var(--vigie-surface-2);
    font-size: 0.72rem;
    color: var(--vigie-texte-3);
    flex-wrap: wrap;
}

/* --- Page ------------------------------------------------------------------- */

.vigie-page {
    position: relative;
    padding: 1rem clamp(0.7rem, 1.6vw, 1.4rem) 2rem;
}

/* --- Grille de douze colonnes (lot 8) --------------------------------------- */

/* Le rythme d'une page qui empilait des sections de largeurs hétérogènes. Douze
   colonnes parce que 12 se divise par 2, 3, 4 et 6 : moitié, tiers, quart et
   deux tiers sont tous exprimables sans fraction, ce qu'un 10 ne permet pas.

   L'INTÉRÊT N'EST PAS LE RYTHME, c'est de poser côte à côte ce qui se compare.
   La règle de largeur est portée par le CONTENEUR (« > * ») et pas par les
   sections : une Cartouche posée hors d'une grille garde donc exactement son
   comportement d'avant, et aucune autre page n'a eu à changer.

   L'espacement vertical passe de la marge des sections à la gouttière de la
   grille : deux sections d'une même rangée n'ont plus de raison de porter
   chacune une marge basse, et le gap donne le même pas au vertical qu'à
   l'horizontal. */
.vigie-grille {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    gap: var(--vigie-pas-3);
    align-items: start;
}

.vigie-grille > * {
    grid-column: span 12;
    min-width: 0;
}

/* Les marges basses héritées des trois familles de sections. Sélecteurs à deux
   classes et non « > * » : les règles de .vigie-section et consorts vivent plus
   bas dans la feuille et gagneraient à égalité de spécificité. */
.vigie-grille > .vigie-section,
.vigie-grille > .vigie-bandeau,
.vigie-grille > .vigie-veille {
    margin-bottom: 0;
}

/* UN SEUL SEUIL, ET IL EST MESURÉ : 1200 px pour la SEULE paire de la grille,
   le 7/5 (latence + alertes sur /site, familles + chronologie sur /etat). Ni
   l'une ni l'autre n'a de largeur plancher : le graphe est en width 100 %, les
   panneaux empilent des lignes de texte. En dessous de 1200, deux colonnes
   deviennent deux colonnes maigres, d'où le seuil.

   IL Y AVAIT UNE SECONDE PAIRE, le 8/4 (derniers runs + pipeline) à 1320 px, et
   elle est DÉFAITE : le pipeline est une timeline (Restore → … → Audit), et sur
   un tiers de largeur ses étapes — leur nombre dépend du contrat de la stack —
   se repliaient sur deux lignes — une
   séquence linéaire qui passe à la ligne ne raconte plus d'ordre. Le stepper et
   la table des runs (753 px de largeur naturelle, mesurés au navigateur)
   reprennent donc chacun leur rangée pleine : deux sections qui réclament
   toutes deux leur largeur ne font pas une paire.

   En dessous, tout retombe sur une colonne unique — l'ordre du DOM est déjà
   l'ordre de lecture (verdict, veille, exploitation, livraison, audience). */

/* MAIS « UNE COLONNE UNIQUE » N'EN ÉTAIT PAS UNE, et ça se voyait à 320 px. Sous
   1200, chaque section couvre bien les douze colonnes — sauf que les douze
   pistes existent toujours, et avec elles ONZE gouttières de 29,75 px. Une
   section n'occupe donc jamais moins de 327 px de gouttières AVANT son premier
   pixel de contenu : la page a un plancher de 351 px, et /etat, /site et
   /analytics débordaient de 19 px sur un écran de 320.
   À 380 px, elles tenaient — à 0,1 px près. Ce n'est pas une marge, c'est une
   coïncidence : un rem un peu plus grand, un zoom texte, et la même page
   débordait à la largeur du contrat.
   La grille passe donc à UNE piste tant que rien ne se met en paire, et les
   sections cessent d'en réclamer douze. La gouttière redevient ce qu'elle était
   déjà visuellement : un espacement vertical. */
@media (max-width: 1199px) {
    .vigie-grille {
        grid-template-columns: minmax(0, 1fr);
    }

    .vigie-grille > * {
        grid-column: span 1;
    }
}

@media (min-width: 1200px) {
    .vigie-grille > .vigie-col-5 {
        grid-column: span 5;
    }

    .vigie-grille > .vigie-col-7 {
        grid-column: span 7;
    }
}

/* Balayage d'allumage : un trait lumineux traverse l'écran UNE FOIS, en 260 ms,
   quand une page apparaît — la dalle qui se rallume au changement de canal.
   · `position: fixed` et non `absolute` : il est ainsi borné par le viewport,
     et .vigie-page n'a pas besoin d'un `overflow: hidden` qui casserait le
     comportement des éléments collants qu'elle contient (gouttière des logs) ;
   · `forwards` + opacité finale nulle : le trait ne revient jamais. Il ne se
     rejoue qu'à la RECRÉATION du bloc, donc à un vrai changement de page — un
     signal du bus temps réel ne le déclenche pas ;
   · faible alpha et 260 ms : on le perçoit sans jamais l'attendre. */
.vigie-page::after {
    content: "";
    position: fixed;
    left: 0;
    right: 0;
    top: 0;
    height: 130px;
    z-index: 2;
    pointer-events: none;
    opacity: 0;
    background: linear-gradient(180deg,
        transparent 0%,
        color-mix(in srgb, var(--vigie-accent) 16%, transparent) 55%,
        color-mix(in srgb, var(--vigie-accent) 34%, transparent) 78%,
        transparent 100%);
    animation: vigie-allumage-balayage 260ms ease-out 1 forwards;
}

@keyframes vigie-allumage-balayage {
    from {
        opacity: 1;
        transform: translateY(-130px);
    }

    to {
        opacity: 0;
        transform: translateY(100dvh);
    }
}

/* --- Utilitaires ------------------------------------------------------------ */

/* Surface d'outil : opaque, bordure 1 px franche, angle quasi vif, ombre
   discrète. Le !important neutralise le background-color que MudPaper pose
   depuis le thème. Chaque surface est un MODULE DE CONSOLE : liseré interne
   fin (double cadre), crochets lumineux aux quatre coins, ligne de balayage
   sur l'arête supérieure, scanlines subliminales en sombre, et un très court
   « allumage » à l'insertion dans le DOM. */
.vigie-surface {
    position: relative;
    background-color: var(--vigie-surface-opaque) !important;
    background-image: var(--vigie-scanlines);
    border: 1px solid var(--vigie-bordure);
    border-radius: var(--vigie-rayon);
    box-shadow: 0 1px 2px var(--vigie-ombre);
    outline: 1px solid var(--vigie-liseret-interne);
    outline-offset: -5px;
    animation: vigie-allumage 150ms ease-out;
}

/* Crochets de coin + balayage d'arête : un seul pseudo-élément, neuf calques
   de dégradés bornés (deux traits par coin, une ligne en haut). Les crochets
   chevauchent la bordure d'un pixel vers l'intérieur : le cadre semble
   « verrouillé » aux angles, comme un cadre holographique. */
.vigie-surface::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    border-radius: inherit;
    background:
        linear-gradient(90deg, transparent, var(--vigie-arete) 30%, var(--vigie-arete) 70%, transparent) no-repeat top center / min(60%, 260px) 1px,
        linear-gradient(var(--vigie-coin), var(--vigie-coin)) no-repeat left top / 11px 2px,
        linear-gradient(var(--vigie-coin), var(--vigie-coin)) no-repeat left top / 2px 11px,
        linear-gradient(var(--vigie-coin), var(--vigie-coin)) no-repeat right top / 11px 2px,
        linear-gradient(var(--vigie-coin), var(--vigie-coin)) no-repeat right top / 2px 11px,
        linear-gradient(var(--vigie-coin), var(--vigie-coin)) no-repeat left bottom / 11px 2px,
        linear-gradient(var(--vigie-coin), var(--vigie-coin)) no-repeat left bottom / 2px 11px,
        linear-gradient(var(--vigie-coin), var(--vigie-coin)) no-repeat right bottom / 11px 2px,
        linear-gradient(var(--vigie-coin), var(--vigie-coin)) no-repeat right bottom / 2px 11px;
}

/* Un bandeau plat (le conteneur de table) garde le cadre mais pas le liseré
   interne : à 5 px du bord, il traverserait son contenu peu margé. */
.vigie-table-defilement.vigie-surface {
    outline: none;
}

/* Une surface focusable (carte de site) retrouve son anneau de focus : le
   liseré décoratif partage la propriété outline, il ne doit jamais gagner. */
.vigie-surface:focus-visible {
    outline: 2px solid var(--vigie-accent);
    outline-offset: 1px;
}

@keyframes vigie-allumage {
    from {
        opacity: 0;
        transform: scale(0.985);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

.vigie-tabulaire {
    font-variant-numeric: tabular-nums;
}

.vigie-mono {
    font-family: var(--vigie-police-mono);
}

.vigie-entete {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: flex-end;
    gap: 0.6rem 1rem;
    margin-bottom: 1.3rem;
    padding-bottom: 0.7rem;
    border-bottom: 1px solid var(--vigie-bordure);
}

.vigie-titre {
    font-size: 1.35rem;
    font-weight: 600;
    margin: 0;
    letter-spacing: -0.01em;
}

/* Kicker façon invite de terminal : la commande qui produirait cette vue. */
.vigie-kicker {
    margin: 0 0 0.2rem;
    font-family: var(--vigie-police-mono);
    font-size: 0.74rem;
    letter-spacing: 0.06em;
    color: var(--vigie-texte-3);
}

.vigie-texte-atone {
    color: var(--vigie-texte-3);
    font-size: 0.86rem;
}

/* LE MICRO-LIBELLÉ : la gravure sous un cadran. « TTFB », « P95 », « 24H »,
   « VISITEURS », le titre d'un groupe d'instruments — tout ce qui NOMME une
   valeur sans être la valeur. Un seul rôle, donc une seule règle.
   Le lot en annonçait CINQ ; l'état réel du dépôt en comptait HUIT — les trois
   qui manquaient à l'inventaire (le libellé de l'afficheur TTFB, celui d'un
   segment de barre, celui d'une tuile de KPI) portent exactement le même rôle.
   Elles avaient divergé sans que personne ne le décide : trois corps
   (0,64 / 0,66 / 0,68 rem) et trois approches (0,10 / 0,12 / 0,14 em) pour la
   même gravure. La règle commune retient les valeurs médianes ; l'écart de
   rendu est de ±0,34 px sur le corps et de ±0,02 em sur l'approche, soit moins
   d'un pixel sur un libellé de huit caractères — c'est le prix, assumé, de ne
   plus avoir huit vérités.
   CE QUI N'EST PAS DANS CETTE RÈGLE, et pourquoi. Une douzaine d'autres
   sélecteurs partagent la même SIGNATURE (mono, capitales, atone) sans partager
   le RÔLE : le titre de groupe du rail (0,56 rem, un autre registre), l'axe de
   la bande de jours, les en-têtes de colonne, les libellés de champ de la page
   de connexion, un terme de définition. Les rassembler parce qu'ils se
   ressemblent à l'écran referait, à l'envers, l'erreur qu'on corrige ici.
   Les rôles que la gravure NE porte pas restent chez leur classe : le titre de
   groupe de KPI est un <h3>, il garde sa marge nulle et sa graisse. */
.vigie-hero-libelle,
.vigie-uptime-libelle,
.vigie-kpi-groupe-titre,
.vigie-kpi-tuile-libelle,
.vigie-kpi-libelle,
.vigie-ttfb-libelle,
.vigie-barre-seg-libelle,
.vigie-stat-libelle {
    font-family: var(--vigie-police-mono);
    font-size: 0.66rem;
    color: var(--vigie-texte-3);
    text-transform: uppercase;
    letter-spacing: 0.12em;
}

.vigie-deconnexion {
    margin: 0;
}

.vigie-horodatage {
    cursor: help;
    text-decoration: underline dotted color-mix(in srgb, var(--vigie-texte-3) 55%, transparent);
    text-underline-offset: 3px;
}

/* Badge mono (version, sha) : petit cartouche technique, icône commit en tête. */
.vigie-badge-mono {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font-family: var(--vigie-police-mono);
    font-size: 0.78rem;
    color: var(--vigie-texte-2);
    background: var(--vigie-badge-fond);
    border: 1px solid var(--vigie-bordure);
    border-radius: var(--vigie-rayon-petit);
    padding: 0.1rem 0.45rem;
    white-space: nowrap;
}

.vigie-badge-ico {
    font-size: 1rem !important;
    width: 1rem;
    height: 1rem;
    color: var(--vigie-texte-3);
    flex-shrink: 0;
}

/* Chip maison (stack, code) : pas de bouton MudChip dans une carte cliquable. */
.vigie-chip {
    font-family: var(--vigie-police-mono);
    font-size: 0.72rem;
    color: var(--vigie-texte-2);
    background: var(--vigie-badge-fond);
    border: 1px solid var(--vigie-bordure);
    border-radius: var(--vigie-rayon-petit);
    padding: 0.08rem 0.4rem;
    white-space: nowrap;
    letter-spacing: 0.04em;
}

.vigie-chip-attention {
    color: var(--vigie-attention);
    border-color: color-mix(in srgb, var(--vigie-attention) 45%, transparent);
    background: color-mix(in srgb, var(--vigie-attention) var(--vigie-plaque), transparent);
}

/* LE CHIP « RESTREINT » EST ROUGE PAR DEMANDE EXPLICITE (2026-08-30), et le
   conflit a été instruit avant d'obéir : dans cette grille, le rouge disait
   jusqu'ici « en panne » (pastille down, liseré d'avarie, chip TLS ko) et
   l'ambre « attention ». Un site restreint n'est ni l'un ni l'autre : c'est une
   DÉCISION de l'opérateur, pas un incident. À teinte égale, c'est donc la FORME
   qui sépare la décision de l'avarie : bordure TIRETÉE — la convention des
   frontières choisies, celle du pied des panneaux — et glyphe d'interdiction,
   là où tous les signaux d'avarie restent pleins et sans glyphe. Le tireté est
   densifié à 55 % (contre 45 % pour les chips pleins) parce qu'un trait
   interrompu perd de l'encre : à densité égale il paraîtrait délavé.
   Le couple rouge/surface est déjà tenu par ContrasteTokensTests (--vigie-ko
   tient le palier 60 sur toutes les surfaces relevées, jour et nuit). */
.vigie-chip-restreint {
    display: inline-flex;
    align-items: center;
    gap: 0.28rem;
    color: var(--vigie-ko);
    border-style: dashed;
    border-color: color-mix(in srgb, var(--vigie-ko) 55%, transparent);
    background: color-mix(in srgb, var(--vigie-ko) var(--vigie-plaque), transparent);
}

.vigie-chip-restreint .vigie-badge-ico {
    font-size: 0.85rem !important;
    width: 0.85rem;
    height: 0.85rem;
    color: inherit;
}

/* Même teinte pour un BADGE : le sha attendu quand la prod ne le sert pas. Deux
   règles plutôt qu'un sélecteur groupé parce que le badge porte une icône et un
   corps de texte différents — seule la teinte est commune. */
.vigie-badge-attention {
    color: var(--vigie-attention);
    border-color: color-mix(in srgb, var(--vigie-attention) 45%, transparent);
    background: color-mix(in srgb, var(--vigie-attention) var(--vigie-plaque), transparent);
}

.vigie-badge-attention .vigie-badge-ico {
    color: inherit;
}

/* --- Instruments (cadrans, radar, jauges segmentées, diodes) ----------------- */

/* Grammaire commune des quatre instruments : une couleur d'état portée par une
   seule classe, que tous les traits SVG consomment via currentColor. Un cadran
   ne connaît donc jamais ses teintes — il hérite du niveau qu'on lui passe, et
   les seuils restent dans EvaluationKpi. */
.vigie-instrument-ok {
    color: var(--vigie-ok);
}

.vigie-instrument-attention {
    color: var(--vigie-attention);
}

/* Deux vocabulaires cohabitent dans l'app et les instruments parlent les deux
   plutôt que d'imposer une traduction en C# : les KPI disent ok / attention /
   ko / inconnu (EvaluationKpi), les états de collecteurs disent ok / attention
   / critique / attente (CatalogueIncidents.ClasseNiveau). Mêmes teintes de part
   et d'autre — une famille critique et un KPI rouge sont le même rouge. */
.vigie-instrument-ko,
.vigie-instrument-critique {
    color: var(--vigie-ko);
}

.vigie-instrument-inconnu,
.vigie-instrument-attente {
    color: var(--vigie-texte-3);
}

/* « neutre » : une mesure de VOLUME (visiteurs, pages vues) qu'aucun seuil ne
   juge. Elle prend l'accent, pas le vert — un vert dit « bon », et personne n'a
   décidé qu'un nombre de visiteurs était bon. */
.vigie-instrument-neutre {
    color: var(--vigie-accent);
}

/* --- Cadran en arc ---------------------------------------------------------- */

/* Largeur FIXE et non « 100 % avec un max » : dans une rangée flex, une base de
   100 % fait prendre toute la ligne au premier cadran et renvoie les suivants
   au rang d'en dessous — en 380 px on obtenait un cadran par ligne, et le
   pupitre chassait les cartes de la flotte hors de l'écran. */
.vigie-cadran {
    display: block;
    flex: 0 1 auto;
    width: 8.6rem;
    max-width: 100%;
}

.vigie-cadran-svg {
    display: block;
    width: 100%;
    height: auto;
    overflow: visible;
}

/* La piste est un creux : elle prend la teinte de la bordure, pas celle de
   l'état — sinon l'arc « vide » se lirait comme de l'arc rempli en pâle. */
.vigie-cadran-piste {
    stroke: var(--vigie-bordure-forte);
    stroke-width: 5;
    stroke-linecap: butt;
}

.vigie-cadran-remplissage {
    stroke: currentColor;
    stroke-width: 5;
    stroke-linecap: butt;
}

.vigie-cadran-graduation {
    stroke: var(--vigie-bordure-forte);
    stroke-width: 0.9;
}

.vigie-cadran-graduation-majeure {
    stroke: var(--vigie-texte-3);
    stroke-width: 1.3;
}

.vigie-cadran-index {
    stroke: currentColor;
    stroke-width: 2.2;
    stroke-linecap: round;
}

/* Aiguille physique : pivote au moyeu, amortie par une transition qui DÉPASSE
   légèrement la cible avant d'y revenir (cubic-bezier à rebond) — le geste d'une
   aiguille lestée. La transform tourne autour de (50, 52) dans le repère viewBox. */
.vigie-cadran-aiguille {
    stroke: currentColor;
    stroke-width: 1.6;
    stroke-linecap: round;
    transform-box: view-box;
    transform-origin: 50px 52px;
    transition: transform 0.9s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.vigie-cadran-moyeu {
    fill: var(--vigie-surface-opaque);
    stroke: currentColor;
    stroke-width: 1.4;
}

/* La valeur reste un texte SVG (elle porte l'identifiant de test et se lit au
   lecteur d'écran), mais TRANSPARENTE : c'est l'afficheur 7-segments qui est vu.
   Le texte n'est là que pour la mesure et l'accessibilité. */
.vigie-cadran-valeur {
    font-family: var(--vigie-police-mono);
    font-size: 15px;
    fill: transparent;
    dominant-baseline: middle;
}

/* Sans signal : le voyant éteint et son étiquette « NO SIG », en teinte sourde
   — surtout pas la couleur d'état, un instrument muet ne juge rien. */
.vigie-cadran-nosig {
    font-family: var(--vigie-police-mono);
    font-size: 8px;
    letter-spacing: 1.6px;
    fill: var(--vigie-texte-3);
    dominant-baseline: middle;
}

.vigie-cadran-nosig-lampe {
    fill: none;
    stroke: var(--vigie-bordure-forte);
    stroke-width: 1.2;
}

/* Afficheur 7-segments maison : segments allumés en pleine teinte d'état,
   segments éteints à peine visibles — le grain d'un vrai cadran LCD. */
.vigie-sept-on {
    stroke: currentColor;
    stroke-width: 1.9;
    stroke-linecap: round;
}

.vigie-sept-off {
    stroke: currentColor;
    stroke-width: 1.9;
    stroke-linecap: round;
    opacity: 0.09;
}

.vigie-sept-point {
    stroke: none;
    fill: currentColor;
}

.vigie-sept-fallback {
    font-family: var(--vigie-police-mono);
    font-size: 7px;
    font-weight: 600;
    fill: currentColor;
    text-anchor: start;
}

.vigie-cadran-libelle {
    font-family: var(--vigie-police-mono);
    font-size: 7.5px;
    letter-spacing: 1.4px;
    text-transform: uppercase;
    fill: var(--vigie-texte-3);
}

/* --- Radar ------------------------------------------------------------------ */

.vigie-radar {
    /* Durée d'un tour de balayage : partagée par la ligne qui tourne ET par la
       rémanence des échos, pour qu'un écho s'allume PILE quand le faisceau
       l'atteint. Changer cette valeur garde tout synchrone. */
    --vigie-radar-tour: 6s;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.9rem 1.4rem;
}

/* Cartouche d'en-tête biseauté : « VEILLE — SECTEUR 04 · N CONTACTS ». */
.vigie-radar-cartouche {
    display: flex;
    align-items: baseline;
    justify-content: center;
    gap: 0.5rem;
    flex: 1 1 100%;
    padding: 0.2rem 0.7rem;
    font-family: var(--vigie-police-mono);
    font-size: 0.62rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--vigie-accent);
    background: color-mix(in srgb, var(--vigie-accent) var(--vigie-plaque), transparent);
    border: 1px solid color-mix(in srgb, var(--vigie-accent) 32%, transparent);
    clip-path: polygon(0.5rem 0, 100% 0, 100% calc(100% - 0.5rem), calc(100% - 0.5rem) 100%, 0 100%, 0 0.5rem);
}

/* LE COMPTE DE CONTACTS. C'était la seule paire du dépôt qui échouait vraiment
   AA (4,11:1) : de l'atone posé sur une plaque teintée à l'accent, laquelle
   rapproche justement le fond du texte. Le relèvement de la rampe de gris le
   ramène à 4,97:1, mais en 9,5 px l'atone reste le mauvais rôle — ce « 5 » est
   une VALEUR, pas du méta-texte, et c'est la seule information chiffrée du
   cartouche. Il passe au secondaire (6,41:1) et reste sous le libellé de
   secteur, qui est en accent : la hiérarchie du cartouche ne bouge pas. */
.vigie-radar-cartouche-compte {
    color: var(--vigie-texte-2);
}

.vigie-radar-cartouche-compte::before {
    content: "· ";
}

.vigie-radar-svg {
    display: block;
    flex: 0 0 auto;
    width: clamp(8rem, 17vw, 11.5rem);
    height: auto;
    overflow: visible;
}

.vigie-radar-cercle {
    stroke: var(--vigie-grille);
    stroke-width: 0.7;
}

.vigie-radar-reticule {
    stroke: var(--vigie-grille);
    stroke-width: 0.7;
}

.vigie-radar-reticule-faible {
    stroke-dasharray: 2 3;
}

.vigie-radar-cran {
    stroke: var(--vigie-bordure-forte);
    stroke-width: 0.8;
}

.vigie-radar-cran-majeur {
    stroke: var(--vigie-texte-3);
    stroke-width: 1.1;
}

/* Graduations d'azimut sur l'anneau extérieur, en micro-monospace. */
.vigie-radar-azimut {
    font-family: var(--vigie-police-mono);
    font-size: 3px;
    letter-spacing: 0.2px;
    fill: var(--vigie-texte-3);
    text-anchor: middle;
}

/* Le secteur de balayage : le dégradé va de l'accent au transparent EN TRAÎNÉE
   derrière la ligne d'attaque, et c'est le GROUPE qui tourne — une seule
   transform composée, jamais un re-tracé. */
.vigie-radar-stop-eteint {
    stop-color: var(--vigie-accent);
    stop-opacity: 0;
}

.vigie-radar-stop-milieu {
    stop-color: var(--vigie-accent);
    stop-opacity: 0.08;
}

.vigie-radar-stop-vif {
    stop-color: var(--vigie-accent);
    stop-opacity: 0.28;
}

.vigie-radar-ligne {
    stroke: var(--vigie-accent);
    stroke-width: 0.9;
    opacity: 0.7;
}

.vigie-radar-balayage {
    transform-box: view-box;
    transform-origin: 50% 50%;
    animation: vigie-radar-tour var(--vigie-radar-tour) linear infinite;
}

@keyframes vigie-radar-tour {
    from {
        transform: rotate(0deg);
    }

    to {
        transform: rotate(360deg);
    }
}

/* Réticule central et croix de visée : le cœur de l'écran. */
.vigie-radar-visee-cercle {
    stroke: color-mix(in srgb, var(--vigie-accent) 55%, transparent);
    stroke-width: 0.6;
}

.vigie-radar-visee-trait {
    stroke: color-mix(in srgb, var(--vigie-accent) 65%, transparent);
    stroke-width: 0.7;
}

.vigie-radar-visee-point {
    fill: var(--vigie-accent);
}

/* RÉMANENCE : au REPOS l'écho est un point simple, posé, sans pulsation ; au
   passage du faisceau il monte BRIÈVEMENT en luminosité puis redescend en
   douceur, façon tube à phosphore. L'amplitude est volontairement faible
   (0,78 → 1) : un radar de veille signale, il ne clignote pas. Le décalage
   d'animation est PROPORTIONNEL à l'angle réel de l'écho (--vigie-echo-phase,
   posée en style inline par le composant) : un écho à 90° s'allume au quart du
   tour. Délai négatif = déjà en régime au chargement, pas de temps mort. */
.vigie-radar-echo {
    animation: vigie-radar-remanence var(--vigie-radar-tour) linear infinite;
    animation-delay: calc(var(--vigie-radar-tour) * (var(--vigie-echo-phase, 0) - 1));
}

@keyframes vigie-radar-remanence {
    0%,
    5% {
        opacity: 1;
    }

    40%,
    100% {
        opacity: 0.78;
    }
}

.vigie-radar-point {
    fill: currentColor;
}

/* Ligne de rappel : le segment radial qui relie un écho à son étiquette posée
   sur la couronne. Fine et sourde — elle sert l'appariement, elle ne doit pas
   se lire comme une donnée de plus sur l'écran. */
.vigie-radar-rappel {
    stroke: currentColor;
    stroke-width: 0.45;
    stroke-dasharray: 1.6 1.6;
    opacity: 0.42;
}

/* Halo d'écho : ÉTEINT au repos (opacité 0), il n'existe qu'à l'instant où le
   faisceau passe — une brève bouffée de phosphore, petite, qui retombe en un
   sixième de tour. Un halo permanent faisait de chaque écho une alarme. */
.vigie-radar-halo {
    fill: currentColor;
    opacity: 0;
    transform-box: fill-box;
    transform-origin: center;
    animation: vigie-radar-flare var(--vigie-radar-tour) linear infinite;
    animation-delay: calc(var(--vigie-radar-tour) * (var(--vigie-echo-phase, 0) - 1));
}

@keyframes vigie-radar-flare {
    0% {
        transform: scale(1.2);
        opacity: 0.4;
    }

    8% {
        transform: scale(1);
        opacity: 0.22;
    }

    18%,
    100% {
        transform: scale(0.9);
        opacity: 0;
    }
}

/* Étiquettes d'écho : elles doivent rester lisibles PAR-DESSUS la grille, le
   faisceau et les cercles de portée, sans jamais être tronquées. D'où le liseré
   de la couleur du panneau tracé SOUS le glyphe (paint-order) : c'est le
   contour d'un cartouche gravé, pas un effet — sans lui, un libellé posé sur
   une graduation devient un enchevêtrement de traits. */
.vigie-radar-etiquette {
    font-family: var(--vigie-police-mono);
    font-size: 5.4px;
    letter-spacing: 0.4px;
    fill: var(--vigie-texte);
    text-anchor: middle;
    dominant-baseline: central;
    paint-order: stroke fill;
    stroke: var(--vigie-surface-opaque);
    stroke-width: 1.5px;
    stroke-linejoin: round;
}

.vigie-radar-legende {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    min-width: 0;
    flex: 1 1 11rem;
}

.vigie-radar-legende li {
    display: flex;
    align-items: baseline;
    gap: 0.45rem;
    font-family: var(--vigie-police-mono);
    font-size: 0.76rem;
    min-width: 0;
}

.vigie-radar-diode {
    flex: none;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: currentColor;
    align-self: center;
}

.vigie-radar-legende-code {
    color: var(--vigie-texte);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    min-width: 2.4rem;
}

.vigie-radar-legende-etat {
    color: var(--vigie-texte-3);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* --- Jauge segmentée -------------------------------------------------------- */

.vigie-barre-seg {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    min-width: 7rem;
}

.vigie-barre-seg-tete {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.6rem;
}

.vigie-barre-seg-valeur {
    font-family: var(--vigie-police-mono);
    font-size: 1.05rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: currentColor;
    line-height: 1.1;
}

/* Hauteur FIXE et preserveAspectRatio="none" : les segments s'étirent tous du
   même facteur en largeur, la rangée garde 14 px de haut quelle que soit la
   colonne qui la porte. */
.vigie-barre-seg-svg {
    display: block;
    width: 100%;
    height: 14px;
}

.vigie-seg-eteint {
    fill: var(--vigie-bordure-forte);
    opacity: 0.55;
}

.vigie-seg-allume {
    fill: currentColor;
    opacity: 0.72;
}

/* Front de charge : le dernier segment allumé, à pleine opacité — on lit la
   position sans compter les diodes. */
.vigie-seg-front {
    fill: currentColor;
}

/* --- Bande de jours (barcode de disponibilité) ------------------------------- */

/* Une barre par journée, la plus ancienne à gauche. Même construction que la
   jauge segmentée — SVG en preserveAspectRatio="none", hauteur fixée ici — pour
   que trente barres s'étirent toutes du même facteur : à un pixel près d'écart,
   l'œil lirait un signal là où il n'y a qu'un arrondi. */
.vigie-bande-jours {
    display: flex;
    flex-direction: column;
    gap: 0.28rem;
    min-width: 10rem;
    flex: 1 1 12rem;
}

.vigie-bande-jours-svg {
    display: block;
    width: 100%;
    height: 26px;
}

/* La teinte vient du niveau porté par CHAQUE barre (vigie-instrument-*), pas de
   l'instrument entier : c'est la seule différence de principe avec la jauge
   segmentée, et c'est tout l'objet de la bande — trente états, pas un.

   AUCUNE RÈGLE `fill` ICI, ET C'EST DÉLIBÉRÉ. En SVG, la propriété CSS l'emporte
   sur l'attribut de présentation homonyme : un `fill` posé sur cette classe
   effaçait sans un mot le motif de hachure des journées muettes, qui devenaient
   des barres invisibles. Le remplissage est donc écrit dans le markup, une seule
   fois, pour les deux cas (voir BandeJours.razor). */
.vigie-bande-jour {
    opacity: 0.85;
}

/* Une journée SANS relevé n'est pas une journée à zéro. Elle ne prend donc ni le
   vert ni le rouge mais la teinte « inconnu », doublée d'une hachure : le glyphe
   sépare, la couleur ne fait que confirmer (même règle que Rien). Pleine opacité
   parce qu'une hachure est déjà, par construction, à moitié vide. */
.vigie-bande-jour.vigie-instrument-inconnu {
    opacity: 1;
}

/* Traits de la hachure : --vigie-texte-3 et non --vigie-bordure-forte, et c'est
   MESURÉ à l'APCA — la bordure ne donnait que Lc 33,5 sur la surface en thème
   clair, sous le plancher d'un élément graphique porteur de sens. Un trait plus
   fin et plus sombre se lit mieux qu'un trait épais et pâle.
   Même encre et même pas dans .vigie-vis-trou : un jour non collecté se dessine
   pareil qu'il s'agisse de disponibilité ou de visiteurs. */
.vigie-bande-jours-hachure {
    stroke: var(--vigie-texte-3);
    stroke-width: 1.4;
}

.vigie-bande-jours-axe {
    display: flex;
    justify-content: space-between;
    gap: 0.5rem;
    font-family: var(--vigie-police-mono);
    font-size: 0.6rem;
    color: var(--vigie-texte-3);
    text-transform: uppercase;
    letter-spacing: 0.08em;
}

/* --- Lampe d'état ------------------------------------------------------------ */

/* Le voyant unique d'une page de site : dôme de verre serti dans sa collerette,
   libellé gravé dessous. La couleur vient du niveau, la lumière du thème. */
.vigie-lampe {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.25rem;
    flex: none;
}

.vigie-lampe-svg {
    display: block;
    width: 2.9rem;
    height: 2.9rem;
    overflow: visible;
}

.vigie-lampe-collerette {
    fill: var(--vigie-surface-2);
    stroke: var(--vigie-coque-arete);
    stroke-width: 1.5;
}

/* Halo interne : la lumière qui déborde du dôme dans la collerette. */
.vigie-lampe-halo {
    fill: currentColor;
    opacity: 0.16;
}

.vigie-lampe-dome {
    fill: currentColor;
}

.vigie-lampe-reflet {
    fill: #fff;
    opacity: 0.42;
}

.vigie-lampe-libelle {
    font-family: var(--vigie-police-mono);
    font-size: 0.68rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: currentColor;
    text-align: center;
}

/* --- Plaque gravée : LA grammaire des titres de section ---------------------- */

/* Les titres de section sont des PLAQUES : lettrage mono majuscule espacé,
   encoche d'accent à gauche, et un filet qui court jusqu'au bord droit comme sur
   un schéma technique. C'est l'élément d'ambiance le plus rentable de la
   refonte — il ne coûte rien, il ne cache aucune donnée, et il signe chaque page
   sans qu'on ait à y ajouter le moindre ornement.

   AUDIT DU LOT « GRAMMAIRE » : ce bloc avait un JUMEAU 1 100 lignes plus bas
   (« Titre de section façon en-tête de panneau d'IDE »), même sélecteur à un
   chevron près, donc même spécificité — c'est le second, écrit plus tard, qui
   gagnait la moitié des propriétés. Les h2 de section se rendaient en graisse
   500 / texte-3 / 0,14 em, les <h2 class="vigie-plaque"> écrits à la main en
   600 / texte-2 / 0,18 em, et personne n'avait choisi ça. Le jumeau est
   supprimé, il ne reste que cette plaque.

   Sélecteur DESCENDANT et non direct : depuis Cartouche, le h2 est enveloppé
   dans la tête de section — un `>` ne l'atteindrait plus. */
.vigie-plaque,
.vigie-section h2 {
    display: flex;
    align-items: center;
    gap: var(--vigie-pas-1);
    margin: 0 0 var(--vigie-pas-2);
    font-family: var(--vigie-police-mono);
    font-size: 0.74rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.18em;
    color: var(--vigie-texte-2);
}

/* L'encoche : un tiret d'accent biseauté, la « tête » de la plaque. */
.vigie-plaque::before,
.vigie-section h2::before {
    content: "";
    flex: none;
    width: 11px;
    height: 11px;
    background: var(--vigie-accent);
    /* Coin biseauté en bas à droite : le cartouche d'angle des consoles
       impériales, obtenu sans image ni pseudo-élément supplémentaire. */
    clip-path: polygon(0 0, 100% 0, 100% 62%, 62% 100%, 0 100%);
}

/* Le filet : il file jusqu'au bord droit et s'éteint, comme une cote de plan. */
.vigie-plaque::after,
.vigie-section h2::after {
    content: "";
    flex: 1 1 auto;
    height: 1px;
    min-width: 1.5rem;
    background: linear-gradient(90deg, var(--vigie-bordure-forte), transparent);
}

/* Sous-titre de bloc : le cran EN DESSOUS de la plaque, à l'intérieur d'une
   section (le nom d'une famille surveillée, d'une règle d'alerting, d'un
   classement d'audience, d'un sous-bloc de configuration).
   Il existait en QUATRE exemplaires — .vigie-classement-titre, .vigie-famille-titre,
   .vigie-regle-titre, .vigie-config-sous-titre — à 0,94 / 1,02 / 0,98 / 0,96 rem.
   Quatre tailles pour un seul rôle : personne ne peut lire une hiérarchie
   là-dedans, et personne n'a jamais voulu ces quatre valeurs. */
.vigie-sous-titre {
    margin: 0;
    font-size: 0.96rem;
    font-weight: 600;
    letter-spacing: -0.01em;
    min-width: 0;
}

/* Un sous-titre qui COIFFE le contenu d'un bloc de configuration a besoin de
   respirer sous lui ; le même sous-titre dans une tête de carte n'a rien
   dessous. La marge appartient donc au contexte, pas au titre.

   LOT 11 — il devient aussi une LÉGENDE DE FACE-PLATE : mono, capitales
   espacées, atone, comme les libellés d'instrument (.vigie-stat-libelle). À
   0,96 rem et en gras il pesait le même poids que le titre de section
   au-dessus, et deux titres de même poids l'un sous l'autre ne se lisent pas
   comme une hiérarchie : « Seuils de surveillance » puis « Cadences des
   collecteurs » se disputaient le premier regard. Le filet sous la légende est
   la réglette du module. */
.vigie-config-bloc > .vigie-sous-titre {
    margin-bottom: var(--vigie-pas-2);
    padding-bottom: 0.3rem;
    border-bottom: 1px solid var(--vigie-bordure);
    font-family: var(--vigie-police-mono);
    font-size: 0.7rem;
    font-weight: 500;
    color: var(--vigie-texte-3);
    text-transform: uppercase;
    letter-spacing: 0.14em;
}

/* --- Cartouche : la tête d'une section (titre, sous-titre, actions) ---------- */

.vigie-cartouche-tete {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--vigie-pas-2);
    margin-bottom: var(--vigie-pas-2);
}

/* Une zone d'actions VIDE ne réserve pas sa gouttière : la section « heure par
   heure » n'offre aucun choix de pas sur une journée, et un conteneur vide y
   décalerait le filet du titre sans rien porter. */
.vigie-cartouche-actions:empty {
    display: none;
}

/* La plaque garde sa marge propre quand elle est SEULE ; sous un sous-titre,
   c'est la tête qui espace, et un doublon de marge décrocherait le filet. */
.vigie-cartouche-tete h2 {
    margin-bottom: 0;
}

.vigie-cartouche-intitule p {
    margin: 0.25rem 0 0;
    max-width: 68ch;
}

/* --- Case vide : les trois causes (composant Rien) --------------------------- */

/* Le raisonnement est dans CauseVide.cs ; ici on ne fait que le rendre visible.
   Ce qui compte : les trois états se distinguent SANS la couleur (glyphe et
   soulignement diffèrent), parce qu'un daltonien doit lire la même chose, et
   qu'un « ∅ » à côté d'un « 0 » ne se confond avec rien. */
.vigie-rien {
    display: inline-flex;
    align-items: baseline;
    cursor: help;
}

.vigie-rien-marque {
    font-variant-numeric: tabular-nums;
}

/* Non configuré : atone et muet. Rien à attendre, rien à surveiller — l'œil
   doit pouvoir glisser dessus. */
.vigie-rien-non-configure {
    color: var(--vigie-texte-3);
}

/* Pas encore collecté : un cran plus présent, et SOULIGNÉ EN TIRETS — c'est un
   trou de supervision, il doit accrocher légèrement l'œil sans jamais prendre
   la couleur d'une alarme. Un ambre ici crierait au loup sur un collecteur qui
   n'a simplement pas encore tourné. */
.vigie-rien-pas-collecte {
    color: var(--vigie-texte-2);
    text-decoration: underline dashed color-mix(in srgb, var(--vigie-texte-3) 60%, transparent);
    text-underline-offset: 3px;
}

/* Zéro mesuré : ce n'est PAS une absence. Pleine couleur de texte, le chiffre
   tel quel, et un pointillé discret pour signaler qu'une explication existe. */
.vigie-rien-zero {
    color: inherit;
    text-decoration: underline dotted color-mix(in srgb, var(--vigie-texte-3) 45%, transparent);
    text-underline-offset: 3px;
}

/* --- Avarie : « cette zone est hors service » -------------------------------- */

/* Une seule convention pour dire qu'une zone ne peut pas faire son travail :
   liseré rouge à gauche, teinte de fond très légère, bordure assortie. Même
   recette que .vigie-badge-attention (qui dit la même chose en ambre) — d'où une
   parenté lisible entre « ça dérive » et « c'est en panne ».
   À ne PAS employer pour une absence de configuration : ne pas être branché
   n'est pas une panne, et c'est justement ce que Rien sépare. */
.vigie-avarie {
    border-color: color-mix(in srgb, var(--vigie-ko) 45%, transparent) !important;
    border-left: 3px solid var(--vigie-ko) !important;
    background-color: color-mix(in srgb, var(--vigie-ko) 6%, var(--vigie-surface-opaque)) !important;
}

/* --- Message encadré (information, avertissement, avarie) ------------------- */

/* Trois classes ne faisaient que ça : .vigie-analytics-alerte (1,2 rem),
   .vigie-logs-alerte (0,8 rem) et .vigie-config-alerte (0,7 rem). Même rôle,
   trois marges tirées au sort. */
.vigie-message {
    margin: var(--vigie-pas-2) 0;
    border-radius: var(--vigie-rayon);
}

/* --- Bande de veille (page site) --------------------------------------------- */

.vigie-veille {
    padding: 0.9rem 1.2rem;
    margin-bottom: 1.4rem;
}

/* Le corps du bloc « Veille » de la vue site : le radar À GAUCHE (largeur bornée,
   il ne s'étale plus sur toute la ligne) et le mini-journal du site À DROITE, en
   pendant — c'est ce qui comblait le vide des deux tiers droits. Grille à deux
   colonnes, comme le poste de commandement de la flotte, pour harmoniser les deux. */
/* HAUTEUR BORNÉE, et c'est le JOURNAL qu'elle borne — pas le bloc. La contrainte
   du lot 8 est intacte, seule sa formulation change : sans plafond, la console
   dictait la hauteur par son NOMBRE DE LIGNES (822 px de dalle pour 38
   événements, 942 px de bloc), le radar suivait en s'étirant, et la latence, les
   alertes et les runs passaient tous sous la ligne de flottaison d'un écran de
   1000 px. Une page de site dont on ne voit que l'ambiance inverse la hiérarchie
   que tout le lot cherche à poser. Le journal n'y perd rien : il défile.

   POURQUOI UNE RANGÉE « minmax » ET PLUS UNE HAUTEUR FIGÉE. Une découpe fixe
   marchait sur la flotte, où le radar est SEUL dans sa colonne. Ici il traîne sa
   LÉGENDE — quatre lignes, et c'est elle qui porte la donnée, l'écran n'en est
   que la mise en scène. Écran carré + légende ne tenaient pas dans les 260 px
   découpés : avec un `justify-content: center` par-dessus, le trop-plein
   ressortait des DEUX côtés, l'écran par-dessus le hero et la légende dans le
   cartouche de latence en dessous. Le plancher garde donc le bloc bas, mais
   c'est le contenu qui a le dernier mot quand il déborde.

   PLANCHER ET COLONNE EN REM, PAS EN PIXELS : la légende est du texte, elle
   grandit avec la police du navigateur. Bornée en pixels, elle s'ellipsait chez
   qui lit en 18 px alors qu'elle tenait en 16 — un défaut invisible au banc. En
   rem, le rapport colonne/texte ne bouge plus d'une police à l'autre.

   LES 21 REM DE COLONNE SONT MESURÉS, pas choisis : le plus long état que le
   composant sache produire (« en dérive sur le dernier CI vert ») demande 21 rem
   une fois la pastille, le code et les gouttières déduits. Une rem de moins et
   il passe à la ligne — ce qui reste lisible, mais pour trente pixels pris à un
   journal qui en a plus de mille, autant qu'il tienne. */
.vigie-veille-corps {
    /* Le plancher du bloc, et le plafond de la console : une seule valeur pour
       les deux, sinon le journal repousse le plancher qu'il est censé respecter. */
    --vigie-h-veille: 17rem;
    display: grid;
    grid-template-columns: minmax(14rem, 21rem) minmax(0, 1fr);
    grid-template-rows: minmax(var(--vigie-h-veille), auto);
    gap: 1rem 1.6rem;
    align-items: stretch;
    margin-top: 0.6rem;
}

.vigie-veille-radar {
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-width: 0;
    min-height: 0;
    border-right: 1px dashed var(--vigie-bordure);
    padding-right: 1.6rem;
}

/* On force une PILE verticale sans retour à la ligne, comme au pupitre : la
   grammaire flex-row de .vigie-radar (« écran et légende côte à côte ») ne tient
   pas dans une colonne de 21 rem, et un retour à la ligne DANS un flex n'est pas
   mesuré par la rangée qui le contient — c'est de là que venait le débordement. */
.vigie-veille-radar .vigie-radar {
    width: 100%;
    height: 100%;
    flex-direction: column;
    flex-wrap: nowrap;
    justify-content: center;
    align-items: center;
    gap: 0.6rem;
}

/* L'écran prend CE QUI RESTE une fois la légende servie (width auto + viewBox =
   il reste carré). L'ordre de priorité est là, et il est volontaire : la légende
   est la donnée, l'écran l'illustration — c'est donc l'écran qui cède.

   LE PLAFOND N'EST PAS DÉCORATIF. Sur un `<svg>` extérieur, `width: auto` vaut
   100 % (SVG2) : sans max-height, l'écran prend toute la largeur de la colonne,
   son ratio 1:1 en fait une hauteur, et c'est LUI qui redicte la hauteur de la
   rangée — 465 px de bloc au lieu de 354, mesurés. */
.vigie-veille-radar .vigie-radar-svg {
    flex: 1 1 auto;
    width: auto;
    height: 100%;
    min-height: 7rem;
    max-height: 10rem;
    max-width: 100%;
}

/* Base auto : le « flex: 1 1 11rem » de la légende est une LARGEUR en rangée, il
   deviendrait une hauteur imposée dans une pile verticale. */
.vigie-veille-radar .vigie-radar-legende {
    flex: 0 0 auto;
    width: 100%;
}

/* Sous l'écran, l'état passe à la ligne au lieu de s'ellipser. L'ellipse a un
   sens quand la légende est une COLONNE à côté du radar et que sa largeur est un
   choix ; ici elle est en pleine largeur et rogner « alignée sur le dernier CI
   vert » à « alignée sur le dernier CI … » ne fait gagner aucune place — ça perd
   juste le verdict, qui est le seul mot utile de la ligne. */
.vigie-veille-radar .vigie-radar-legende-etat {
    overflow: visible;
    white-space: normal;
    text-overflow: clip;
}

.vigie-veille-journal {
    display: flex;
    min-width: 0;
    min-height: 0;
}

/* Le plafond du lot 8, désormais posé là où il agit vraiment. La console suit la
   rangée (height 100 %) et défile dedans ; son max-height l'empêche de peser sur
   le calcul de cette rangée avec ses soixante lignes. */
.vigie-veille-journal .vigie-journal {
    height: 100%;
    min-height: 0;
    max-height: var(--vigie-h-veille);
}

/* En étroit, le radar et le mini-journal s'empilent — radar d'abord — et le
   filet passe à l'horizontale. Les deux rangées reprennent une hauteur AUTO pour
   le radar (il doit rester carré) et bornée pour la console. */
@media (max-width: 760px) {
    .vigie-veille-corps {
        grid-template-columns: minmax(0, 1fr);
        grid-template-rows: auto 220px;
        gap: 0.8rem;
    }

    .vigie-veille-radar {
        border-right: none;
        padding-right: 0;
        padding-bottom: 0.8rem;
        border-bottom: 1px dashed var(--vigie-bordure);
        align-items: center;
    }

    /* Empilé, l'écran n'a plus de rangée à remplir : c'est LUI qui donne sa
       hauteur, et le plafond de la vue large réduirait sa boîte à 255 × 170. Le
       dessin resterait carré (preserveAspectRatio) mais tomberait à 170 px de
       côté, centré dans une boîte trop large. Il reprend donc sa taille propre. */
    .vigie-veille-radar .vigie-radar-svg {
        flex: 0 0 auto;
        width: min(15rem, 100%);
        height: auto;
        max-height: none;
    }
}

/* Bandeau d'instruments : la rangée de cadrans d'un panneau de console. */
.vigie-instruments {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 1.1rem 1.6rem;
}

/* --- Poste de commandement (vue flotte) ------------------------------------- */

/* Une BANDE en tête, pas un second tableau de bord : deux moitiés — le radar
   analogique à GAUCHE (ambiance + vue d'ensemble), le journal de bord à DROITE
   (l'HISTOIRE de la flotte). Les cartes juste dessous restent l'information
   principale. Grille à deux colonnes : le radar cale sa largeur, le journal
   prend le reste ; align-items: stretch met les deux à la même hauteur.

   HAUTEUR : la bande reste BORNÉE, mais elle a été REMONTÉE (220 px contre 140)
   parce qu'à 140 la dalle ne montrait que cinq lignes — trop peu pour qu'un
   registre se lise comme un registre. Le critère n'a pas changé de nature, il
   reste mesurable : à 1600 px de large sur un écran de 900 px de haut, la
   PREMIÈRE RANGÉE de cartes doit tenir sous la bande sans défilement (mesuré :
   son bas affleure ~885 px, la marge est courte et c'est voulu — chaque pixel
   rendu à la bande est une ligne de journal en moins). Un poste qui repousse
   le parc hors de l'écran inverserait la hiérarchie de la page : l'ambiance
   passerait devant l'information. Le journal, lui, défile de toute façon. */
.vigie-poste {
    /* LA valeur qui pilote la bande : une hauteur de rangée IMPOSÉE. Sans elle,
       la console dicterait la hauteur par son contenu (autant de lignes que le
       journal en a) et le radar suivrait — c'est ce qui repoussait les cartes
       hors de l'écran. Ici c'est l'inverse : la bande fixe sa hauteur, le radar
       s'y adapte et la console défile dedans. À 220 px, la dalle affiche ~9-10
       lignes de flux (18,7 px la ligne) contre 5 à l'ancienne valeur. */
    --vigie-h-poste: 220px;
    display: grid;
    grid-template-columns: minmax(140px, 190px) minmax(0, 1fr);
    grid-template-rows: var(--vigie-h-poste);
    gap: 0.5rem 1.1rem;
    align-items: stretch;
    padding: 0.5rem 0.9rem;
    margin-bottom: 0.9rem;
}

/* Moitié gauche : le radar, centré, cartouche au-dessus. */
.vigie-poste-radar {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    min-width: 0;
    min-height: 0;
    border-right: 1px dashed var(--vigie-bordure);
    padding-right: 1.1rem;
}

/* On force une PILE verticale sans retour à la ligne : la grammaire flex-row de
   .vigie-radar (pensée pour « svg + légende côte à côte ») ferait sinon passer
   le SVG dans une seconde colonne, à droite, par-dessus le journal. */
.vigie-poste-radar .vigie-radar {
    width: 100%;
    height: 100%;
    flex-direction: column;
    flex-wrap: nowrap;
    align-items: center;
    gap: 0.3rem;
}

/* Le cartouche reprend une base auto : son « flex: 1 1 100% » (largeur pleine en
   rangée) deviendrait une HAUTEUR pleine dans une pile verticale. Compacté avec
   la bande : c'est une plaque gravée, elle n'a pas besoin d'air. */
.vigie-poste-radar .vigie-radar-cartouche {
    flex: 0 0 auto;
    width: 100%;
    padding: 0.1rem 0.4rem;
    font-size: 0.56rem;
    /* Le libellé passe sur deux lignes dans une colonne aussi étroite : c'est
       voulu (une plaque gravée tient sur deux lignes), mais l'interligne par
       défaut lui ferait manger dix pixels de plus que le radar sous elle. */
    line-height: 1.2;
}

/* L'écran se cale sur la HAUTEUR disponible et reste carré (width auto + le
   viewBox) : c'est la bande qui commande, pas l'inverse. */
.vigie-poste-radar .vigie-radar-svg {
    flex: 1 1 auto;
    width: auto;
    height: 100%;
    min-height: 0;
    max-width: 100%;
}

/* Sous ~120 px, les graduations d'azimut font 3,5 px de haut : ce n'est plus du
   texte, c'est du grain. On les retire ici (la couronne de crans reste, et
   c'est elle qui fait l'instrument) et on regrossit d'autant les étiquettes
   d'écho, qui portent, elles, un code de site. */
.vigie-poste-radar .vigie-radar-azimut {
    display: none;
}

.vigie-poste-radar .vigie-radar-etiquette {
    font-size: 7.6px;
    letter-spacing: 0.2px;
    stroke-width: 2px;
}

/* Moitié droite : le journal remplit toute la hauteur de la bande. */
.vigie-poste-journal {
    display: flex;
    min-width: 0;
    min-height: 0;
}

/* Dans la bande, la console se resserre : châssis fin, dalle collée au cadre.
   Elle garde sa police et ses couleurs — on lui retire de l'air, pas de la
   lisibilité. */
.vigie-poste-journal .vigie-journal {
    min-height: 0;
    height: 100%;
    padding: 0.4rem 0.45rem;
    gap: 0.25rem;
}

.vigie-poste-journal .vigie-journal-cartouche {
    padding: 0.1rem 0.4rem;
    font-size: 0.6rem;
}

.vigie-poste-journal .vigie-journal-flux {
    padding: 0.35rem 0.6rem;
    line-height: 1.45;
}

/* En étroit, le filet vertical n'a plus de sens : les deux moitiés s'empilent —
   le radar d'abord, le journal ensuite — et c'est un filet HORIZONTAL qui les
   sépare. Le poste reste une bande d'ambiance, les cartes restent le sujet. */
@media (max-width: 760px) {
    .vigie-poste {
        grid-template-columns: minmax(0, 1fr);
        /* Empilées, les deux moitiés ont chacune leur rangée : la hauteur imposée
           du bureau ne vaut plus, chacune prend la sienne. */
        grid-template-rows: auto auto;
        gap: 0.6rem;
        padding: 0.6rem 0.7rem;
    }

    .vigie-poste-radar {
        border-right: none;
        padding-right: 0;
        padding-bottom: 0.6rem;
        border-bottom: 1px dashed var(--vigie-bordure);
    }

    .vigie-poste-radar .vigie-radar-svg {
        flex: 0 0 auto;
        width: min(150px, 46vw);
        height: auto;
    }

    /* Empilée, la console n'est plus étirée par le radar : sans hauteur imposée
       elle prendrait celle de ses soixante lignes et repousserait les cartes à
       un écran et demi de défilement. Elle garde donc son propre plafond — une
       BORNE MOBILE DISTINCTE de la bande de bureau : 240 px et non 220, parce
       qu'empilée elle n'a plus le radar à côté pour justifier sa hauteur, mais
       pas davantage — à 380 px de large sur un écran de ~700 px, la console ne
       doit pas manger l'écran (240 px en laissent plus de la moitié au radar
       et aux premières cartes). */
    .vigie-poste-journal .vigie-journal {
        height: 240px;
        min-height: 0;
    }
}

/* --- Journal de bord : terminal « console verte » (CRT phosphore) ------------ */

/* Un vrai MONITEUR encastré : un CHÂSSIS biseauté entoure une DALLE en retrait.
   L'écran reste un ÉCRAN dans les deux quarts — texte clair sur dalle sombre.
   La parade au « trou dans la page » n'est PAS de passer en fond clair / texte
   sombre : en polarité inversée, le glow de phosphore (halo sombre sur texte
   sombre) épaissit et délave les glyphes.

   MAIS UNE CONSOLE DE JOUR N'EST PAS NOIRE. La version précédente encadrait
   bien la dalle et s'arrêtait là : elle gardait le quasi-noir de la nuit
   (#1E2430, L* 14) sous un bezel clair, et l'objet restait un puits au milieu
   du parchemin. Un tube regardé en plein jour ne devient pas clair, il devient
   TEINTÉ : la lumière ambiante se dépose sur la dalle, le noir remonte, le
   phosphore se délave. La dalle de jour est donc un graphite VERT-DE-GRIS
   (#38423B, L* 26) — douze points de clarté au-dessus, la teinte du phosphore
   éteint plutôt que celle de l'encre. L'écart avec le bezel reste franc (c'est
   lui qui fait l'instrument), mais l'objet ne troue plus la planche de bord.

   Variables nommées PAR USAGE (patron gruvbox/Radix), aucune couleur brute plus
   bas ; le quart de nuit les redéfinit. */
.vigie-journal {
    /* QUART DE JOUR (base). La dalle remonte, donc les encres redescendent : à
       texte constant, une dalle plus claire perd du contraste. Toutes les paires
       sont recalculées contre #38423B — 8,33:1 pour le texte courant, 5,28:1
       pour l'atone, 5,61:1 au pire des niveaux. */
    --log-fond: #38423b;
    --log-texte: #dce9de;
    --log-attenue: #aabdaf;
    --log-code: #e9f3eb;
    --log-ok: #86e0ac;
    --log-warn: #e6bd68;
    /* Même arbitrage que --vigie-ko, pour la même raison : sur une dalle sombre
       un rouge ne gagne du contraste qu'en montant en luminance, donc en
       rosissant. À #ff9189 l'erreur du journal passait DIX POINTS d'APCA sous
       son propre ambre — l'échelle de gravité se lisait à l'envers dans la
       console comme elle se lisait à l'envers dans les chips avant la PR #57. */
    --log-err: #ffb8b1;
    --log-info: #7ecad8;
    --log-accent: #bbd4ff;

    /* Le bezel : c'est LE point qui fait tout. Épais, liseré interne clair,
       ombre portée douce (objet posé), highlight de bord haut. De jour c'est la
       MÊME COQUE que le reste : le journal est vissé sur la planche de bord, il
       n'est pas posé dessus — donc quand la coque est passée du parchemin à la
       laque immaculée, ce bezel devait suivre, sous peine d'être le seul objet
       chaud d'un écran devenu froid. Il n'a plus aucune dominante colorée. */
    --log-bezel: linear-gradient(158deg, #fafbfc 0%, #e4e7ea 55%, #dce0e4 100%);
    --log-bezel-bord: #c3c9cf;
    --log-bezel-arete: rgba(255, 255, 255, 0.95);
    --log-bezel-liseret: #f4f6f8;
    --log-bezel-ombre: rgba(20, 24, 29, 0.2);
    --log-cartouche: #3f464e;
    --log-cartouche-2: #545c65;

    --log-dalle-bord: #232b26;
    --log-dalle-ombre: rgba(20, 26, 20, 0.5);
    /* Effets CRT abaissés de jour : glow /2, scanlines très faibles, vignettage
       quasi coupé (il alourdit un objet déjà sombre posé sur du clair). */
    --crt-scanline: rgba(0, 0, 0, 0.07);
    --crt-vignette: rgba(18, 26, 20, 0.13);
    --crt-glow-blur: 4px;
    --crt-glow-pct: 30%;

    display: flex;
    flex-direction: column;
    width: 100%;
    min-height: 240px;
    gap: 0.4rem;
    padding: 0.6rem;
    background: var(--log-bezel);
    border: 1px solid var(--log-bezel-bord);
    border-radius: 12px;
    box-shadow:
        0 4px 14px var(--log-bezel-ombre),
        inset 0 1px 0 var(--log-bezel-arete),
        inset 0 0 0 1px var(--log-bezel-liseret);
    position: relative;
}

/* QUART DE NUIT : quasi-noir « tube éteint », bezel sombre fondu dans la coque.
   Glow et vignettage restent FORTS — le contraste ne souffre pas des scanlines
   sur fond très sombre (le texte passe même de 10.5 à ~11:1). */
:root.vigie-sombre .vigie-journal {
    --log-fond: #05070a;
    --log-texte: #35e07a;
    /* Vert « sourd » remonté une seconde fois, de #229356 à #2fae67. À la
       valeur précédente il tenait AA de justesse (5,16:1) mais retombait à
       Lc 35,5 en APCA — et il porte la colonne d'horodatage, les séparateurs de
       jour et le sous-titre du cartouche, donc du texte COURANT en 11 px. Il
       passe à Lc 47,6 / 7,08:1, ce qui le laisse à vingt-quatre points sous le
       texte plein : il reste l'atone de la console, il cesse d'être illisible.
       Le sous-titre du cartouche, lui, était à 3,83:1 sur le bezel — sous AA. */
    --log-attenue: #2fae67;
    --log-code: color-mix(in srgb, #35e07a 82%, white);
    --log-ok: #35e07a;
    /* LES TROIS NIVEAUX DU JOURNAL PARLENT LA LANGUE DU RESTE DE L'APP. Ambre
       et rouge reprennent ici exactement --vigie-attention et --vigie-ko : même
       rôle, même teinte, un seul endroit où se tromper. C'est ce qui redresse
       l'échelle — à #ff6b7a l'erreur tenait Lc 49,4 quand son ambre voisin
       tenait 65,9, seize points d'écart À L'ENVERS. Elle passe devant, de 1,3
       point, exactement comme dans la ventilation du verdict.
       « info » n'est plus l'atone recyclé : une ligne d'information est une
       ligne de registre, elle se lit comme les autres niveaux, pas comme du
       méta-texte. */
    --log-warn: #e0af68;
    --log-err: #ff9eae;
    --log-info: #4fd08d;
    --log-accent: #35e07a;

    /* Bezel de nuit : même neutralisation que la coque. Il descend sous le
       bezel de l'habitacle (#0e1012) parce que le journal est encastré DANS la
       planche de bord, pas vissé dessus. */
    --log-bezel: linear-gradient(158deg, #17191c 0%, #0e1012 55%, #0b0d0f 100%);
    --log-bezel-bord: #08090b;
    --log-bezel-arete: rgba(227, 230, 232, 0.07);
    --log-bezel-liseret: rgba(227, 230, 232, 0.025);
    --log-bezel-ombre: rgba(0, 0, 0, 0.5);
    --log-cartouche: #35e07a;
    --log-cartouche-2: #2fae67;

    --log-dalle-bord: #000;
    --log-dalle-ombre: rgba(0, 0, 0, 0.9);
    --crt-scanline: rgba(0, 0, 0, 0.3);
    --crt-vignette: rgba(0, 0, 0, 0.75);
    --crt-glow-blur: 5px;
    --crt-glow-pct: 52%;
}

/* Cartouche d'en-tête : une plaque gravée sur le châssis, au-dessus de la dalle.
   Sur le bezel (clair en thème clair) : texte sombre, sans glow. */
.vigie-journal-cartouche {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.6rem;
    padding: 0.25rem 0.55rem;
    font-family: var(--vigie-police-mono);
    font-size: 0.66rem;
    letter-spacing: 0.14em;
    color: var(--log-cartouche);
    z-index: 3;
}

.vigie-journal-secteur {
    color: var(--log-cartouche-2);
}

/* La DALLE : l'écran, en RETRAIT dans le châssis. Coins arrondis, ombre interne
   sur tout le pourtour (l'écran est encastré), scanlines et vignettage — le tout
   FIXE au-dessus du flux qui défile. */
.vigie-journal-dalle {
    position: relative;
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    background: var(--log-fond);
    border: 1px solid var(--log-dalle-bord);
    border-radius: 7px;
    overflow: hidden;
    box-shadow:
        inset 0 0 0 1px rgba(255, 255, 255, 0.04),
        inset 0 0 14px 3px var(--log-dalle-ombre);
}

/* Scanlines + vignettage de tube, en overlay au-dessus du texte sans toucher le
   DOM ni défiler avec lui. Intensités pilotées par thème (--crt-*). */
.vigie-journal-dalle::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    background:
        repeating-linear-gradient(0deg, var(--crt-scanline) 0, var(--crt-scanline) 1px, transparent 1px, transparent 3px),
        radial-gradient(130% 100% at 50% 50%, transparent 58%, var(--crt-vignette) 100%);
    border-radius: inherit;
    z-index: 2;
}

/* Le flux : column-reverse cale la vue au bas (la ligne la plus récente) sans
   JavaScript, et l'y maintient quand une entrée arrive — l'auto-défilement du
   terminal est ici une propriété de mise en page, pas du script. Le glow suit
   currentColor (donc la couleur de CHAQUE ligne) via une seule règle héritée. */
.vigie-journal-flux {
    list-style: none;
    margin: 0;
    padding: 0.5rem 0.7rem;
    display: flex;
    flex-direction: column-reverse;
    justify-content: flex-start;
    gap: 0.02rem;
    flex: 1 1 auto;
    min-width: 0;
    min-height: 0;
    overflow-y: auto;
    font-family: var(--vigie-police-mono);
    font-size: 0.76rem;
    line-height: 1.55;
    color: var(--log-texte);
    text-shadow: 0 0 var(--crt-glow-blur) color-mix(in srgb, currentColor var(--crt-glow-pct), transparent);
    scrollbar-width: thin;
    scrollbar-color: var(--log-attenue) transparent;
}

.vigie-journal-ligne {
    display: flex;
    gap: 0.55rem;
    align-items: baseline;
    padding: 0.02rem 0;
    animation: vigie-journal-apparition 420ms ease-out;
}

/* Les trois premières colonnes sont à LARGEUR FIXE, en chiffres tabulaires :
   c'est ce qui fait lire l'écran comme un registre système et non comme une
   liste de phrases. Une colonne qui danse d'une ligne à l'autre suffit à
   détruire l'illusion — et surtout à rendre le flux illisible en diagonale. */
.vigie-journal-horodatage {
    flex: 0 0 6.6rem;
    color: var(--log-attenue);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* Gravité façon syslog : quatre caractères, alignés, dans la couleur de la
   ligne. On ne répète pas le mot entier — « WARN » se reconnaît à sa forme. */
.vigie-journal-niveau {
    flex: 0 0 2.2rem;
    color: currentColor;
    opacity: 0.85;
    letter-spacing: 0.06em;
    white-space: nowrap;
}

/* Le SECTEUR. Un flex-basis ne suffit pas à tenir une colonne : min-width vaut
   auto par défaut, donc un code long pousse la colonne au lieu de la remplir —
   mesuré, « creances » la faisait passer de 44 à 66 px et décalait d'autant la
   colonne du message SUR CETTE LIGNE SEULEMENT. La grille sautait précisément
   sur la ligne la plus bavarde, et le renvoi à la ligne ci-dessous aurait posé
   son renfoncement à un x différent des autres. On élargit donc à la longueur
   réelle des codes (huit lettres, en ch parce que la dalle est monospacée) et
   on clôt la colonne. */
.vigie-journal-code {
    flex: 0 0 9ch;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--log-code);
}

/* LE MESSAGE REVIENT À LA LIGNE au lieu d'être coupé à l'ellipse. Le tronquage
   faisait disparaître l'URL de production en tout premier — donc exactement ce
   qu'on veut lire (et coller) d'une alerte de dérive.
   Le renfoncement ne demande aucune règle : le message est une case de la
   rangée flex, ses lignes suivantes repartent donc du bord GAUCHE DE SA
   COLONNE, jamais sous l'horodatage. C'est la grille qui porte le renvoi.
   break-word et pas anywhere : il ne coupe un mot que si ce mot ne peut PAS
   tenir seul sur une ligne — les URL sont césurées, les mots normaux non. */
.vigie-journal-message {
    min-width: 0;
    overflow-wrap: break-word;
}

/* Séparateur de journée : le repère qui rend un journal long relisible. Deux
   filets qui s'évanouissent de part et d'autre de la date, très sourds — c'est
   une respiration, pas un événement de plus. */
.vigie-journal-jour {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin: 0.28rem 0 0.12rem;
    color: var(--log-attenue);
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.14em;
    text-shadow: none;
}

.vigie-journal-jour::before,
.vigie-journal-jour::after {
    content: "";
    flex: 1 1 auto;
    height: 1px;
    background: currentColor;
    opacity: 0.32;
}

.vigie-journal-jour::before {
    flex: 0 0 1.4rem;
}

/* Les lignes de STATUT en direct sont plus sourdes : ce sont des relevés
   d'ambiance (« BW · dispo 100% · p95 319ms »), pas des événements datés. Leur
   colonne d'horodatage porte « live » au lieu d'une date — même largeur, donc
   même alignement que tout le reste du registre. */
.vigie-journal-statut {
    color: var(--log-attenue);
    text-shadow: none;
}

.vigie-journal-statut .vigie-journal-code,
.vigie-journal-statut .vigie-journal-niveau {
    color: var(--log-attenue);
}

/* Couleurs d'événement : la base, l'ambre alerte, le rouge coupe. Le glow est
   hérité et suit currentColor — chaque ligne rayonne de sa propre teinte. */
.vigie-journal-ok {
    color: var(--log-ok);
}

.vigie-journal-attention {
    color: var(--log-warn);
}

.vigie-journal-ko {
    color: var(--log-err);
}

.vigie-journal-info {
    color: var(--log-info);
    text-shadow: none;
}

/* Ligne d'invite en pied de terminal (bas de l'écran via column-reverse) :
   prompt, horloge, curseur clignotant. C'est le « toujours du texte qui vit ». */
.vigie-journal-invite {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding-top: 0.15rem;
    color: var(--log-texte);
}

.vigie-journal-prompt {
    color: var(--log-accent);
}

.vigie-journal-horloge {
    color: var(--log-attenue);
    font-variant-numeric: tabular-nums;
}

/* Curseur d'invite : un clignotement DOUX et discret — l'opacité varie de 1 à
   0.55 (jamais jusqu'à l'extinction), en fondu lent, pour qu'il ne happe pas
   l'œil. C'est un curseur qui respire, pas une alarme. */
.vigie-journal-curseur {
    width: 0.5rem;
    height: 0.9rem;
    background: var(--log-texte);
    box-shadow: 0 0 var(--crt-glow-blur) color-mix(in srgb, var(--log-texte) 55%, transparent);
    animation: vigie-journal-curseur 1.8s ease-in-out infinite;
}

@keyframes vigie-journal-curseur {
    0%,
    100% {
        opacity: 1;
    }

    50% {
        opacity: 0.55;
    }
}

/* En étroit, l'horodatage complet mangerait la moitié de la ligne et le message
   serait tronqué au troisième mot. On retombe donc sur l'heure seule — les
   séparateurs de jour restent, eux, et suffisent alors à lever l'ambiguïté
   « hier ou aujourd'hui ». La largeur reste FIXE, simplement plus courte. */
@media (max-width: 700px) {
    .vigie-journal-date {
        display: none;
    }

    .vigie-journal-horodatage {
        flex-basis: 3.9rem;
    }

    .vigie-journal-niveau {
        flex-basis: 2rem;
    }
}

/* Plus bas, la rangée unique n'a plus assez de place pour un message. MESURÉ,
   colonnes raccourcies comprises : la colonne du message vaut 34 signes à
   560 px, 24 à 480 et DIX à 380 — le message d'une dérive de version y occupait
   vingt et une lignes de dix caractères, ce qui n'est plus du texte.
   La grille passe donc à DEUX RANGÉES : l'en-tête (heure, gravité, secteur)
   puis le message dessous, renfoncé d'un cran pour rester rattaché à lui. Elle
   ne disparaît pas, elle se lit sur deux lignes — et le seuil n'est pas
   arbitraire : à 380 px le message retrouve sur sa rangée les 34 signes qu'il
   avait à 560 sur une seule, donc on bascule pile là où l'échange devient
   gagnant. */
@media (max-width: 560px) {
    .vigie-journal-ligne {
        flex-wrap: wrap;
        row-gap: 0;
    }

    .vigie-journal-message {
        flex: 1 0 100%;
        padding-left: 0.7rem;
    }

    /* Deux rangées par entrée : sans une gouttière un peu plus franche, deux
       entrées consécutives se lisent comme un seul pavé de quatre lignes. */
    .vigie-journal-flux {
        gap: 0.25rem;
    }
}

/* Apparition douce des nouvelles lignes : elles montent d'un cran en fondu. */
@keyframes vigie-journal-apparition {
    from {
        opacity: 0;
        transform: translateY(0.5rem);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

/* --- Pastilles de statut (sites) -------------------------------------------- */

.vigie-pastille-statut {
    position: relative;
    width: 0.55rem;
    height: 0.55rem;
    border-radius: 50%;
    display: inline-block;
    flex-shrink: 0;
}

.vigie-statut-up {
    background: var(--vigie-ok);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--vigie-ok) 22%, transparent);
}

.vigie-statut-down {
    background: var(--vigie-ko);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--vigie-ko) 25%, transparent);
    animation: vigie-pastille-alerte 1.6s ease-in-out infinite;
}

.vigie-statut-inconnu {
    background: var(--vigie-pastille-neutre);
}

/* --- Cartes de la vue flotte ------------------------------------------------ */

.vigie-carte {
    padding: 0.95rem 1.05rem 0.9rem;
    display: flex;
    flex-direction: column;
    gap: 0.7rem;
    height: 100%;
    cursor: pointer;
    position: relative;
    overflow: hidden;
    transition: border-color var(--vigie-transition), background-color var(--vigie-transition);
}

/* Liseré gauche : la carte se lit comme un fichier de l'explorateur, et le
   liseré s'allume au survol sans faire bouger la mise en page. */
.vigie-carte::before {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: 2px;
    background: var(--vigie-accent);
    opacity: 0;
    transition: opacity var(--vigie-transition);
}

.vigie-carte:hover,
.vigie-carte:focus-visible {
    border-color: var(--vigie-bordure-forte);
}

.vigie-carte:hover::before,
.vigie-carte:focus-visible::before {
    opacity: 1;
}

.vigie-carte-entete {
    display: flex;
    /* REPLI AUTORISÉ (2026-08-30), et c'est un calcul, pas une précaution : les
       chips sont tous en min-content (nowrap) et seule l'ellipse du nom absorbe
       le manque. Un « déploiement » et un « restreint » (désormais porteur d'un
       glyphe) simultanés totalisent ~340 px de contenu incompressible — plus
       large qu'une carte sous 360 px de viewport. Sans repli, l'excédent
       sortirait de la carte, et AUCUNE porte ne le verrait : la base de la
       porte e2e est vierge, elle ne rend jamais ces chips. La rangée de
       version, juste en dessous, replie déjà — même langue. */
    flex-wrap: wrap;
    align-items: center;
    gap: 0.45rem;
    min-width: 0;
}

.vigie-carte-nom {
    font-weight: 600;
    font-size: 1rem;
    letter-spacing: -0.01em;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.vigie-carte-code {
    font-family: var(--vigie-police-mono);
    color: var(--vigie-texte-3);
    font-size: 0.72rem;
    border: 1px solid var(--vigie-bordure);
    padding: 0.08rem 0.4rem;
    border-radius: var(--vigie-rayon-petit);
    margin-left: auto;
}

.vigie-carte-version {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
    min-height: 1.4rem;
}

/* Métrique héro : la grande valeur TTFB en haut, la sparkline en fond de la
   moitié basse de la zone — la courbe ne traverse jamais le chiffre. */
.vigie-carte-latence {
    position: relative;
    min-height: 84px;
    display: flex;
    align-items: flex-start;
}

.vigie-carte-spark {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 58%;
    opacity: 0.9;
}

.vigie-ttfb-bloc {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 0.12rem;
    pointer-events: none; /* le tooltip de la sparkline reste accessible dessous. */
}

.vigie-ttfb {
    font-family: var(--vigie-police-mono);
    font-size: 2rem;
    font-weight: 600;
    line-height: 1;
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.03em;
}

.vigie-ttfb-unite {
    font-size: 0.85rem;
    font-weight: 400;
    color: var(--vigie-texte-3);
    margin-left: 0.2rem;
    letter-spacing: 0;
}

.vigie-spark {
    width: 100%;
    height: 100%;
}

.vigie-spark-squelette {
    border-radius: var(--vigie-rayon-petit);
    opacity: 0.5;
}

.vigie-carte-horodatage {
    display: flex;
    align-items: center;
}

/* Pied de carte : séparateur POINTILLÉ — le trait technique d'un cadran, pas
   la ligne pleine d'une card web. */
.vigie-carte-pied {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.5rem;
    margin-top: auto;
    font-size: 0.82rem;
    padding-top: 0.6rem;
    border-top: 1px dashed var(--vigie-bordure);
}

/* Grand écran : les tuiles occupent l'espace — la vue reste dense, mais la
   métrique héro et le nom respirent. */
@media (min-width: 1600px) {
    .vigie-carte {
        padding: 1.15rem 1.25rem 1.05rem;
        gap: 0.85rem;
    }

    .vigie-carte-nom {
        font-size: 1.08rem;
    }

    .vigie-ttfb {
        font-size: 2.5rem;
    }

    .vigie-carte-latence {
        min-height: 128px;
    }

    .vigie-hero-valeur {
        font-size: 2.15rem;
    }

    .vigie-titre {
        font-size: 1.55rem;
    }
}

/* --- Pipeline des gates ----------------------------------------------------- */

/* Compact (cartes) : segments connectés façon barre de progression segmentée. */
.vigie-gates {
    display: flex;
    align-items: center;
    gap: 3px;
    width: 100%;
}

.vigie-gates-compactes .vigie-gate-item {
    display: flex;
    flex: 1 1 0;
    min-width: 0;
}

.vigie-gates-compactes .vigie-gate {
    height: 5px;
    flex: 1 1 0;
    border-radius: 2px;
}

.vigie-gate-success {
    background: var(--vigie-ok);
}

.vigie-gate-failure {
    background: var(--vigie-ko);
}

.vigie-gate-encours {
    background: var(--vigie-attention);
    animation: vigie-pulse 1.2s ease-in-out infinite;
}

.vigie-gate-pending {
    background: transparent;
    border: 1px solid var(--vigie-pastille-neutre);
}


/* EN FILE (queued) — LE TROISIÈME ÉTAT, et ce n'est pas un raffinement.
   « Le runner tient ce pas, il démarre dans trois secondes » et « le run n'est
   jamais arrivé jusqu'ici » se rendaient par le MÊME carré vide. Sur un écran
   qu'on opère pendant un déploiement, c'est exactement la différence qu'on
   vient chercher. Elle se dit en STATIQUE : un état n'a pas à s'animer pour
   être lu, et une quatrième animation ici n'apprendrait rien de plus. */
.vigie-gate-enfile {
    background: color-mix(in srgb, var(--vigie-texte-3) 34%, transparent);
    border: 1px solid var(--vigie-texte-3);
}

/* La durée ÉCOULÉE d'un pas qui tourne, en ambre comme le pas lui-même. Le
   couple teinte/surface est celui que ContrasteTokensTests mesure déjà. */
.vigie-gate-duree-encours {
    color: var(--vigie-attention);
}
.vigie-gate-skipped {
    background: repeating-linear-gradient(
        45deg,
        color-mix(in srgb, var(--vigie-texte-3) 45%, var(--vigie-grille)) 0 4px,
        var(--vigie-grille) 4px 8px);
    color: var(--vigie-texte-2);
}

/* Step hors contrat : signalé en violet, il ne devrait pas exister. */
.vigie-gate-inconnue {
    background: var(--vigie-hors-contrat);
}

/* Grande (vue site) : stepper horizontal, nœuds reliés par des connecteurs. */
.vigie-gates-grandes {
    align-items: stretch;
    gap: 0;
    row-gap: 1rem;
    flex-wrap: wrap;
}

.vigie-gates-grandes .vigie-gate-item {
    flex: 1 1 0;
    min-width: 6.5rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.4rem;
    position: relative;
    text-align: center;
}

.vigie-gates-grandes .vigie-gate {
    width: 1rem;
    height: 1rem;
    border-radius: 3px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 0.75rem;
    line-height: 1;
    box-sizing: border-box;
    z-index: 1;
}

/* Connecteur : trait qui relie chaque nœud au précédent. */
.vigie-gates-grandes .vigie-gate-item::before {
    content: "";
    position: absolute;
    top: calc(0.5rem - 1px);
    left: calc(-50% + 1rem);
    right: calc(50% + 1rem);
    height: 1px;
    background: var(--vigie-bordure-forte);
}

.vigie-gates-grandes .vigie-gate-item:first-child::before {
    display: none;
}

.vigie-gate-nom {
    font-family: var(--vigie-police-mono);
    font-size: 0.76rem;
    color: var(--vigie-texte);
}

.vigie-gate-duree {
    font-family: var(--vigie-police-mono);
    color: var(--vigie-texte-3);
    font-size: 0.72rem;
    font-variant-numeric: tabular-nums;
}

.vigie-gates-vide {
    color: var(--vigie-texte-3);
    font-size: 0.86rem;
}

@media (max-width: 640px) {
    /* Le stepper passe en liste verticale : nœud + nom + durée sur une ligne. */
    .vigie-gates-grandes {
        flex-direction: column;
        align-items: stretch;
        gap: 0.5rem;
    }

    .vigie-gates-grandes .vigie-gate-item {
        flex-direction: row;
        text-align: left;
        min-width: 0;
        gap: 0.6rem;
    }

    .vigie-gates-grandes .vigie-gate-item::before {
        display: none;
    }

    .vigie-gates-grandes .vigie-gate-duree {
        margin-left: auto;
    }
}

@keyframes vigie-pulse {
    0%, 100% {
        opacity: 1;
    }

    50% {
        opacity: 0.35;
    }
}


/* ══ LE DÉPLOIEMENT, EN QUATRE ACTES ═══════════════════════════════════════════

   Ce bloc est le seul de la feuille où La Vigie a le droit de hausser la voix.
   Partout ailleurs elle chuchote, et ses propres commentaires le disent : « un
   radar de veille signale, il ne clignote pas » (écho 0,78 → 1), « un voyant
   analogique respire, il ne fait pas de morse » (1 → 0,74). Cette sobriété
   n'est pas de l'austérité, c'est ce qui rend un geste ample LISIBLE quand il
   arrive. Un déploiement est le seul moment qui le mérite.

   CE QUE CHAQUE GESTE COÛTE, ET POURQUOI IL EST DÉFENDABLE. Ce lot ajoute cinq
   animations « infinite » aux douze existantes. Les cinq sont conditionnées à un
   run réellement en cours — quelques minutes par jour. Sur une flotte au repos,
   c'est-à-dire l'état normal d'un onglet laissé ouvert toute la journée, ce lot
   ne coûte RIEN de plus qu'avant. C'est la seule raison pour laquelle elles ont
   le droit d'exister.

   CE QU'AUCUNE D'ELLES NE FAIT, ET CE QUE ÇA CASSERAIT. Aucune ne pose filter,
   transform, will-change, contain: paint ni overflow: hidden sur un ANCÊTRE
   d'un position: sticky. La Vigie en porte quatre, vivants :

     .vigie-verriere      (l. ~1270)  bande de commande, top: 0, z-index: 29
     .vigie-appbar-titre  (l. ~7333)  retour vers l'accueil, sous 900 px
     .vigie-activite-bas  (l. ~1215)  thème + déconnexion, épinglés au rail
     .vigie-log-num       (l. ~6280)  gouttière des numéros du visualiseur

   Ces cinq propriétés créent un bloc conteneur et DÉCROCHENT un sticky
   descendant : sans erreur, sans avertissement. Un sticky mort SE VOIT
   désormais en CI : la porte e2e (tests/e2e/parcours/03-epinglage.mjs, jouée
   dans un vrai navigateur derrière le second facteur par
   .github/workflows/extended-quality.yml) mesure l'épinglage PAR LE
   COMPORTEMENT — scroll, position comparée, ancêtre fautif nommé — pour trois
   des quatre. Le quatrième, .vigie-log-num, reste hors couverture : sa page
   n'affiche des lignes qu'avec un run CI en base et des traces GitHub, et le
   décor à semer est un lot à part (tests/e2e/README.md, « Angles morts »).
   Les quatre vivent dans la coque et le visualiseur de logs ; aucun n'est
   descendant de .vigie-carte, de .vigie-gates ni de .vigie-badge-mono. C'est
   CE FAIT, et lui seul, qui autorise le transform du retournement de version
   plus bas. La règle n'est pas « pas de transform », elle est « pas de
   transform sur un ancêtre de sticky ».

   Aucun overflow: hidden n'est ajouté par ce lot. Les balayages sont bornés par
   leur background-size, pas par un découpage d'ancêtre : un dégradé borné ne
   peut pas déborder. C'est un choix de mécanique, pas de prudence.

   UNE RÈGLE DE DESSIN QUE LE SOCLE DE MOUVEMENT RÉDUIT IMPOSE. Sous
   « animation-duration: 0.01ms; animation-iteration-count: 1 », chaque animation
   SAUTE À SON IMAGE DE 100 %. Le 100 % de chaque keyframe ci-dessous est donc
   l'état de repos correct. Les trois cas où ce n'est pas possible sont traités
   nommément dans le bloc prefers-reduced-motion, et c'est précisément à ça que
   sert une garde par énumération à côté d'une garde par famille : la famille
   sait dire « ça ne bouge plus », elle ne sait pas dire QUEL REPOS EST LE BON. */

/* ── ACTE I — LA MISE SOUS TENSION ────────────────────────────────────────────
   Déclencheur : un run queued ou in_progress sur ce site. Cible : l'œil qui
   balaie une grille de huit cartes sans les lire. */

/* Le HALO d'encastrement : la carte devient un module de rack sous tension.
   3,8 s est la cadence EXACTE de vigie-voyant, le voyant du rail d'activité —
   l'écran n'a pas besoin de deux respirations différentes pour dire la même
   chose. box-shadow porte à trois mètres et ne crée aucun bloc conteneur : sur
   une carte, c'est l'outil juste, là où un filter aurait été le piège. */
.vigie-carte-deploiement {
    animation: vigie-carte-souffle 3.8s ease-in-out infinite;
}

@keyframes vigie-carte-souffle {
    0%,
    100% {
        box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--vigie-attention) 26%, transparent),
            0 0 24px -8px color-mix(in srgb, var(--vigie-attention) 50%, transparent);
    }

    50% {
        box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--vigie-attention) 14%, transparent),
            0 0 18px -10px color-mix(in srgb, var(--vigie-attention) 28%, transparent);
    }
}

/* Le LISERÉ VIVANT. Le liseré de survol existe déjà (2 px, accent, opacité 0) :
   en déploiement il s'allume en ambre, s'épaissit, et un dégradé DESCEND le
   long de sa hauteur — un signal qui parcourt un bus, pas un clignotant.
   2,6 s parce qu'il fait toute la hauteur de la carte (~320 px) : plus rapide,
   huit cartes deviennent un stroboscope. background-position, jamais transform. */
.vigie-carte-deploiement::before {
    width: 3px;
    opacity: 1;
    background: linear-gradient(180deg,
        transparent 0%,
        var(--vigie-attention) 38%,
        var(--vigie-attention) 62%,
        transparent 100%) repeat-y;
    background-size: 100% 200%;
    animation: vigie-carte-signal 2.6s linear infinite;
}

@keyframes vigie-carte-signal {
    from {
        background-position: 0 0;
    }

    to {
        background-position: 0 -200%;
    }
}

/* La LIGNE DE SCAN sur la sparkline. C'est le seul endroit de la carte qui est
   déjà un instrument de mesure : une ligne qui le traverse dit « relevé en
   cours » dans la langue d'un oscilloscope, et n'a besoin d'aucune légende.
   Le graphe lui-même n'est PAS touché — il est rendu par ApexCharts en JS ;
   l'animer voudrait dire pousser des options au graphe à chaque changement de
   run, pour un gain que cette surcouche donne gratuitement. */
.vigie-carte-deploiement .vigie-carte-spark {
    position: relative;
}

.vigie-carte-deploiement .vigie-carte-spark::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: linear-gradient(90deg,
        transparent 0%,
        color-mix(in srgb, var(--vigie-attention) 55%, transparent) 50%,
        transparent 100%) no-repeat;
    background-size: 14% 100%;
    animation: vigie-spark-scan 2s ease-in-out infinite;
}

@keyframes vigie-spark-scan {
    from {
        background-position: -20% 0;
    }

    to {
        background-position: 120% 0;
    }
}

/* Le CARTOUCHE. Le halo attrape l'œil de loin ; arrivé sur la carte, il faut un
   MOT. C'est aussi ce qui rend l'écran lisible pour quelqu'un qui n'a pas le
   contexte — et pour quelqu'un qui ne distingue pas l'ambre du reste. */
.vigie-chip-deploiement {
    display: inline-flex;
    align-items: center;
    gap: 0.32rem;
    color: var(--vigie-attention);
    border-color: color-mix(in srgb, var(--vigie-attention) 45%, transparent);
    background: color-mix(in srgb, var(--vigie-attention) 10%, transparent);
}

.vigie-chip-diode {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--vigie-attention);
    box-shadow: 0 0 6px 0 color-mix(in srgb, var(--vigie-attention) 70%, transparent);
    animation: vigie-pulse 1.2s ease-in-out infinite;
}

/* ── ACTE II — LE SIGNAL QUI COURT ────────────────────────────────────────────
   Le run se VOIT avancer, sur les deux formes du pipeline. */

.vigie-gates-encours {
    position: relative;
}

/* LA COURSE : une tête de comète parcourt la rangée. 2,4 s parce qu'à cette
   vitesse l'œil SUIT la tête, là où à une seconde il la subit. Et surtout : ce
   n'est PAS une jauge, elle ne prétend mesurer aucune progression. La lenteur
   est précisément ce qui l'empêche d'être lue comme une barre de chargement. */
.vigie-gates-encours::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: linear-gradient(90deg,
        transparent 0%,
        color-mix(in srgb, var(--vigie-attention) 40%, transparent) 42%,
        color-mix(in srgb, var(--vigie-attention) 85%, transparent) 50%,
        color-mix(in srgb, var(--vigie-attention) 40%, transparent) 58%,
        transparent 100%) no-repeat;
    background-size: 26% 100%;
    animation: vigie-gates-course 2.4s linear infinite;
}

@keyframes vigie-gates-course {
    from {
        background-position: -30% 0;
    }

    to {
        background-position: 130% 0;
    }
}

/* La LUEUR du pas actif : un seul pas brille dans la rangée. */
.vigie-gates-encours .vigie-gate-encours {
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--vigie-attention) 45%, transparent),
        0 0 12px -2px color-mix(in srgb, var(--vigie-attention) 75%, transparent);
}

/* Les deux issues repeignées en DÉGRADÉ et non en couleur plate : c'est ce qui
   rend background-size animable, donc un remplissage possible. Aucune teinte ne
   change ; seule la mécanique de peinture change. */
.vigie-gate-success {
    background: linear-gradient(var(--vigie-ok), var(--vigie-ok)) no-repeat left center;
    background-size: 100% 100%;
}

.vigie-gate-failure {
    background: linear-gradient(var(--vigie-ko), var(--vigie-ko)) no-repeat left center;
    background-size: 100% 100%;
}

/* ── ACTE III — L'ISSUE ───────────────────────────────────────────────────────

   DEUX ISSUES, UN SEUL VOCABULAIRE, ET C'EST TOUT L'ENJEU. La réussite et
   l'échec ne sont pas deux effets : ce sont deux issues de la MÊME mécanique.
   L'une la propage, l'autre la brise. C'est ce qui rend l'opposition lisible —
   et lisible AUSSI pour quelqu'un qui ne distingue pas le rouge du vert, parce
   qu'elle est d'abord une différence de FORME : une vague qui se propage contre
   une course qui s'arrête net. La couleur ne fait que confirmer.

   L'ISSUE NE SE JOUE QUE SI ELLE A ÉTÉ VUE ARRIVER. Les classes d'issue
   (cascade, rupture, onde de carte) disent un ÉTAT et se posent dès le premier
   rendu ; le MOUVEMENT, lui, exige en plus .vigie-issue-observee, que le
   composant n'ajoute qu'après avoir observé la transition « run en cours →
   conclu » dans ce circuit (même garde que la bascule de version). Sans cette
   séparation, ouvrir /flotte rejouait l'onde et la cascade de huit cartes pour
   des runs conclus depuis des heures. Au premier rendu, chaque keyframe est
   remplacée par son état FINAL, posé en statique sur la classe d'état —
   la doctrine du socle de mouvement réduit, appliquée à l'arrivée sur la page :
   l'information reste, le geste part. Verrous : CarteDeploiementTests et
   RangeeGatesTests (le modifieur n'est pas là au premier rendu, il est là après
   la transition), et les assertions de présence sur cette feuille. */

/* LA CASCADE. Chaque pas se remplit de gauche à droite, décalé de 90 ms sur son
   voisin. Le décalage est porté par --vigie-gate-rang, posée en style inline par
   le composant — MÊME MÉCANIQUE que --vigie-echo-phase sur les échos du radar.
   90 ms : sous ~50 ms l'œil FUSIONNE la série en un seul événement et la cascade
   cesse d'exister ; au-delà de ~150 ms elle devient une file d'attente qui
   survit à l'attention. 90 ms est l'intervalle où dix pas se lisent comme UNE
   VAGUE. Total : 880 ms sur cinq gates, 1,33 s sur dix — un rapport de 1 à 200
   avec le run qu'elle clôt, donc le récit ne prend jamais le pas sur ce qu'il
   raconte, et un pipeline plus long mérite bien une cascade plus longue.
   480 ms par pas : sous ~300 ms un balayage se lit comme un saut, au-delà de
   ~600 ms il traîne derrière l'information.
   « backwards » n'est pas décoratif : sans lui, un pas s'afficherait PLEIN
   pendant son délai avant de sauter à vide pour se remplir. */
.vigie-gates-cascade.vigie-issue-observee .vigie-gate-success {
    animation: vigie-gate-remplissage 480ms ease-out 1 backwards;
    animation-delay: calc(var(--vigie-gate-rang, 0) * 90ms);
}

@keyframes vigie-gate-remplissage {
    from {
        background-size: 0% 100%;
    }

    to {
        background-size: 100% 100%;
    }
}

/* Le FLARE de phosphore qui suit chaque remplissage. Un box-shadow qui s'ouvre
   et retombe — la langue de vigie-radar-flare, sans son transform. */
.vigie-gates-cascade.vigie-issue-observee .vigie-gate-success {
    animation: vigie-gate-remplissage 480ms ease-out 1 backwards,
        vigie-gate-flare 520ms ease-out 1 backwards;
}

@keyframes vigie-gate-flare {
    0% {
        box-shadow: 0 0 0 0 transparent;
    }

    45% {
        box-shadow: 0 0 14px 3px color-mix(in srgb, var(--vigie-ok) 60%, transparent);
    }

    100% {
        box-shadow: 0 0 0 0 transparent;
    }
}

/* Le CONNECTEUR qui se charge (forme stepper). Ces filets étaient morts : ils
   n'occupaient que de l'espace. Chargés en vert dans le sillage de la vague, le
   pipeline devient un circuit qui se remplit, et « le run est allé jusqu'au
   bout » se voit là mieux que partout ailleurs.
   La CHARGE (le vert plein) est l'état et se pose dès le premier rendu ; seul
   le remplissage — le sillage — attend l'issue observée. */
.vigie-gates-grandes.vigie-gates-cascade .vigie-gate-item::before {
    background: linear-gradient(var(--vigie-ok), var(--vigie-ok)) no-repeat left center,
        var(--vigie-bordure-forte);
    background-size: 100% 100%;
}

.vigie-gates-grandes.vigie-gates-cascade.vigie-issue-observee .vigie-gate-item::before {
    animation: vigie-gate-remplissage 480ms ease-out 1 backwards;
    animation-delay: calc(var(--vigie-gate-rang, 0) * 90ms);
}

/* LA RUPTURE. Le pas fautif ne se remplit PAS : le balayage part de gauche,
   atteint la moitié, et s'arrête net. Un remplissage interrompu en pleine course
   dit « ça s'est arrêté ICI » mieux qu'un carré rouge, et il le dit sans
   couleur.
   AUCUN DÉLAI, ET C'EST DÉLIBÉRÉ : le rouge arrive à zéro milliseconde, non
   décalé. Ce qui est mis en scène, c'est l'ARRÊT DE LA COURSE — jamais
   l'annonce de la nouvelle. Échelonner le rouge retarderait une mauvaise
   nouvelle de plus d'une seconde, et un écran qu'on opère n'a pas ce droit.
   « forwards » parce que l'échec DEMEURE : la réussite se referme, l'échec
   reste à moitié rempli, en travers du pipeline, aussi longtemps qu'on le
   regarde.
   Le remplissage arrêté à 52 % est L'ÉTAT de la rupture : il se pose en
   statique dès le premier rendu, et c'est la COURSE vers lui — le balayage qui
   s'arrête net — qui exige l'issue observée. */
.vigie-gates-rupture .vigie-gate-failure {
    background-size: 52% 100%;
}

.vigie-gates-rupture.vigie-issue-observee .vigie-gate-failure {
    animation: vigie-gate-arret 520ms ease-out 1 forwards;
    animation-delay: 0ms;
}

@keyframes vigie-gate-arret {
    from {
        background-size: 0% 100%;
    }

    62% {
        background-size: 52% 100%;
    }

    to {
        background-size: 52% 100%;
    }
}

/* CE QUI SUIT LA RUPTURE RESTE ÉTEINT, ET NE CASCADE JAMAIS. Ce n'est pas une
   précaution esthétique, c'est un fait : sur un échec, les pas suivants n'ont
   pas tourné. La rangée qui reste noire après le point de rupture EST
   l'information, et l'animer serait inventer un déroulé qui n'a pas eu lieu.
   Verrou : RangeeGatesTests, le pas fautif porte .vigie-gate-item-rupture et
   rien après lui ne peut s'animer. */
.vigie-gates-rupture .vigie-gate-item-rupture ~ .vigie-gate-item .vigie-gate {
    animation: none !important;
}

.vigie-gates-rupture .vigie-gate-item-rupture ~ .vigie-gate-item::before {
    animation: none !important;
}

/* L'ONDE DE LA CARTE. La cascade se joue sur une rangée de 5 px de haut : sans
   elle, l'issue est invisible à deux mètres — or c'est à deux mètres qu'on
   regarde un tableau de bord. La réussite MONTE ET RETOMBE ; l'échec MONTE ET
   RESTE, et la carte garde son anneau rouge tant que le dernier run est rouge.
   C'est la même opposition que sur les gates, à l'échelle de la carte.
   L'onde ne se joue que sur une issue OBSERVÉE : l'état de repos d'une
   réussite est « rien », il n'y a donc rien à poser en statique ici. */
.vigie-carte-issue-succes.vigie-issue-observee {
    animation: vigie-carte-onde 900ms ease-out 1;
}

@keyframes vigie-carte-onde {
    0% {
        box-shadow: 0 0 0 0 transparent;
    }

    35% {
        box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--vigie-ok) 45%, transparent),
            0 0 26px -6px color-mix(in srgb, var(--vigie-ok) 55%, transparent);
    }

    100% {
        box-shadow: 0 0 0 0 transparent;
    }
}

/* L'anneau d'échec est un ÉTAT : il se pose en statique dès le premier rendu
   (un run rouge vieux de trois heures reste un run rouge). Seule la MONTÉE de
   l'anneau — l'annonce — exige d'avoir vu l'issue arriver. */
.vigie-carte-issue-echec {
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--vigie-ko) 30%, transparent),
        0 0 20px -10px color-mix(in srgb, var(--vigie-ko) 45%, transparent);
}

.vigie-carte-issue-echec.vigie-issue-observee {
    animation: vigie-carte-onde-echec 900ms ease-out 1 backwards;
}

@keyframes vigie-carte-onde-echec {
    0% {
        box-shadow: 0 0 0 0 transparent;
    }

    40%,
    100% {
        box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--vigie-ko) 30%, transparent),
            0 0 20px -10px color-mix(in srgb, var(--vigie-ko) 45%, transparent);
    }
}

/* ── ACTE IV — LA BASCULE DE VERSION ──────────────────────────────────────────

   Le moment le plus attendu de l'écran, et jusqu'ici le seul complètement muet.
   Le badge se RETOURNE comme un compteur mécanique, puis s'entoure d'un halo
   vert qui monte et retombe. 420 ms + 780 ms décalés = 1,2 s, quelques fois par
   jour, sur un écran qu'on ouvre pour ça.

   transform EST AUTORISÉ ICI, et c'est le point de mécanique du lot : le badge
   de version n'a aucun position: sticky sous lui. La contrainte n'a jamais été
   « pas de transform », elle a toujours été « pas de transform sur un ancêtre
   de sticky ». Voir l'inventaire des quatre en tête de ce bloc.

   POURQUOI CE GESTE N'EST PAS ENCHAÎNÉ SUR LA CASCADE. Le run vert et la
   version servie sont relevés par DEUX COLLECTEURS À DEUX CADENCES (8 s en
   cadence live pour le CI, des minutes pour version.json). La bascule arrive
   réellement plus tard, et CET ÉCART EST L'INFORMATION : entre les deux, la
   prod sert encore l'ancienne version. Les enchaîner ferait croire qu'un
   déploiement est instantané. */
.vigie-badge-bascule {
    transform-origin: center;
    animation: vigie-bascule-retournement 420ms ease-out 1,
        vigie-bascule-halo 780ms ease-out 420ms 1;
}

@keyframes vigie-bascule-retournement {
    from {
        transform: rotateX(-90deg);
    }

    to {
        transform: rotateX(0deg);
    }
}

@keyframes vigie-bascule-halo {
    0% {
        box-shadow: 0 0 0 0 transparent;
    }

    35% {
        box-shadow: 0 0 14px 2px color-mix(in srgb, var(--vigie-ok) 55%, transparent);
    }

    100% {
        box-shadow: 0 0 0 0 transparent;
    }
}

/* ── LE REJEU ─────────────────────────────────────────────────────────────────

   Un run PASSÉ rejoué à partir de sa chronologie mesurée. Les décalages et les
   durées viennent des variables inline --vigie-pas-debut et --vigie-pas-duree,
   posées par le composant : ce sont des millisecondes RÉELLES multipliées par un
   facteur UNIQUE, donc les proportions sont strictement préservées — un pas qui
   a mis six fois plus de temps qu'un autre en prend toujours six fois plus à
   l'écran (voir Chronologie).

   UN REJEU NE DOIT JAMAIS POUVOIR ÊTRE PRIS POUR UN RUN EN COURS. C'est le
   mensonge d'écran que ce dépôt refuse partout, et c'est pourquoi
   .vigie-gates-rejeu ne cohabite avec aucune classe de direct (garanti côté
   composant), pourquoi le bandeau porte l'horodatage D'ORIGINE et le facteur
   d'accélération, et pourquoi un vrai run qui démarre coupe le rejeu.

   .vigie-issue-observee figure dans ces sélecteurs pour la SPÉCIFICITÉ, pas
   pour le sens : un rejeu la porte toujours (il n'existe que parce qu'on vient
   de le demander), et sans elle les règles de cascade et de rupture — trois
   classes depuis que le mouvement est gardé — écraseraient ces délais mesurés
   par leurs délais de rang. */
.vigie-gates-rejeu.vigie-issue-observee .vigie-gate-success,
.vigie-gates-rejeu.vigie-issue-observee .vigie-gate-failure,
.vigie-gates-rejeu.vigie-issue-observee .vigie-gate-item::before {
    animation-delay: var(--vigie-pas-debut, 0ms);
    animation-duration: var(--vigie-pas-duree, 480ms);
}

.vigie-rejeu-bandeau {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.55rem;
    margin-bottom: 0.7rem;
    padding: 0.35rem 0.6rem;
    border: 1px dashed color-mix(in srgb, var(--vigie-accent) 40%, transparent);
    border-radius: var(--vigie-rayon);
    background: var(--vigie-surface-2);
    font-size: 0.76rem;
}

.vigie-rejeu-marque {
    font-family: var(--vigie-police-mono);
    font-size: 0.68rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--vigie-accent-contraste);
    background: var(--vigie-accent);
    padding: 0.1rem 0.4rem;
    border-radius: 3px;
}

.vigie-rejeu-detail {
    color: var(--vigie-texte-2);
    font-family: var(--vigie-police-mono);
}
/* --- Vue site --------------------------------------------------------------- */

/* BANDEAU DE TÊTE de la vue site : identité au-dessus, rangée d'instruments
   en dessous, UNE seule surface. Avant, c'étaient deux bandes pleine largeur
   (« hero » et « INDICATEURS ») séparées par tout le bloc Veille : le verdict
   « EN LIGNE » d'un côté, les chiffres qui le justifient hors de l'écran de
   l'autre. Ici tout tient dans un bloc de ~230 px, et surtout dans le même
   regard. Le filet pointillé horizontal marque la charnière identité/mesures :
   c'est la même grammaire que les autres panneaux de la coque. */
.vigie-bandeau {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    padding: 0.9rem 1.2rem;
    margin-bottom: 1rem;
}

.vigie-bandeau-identite {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    min-width: 0;
}

/* La rangée d'instruments : DEUX colonnes — le verdict, puis les groupes qui le
   démontrent. L'ordre n'est pas décoratif, c'est la phrase « voilà l'état, et
   voilà pourquoi ». La colonne de gauche se dimensionne sur son contenu, celle
   de droite prend tout le reste et fait défiler ses groupes sur une ou deux
   rangées selon la largeur. */
.vigie-bandeau-instruments {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: stretch;
    gap: 1rem 1.8rem;
    padding-top: 0.75rem;
    border-top: 1px dashed var(--vigie-bordure);
}

/* Le bloc de verdict : lampe + les deux mesures de l'instant. Le filet vertical
   court sur TOUTE la hauteur de la rangée — il détache le verdict des fenêtres
   longues (24 h / 7 j / 30 j) qui suivent, sans quoi la dispo « sur la fenêtre
   du graphe » se lirait comme un quatrième cadran. */
.vigie-bandeau-verdict {
    display: flex;
    /* flex-wrap : sans lui, deux lampes + la mesure TTFB forment un rang
       INCOMPRESSIBLE dont le min-content depend des libelles (« en ligne »,
       derive...) — mesure par la porte a 341 px pour 320 sur /site/bw, en
       fluctuant d'un run a l'autre avec les donnees semees. Meme repli que la
       tete de carte : la mesure descend sous les lampes quand le rang manque. */
    flex-wrap: wrap;
    align-items: center;
    gap: 1.2rem;
    padding-right: 1.8rem;
    border-right: 1px dashed var(--vigie-bordure);
}

.vigie-bandeau-groupes {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    align-content: flex-start;
    gap: 1rem 1.6rem;
    min-width: 0;
}

/* Tout se resserre dans le bandeau : à leur taille de panneau (cadrans 8,6 rem,
   tuiles côte à côte), les quatre groupes tenaient sur trois rangées. Les
   valeurs, elles, ne bougent pas d'un corps — c'est l'AIR qu'on retire, jamais
   la lisibilité. */
.vigie-bandeau .vigie-cadran {
    width: 5.8rem;
}

.vigie-bandeau .vigie-kpi-jauges {
    min-width: 9rem;
    max-width: 11rem;
}

/* La bande de 30 jours est l'instrument le plus large du bandeau, et c'est
   voulu : trente barres sous 9 rem deviennent illisibles (moins de 3 px par
   jour, gouttières comprises). Elle s'étire donc sur le reste de sa rangée. */
.vigie-bandeau .vigie-bande-jours {
    min-width: 7rem;
    max-width: 15rem;
}

/* LA FENÊTRE DE QUATRE-VINGT-DIX JOURS RÉCLAME SA LARGEUR, et c'est le seuil
   ci-dessus qui l'impose : à 15 rem (≈ 240 px), quatre-vingt-dix barres valent
   2,7 px chacune — sous les 3 px que la règle précédente déclare illisibles. Une
   hachure écrasée sous 3 px ne se voit plus, c'est-à-dire qu'un mois de collecte
   manquante redeviendrait invisible : c'est la garantie de l'instrument qui
   disparaîtrait, pas seulement du confort. 26 rem donnent 4,6 px par journée.
   Les fenêtres de 12 et 24 mois ne sont pas concernées — douze et vingt-quatre
   barres tiennent dans 15 rem avec de la marge. */
.vigie-bandeau .vigie-bande-jours-dense {
    max-width: 26rem;
}

.vigie-bandeau .vigie-bande-jours-svg {
    height: 22px;
}

.vigie-bandeau .vigie-instruments {
    gap: 0.6rem 0.9rem;
}

/* Les deux durées de build s'empilent : côte à côte, elles imposaient au groupe
   CI la largeur de deux tuiles pour deux valeurs de quatre caractères. */
.vigie-bandeau .vigie-instruments .vigie-kpi-tuiles {
    flex-direction: column;
    gap: 0.35rem;
}

.vigie-bandeau .vigie-kpi-tuile-large {
    min-width: 7.5rem;
}

/* En étroit, le filet vertical n'a plus de sens : le verdict passe au-dessus
   des groupes et c'est un filet HORIZONTAL qui les sépare. */
@media (max-width: 900px) {
    .vigie-bandeau-instruments {
        grid-template-columns: minmax(0, 1fr);
    }

    .vigie-bandeau-verdict {
        padding-right: 0;
        padding-bottom: 0.75rem;
        border-right: none;
        border-bottom: 1px dashed var(--vigie-bordure);
    }
}

.vigie-hero-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.86rem;
}

.vigie-hero-mesure {
    display: flex;
    flex-direction: column;
    gap: 0.18rem;
    min-width: 5rem;
}

.vigie-hero-valeur {
    font-family: var(--vigie-police-mono);
    font-size: 1.75rem;
    font-weight: 600;
    line-height: 1.1;
    letter-spacing: -0.03em;
    font-variant-numeric: tabular-nums;
}

.vigie-hero-valeur .vigie-ttfb-unite {
    font-size: 0.9rem;
}

.vigie-section {
    margin-bottom: var(--vigie-pas-3);
}

/* Le titre de section vit avec la plaque gravée, plus haut : c'est le doublon
   qui occupait cette place qui faisait diverger les deux moitiés de la règle. */

.vigie-panneau {
    padding: 1.1rem 1.2rem;
}

.vigie-pipeline-meta {
    margin: 0 0 0.9rem;
}

/* Table des runs : lignes serrées, en-têtes mono — une sortie de commande. */
.vigie-table-defilement {
    overflow-x: auto;
}

.vigie-table-runs {
    background-color: transparent !important;
    min-width: 640px;
}

.vigie-table-runs .mud-table-container {
    background: transparent;
}

.vigie-table-runs th {
    font-family: var(--vigie-police-mono) !important;
    color: var(--vigie-texte-3) !important;
    font-size: 0.7rem !important;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    border-bottom: 1px solid var(--vigie-bordure) !important;
    white-space: nowrap;
}

.vigie-table-runs td {
    border-bottom: 1px solid var(--vigie-bordure) !important;
    font-size: 0.86rem;
    white-space: nowrap;
}

.vigie-table-runs tr:last-child td {
    border-bottom: none !important;
}

.vigie-table-runs .mud-table-row:hover {
    background: var(--vigie-surface-2) !important;
}

/* Chips de conclusion des runs. */
.vigie-conclusion {
    display: inline-block;
    font-family: var(--vigie-police-mono);
    padding: 0.08rem 0.45rem;
    border-radius: var(--vigie-rayon-petit);
    font-size: 0.74rem;
    border: 1px solid;
    line-height: 1.5;
}

/* La pastille devenue LIEN (PastilleRun) garde l'habit du chip : la couleur
   d'état reste celle des classes ci-dessous, et le soulignement n'apparaît
   qu'au survol — l'affordance sans repeindre l'instrument. Le clavier est déjà
   servi par l'anneau global a:focus-visible. */
a.vigie-conclusion {
    text-decoration: none;
}

a.vigie-conclusion:hover {
    text-decoration: underline;
}

.vigie-conclusion-success {
    color: var(--vigie-ok);
    border-color: color-mix(in srgb, var(--vigie-ok) 40%, transparent);
    background: color-mix(in srgb, var(--vigie-ok) var(--vigie-plaque), transparent);
}

.vigie-conclusion-failure,
.vigie-conclusion-cancelled,
.vigie-conclusion-timed_out {
    color: var(--vigie-ko);
    border-color: color-mix(in srgb, var(--vigie-ko) 40%, transparent);
    background: color-mix(in srgb, var(--vigie-ko) var(--vigie-plaque), transparent);
}

.vigie-conclusion-in_progress,
.vigie-conclusion-queued {
    color: var(--vigie-attention);
    border-color: color-mix(in srgb, var(--vigie-attention) 40%, transparent);
    background: color-mix(in srgb, var(--vigie-attention) var(--vigie-plaque), transparent);
}

/* Graphe de latence ApexCharts. */
.vigie-graphe-conteneur {
    padding: 0.8rem 0.8rem 0.3rem;
}

.vigie-graphe-squelette {
    border-radius: var(--vigie-rayon);
    opacity: 0.5;
}

/* Porte-motifs des graphes ApexCharts : un SVG de taille nulle qui ne sert qu'à
   déclarer un <pattern>. ApexCharts construit son propre SVG et n'accepte qu'une
   COULEUR de remplissage ; une référence de serveur de peinture (url(#motif)) se
   résout à l'échelle du document, ce qui permet de lui prêter notre hachure.
   Retiré du flux et de l'arbre d'accessibilité : il n'a rien à annoncer. */
.vigie-graphe-motifs {
    position: absolute;
    width: 0;
    height: 0;
    overflow: hidden;
}

/* Légende du troisième état, sous le graphe. Elle ne paraît qu'en présence de
   journées muettes (voir GrapheVisiteurs) : une légende qui explique un motif
   absent de l'écran est du bruit. */
.vigie-graphe-legende {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.2rem 1rem;
    margin: 0.1rem 0 0.4rem;
    font-size: 0.72rem;
}

.vigie-graphe-legende-item {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
}

/* La marque reprend EXACTEMENT la hachure des bandes : même encre, même angle,
   même pas que .vigie-vis-trou et que BandeJours. */
.vigie-graphe-legende-marque {
    flex: none;
    width: 0.85rem;
    height: 0.85rem;
    border-radius: 2px;
    border: 1px solid color-mix(in srgb, var(--vigie-texte-3) 20%, transparent);
    background: repeating-linear-gradient(
        45deg,
        var(--vigie-texte-3) 0 1.4px,
        transparent 1.4px 4px);
}

/* Le repère des journées à zéro : la même encre que les barres, parce que c'est
   une MESURE et pas une absence, et une forme qui ne peut pas se lire comme une
   hauteur. Il ne prend aucune teinte de rôle : l'audience ne juge rien. */
.vigie-graphe-legende-zero {
    flex: none;
    width: 0.85rem;
    height: 0.3rem;
    border-radius: 1px;
    background: var(--vigie-accent);
    opacity: 0.75;
}

/* Le panneau de dérive PLEINE LARGEUR a disparu au lot 8 : ses six règles, comme
   son markup, servaient à écrire deux sha et une phrase à trois cents pixels de
   défilement du verdict de disponibilité. La dérive est désormais une LAMPE dans
   le bandeau de tête (deuxième verdict, même instrument que le premier), et le
   sha attendu un badge ambre à côté du sha servi. */

/* La note d'échelle sous un graphe : une échelle logarithmique se DIT, comme un
   cadran dit sa graduation tronquée dans son infobulle. */
.vigie-graphe-note {
    margin: 0.5rem 0 0;
    font-size: 0.78rem;
}

/* --- Habillage ApexCharts (tooltips assortis, fond transparent) -------------- */

.apexcharts-canvas {
    background: transparent !important;
}

.apexcharts-tooltip.apexcharts-theme-dark,
.apexcharts-tooltip.apexcharts-theme-light {
    background: var(--vigie-surface-opaque) !important;
    border: 1px solid var(--vigie-bordure-forte) !important;
    border-radius: var(--vigie-rayon-petit);
    box-shadow: 0 6px 20px var(--vigie-ombre-forte) !important;
    font-family: var(--vigie-police-mono);
    font-size: 0.75rem;
    color: var(--vigie-texte);
}

.apexcharts-tooltip-title {
    background: transparent !important;
    border-bottom: 1px solid var(--vigie-bordure) !important;
    font-family: var(--vigie-police-mono) !important;
}

/* --- Panneau « À propos » ---------------------------------------------------- */

/* L'identité de La Vigie (signature, stack, build servi, liens externes) tenait
   un bandeau permanent en bas de chaque vue pour une information qu'on lit une
   fois par trimestre. Elle se déplie maintenant depuis le badge « la-vigie » de
   la barre d'état — zéro pixel consommé au repos.

   Ancré à GAUCHE, contre son badge, et jamais plus haut que la barre d'état
   (bottom) : à 380 px comme à 1600, il ne sort pas de l'écran et ne recouvre
   pas le pouls de la supervision. La hauteur est bornée au viewport, le
   débordement défile DANS le panneau. */
.vigie-apropos-panneau {
    position: fixed;
    bottom: var(--vigie-h-etat);
    left: 0.4rem;
    width: min(26rem, calc(100vw - 0.8rem));
    max-height: calc(100dvh - var(--vigie-h-etat) - 0.8rem);
    overflow-y: auto;
    z-index: 71;
    padding: 0.9rem 1.05rem;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    background: var(--vigie-surface-opaque);
    border: 1px solid var(--vigie-bordure-forte);
    border-radius: var(--vigie-rayon) var(--vigie-rayon) 0 0;
    font-size: 0.82rem;
    color: var(--vigie-texte-2);
    box-shadow: 0 -8px 28px var(--vigie-ombre-forte);
}

.vigie-apropos-signature {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}

.vigie-apropos-signature strong {
    color: var(--vigie-texte);
    font-weight: 600;
}

.vigie-apropos-stack {
    font-family: var(--vigie-police-mono);
    font-size: 0.72rem;
    letter-spacing: 0.04em;
    color: var(--vigie-texte-3);
}

.vigie-apropos-build {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem;
}

/* Les pills de build sont des liens : même cartouche, survol accentué. */
.vigie-apropos-build a.vigie-badge-mono {
    text-decoration: none;
    transition: border-color var(--vigie-transition), color var(--vigie-transition);
}

.vigie-apropos-build a.vigie-badge-mono:hover {
    border-color: var(--vigie-bordure-vive);
    color: var(--vigie-texte);
}

.vigie-apropos-liens {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.9rem;
    padding-top: 0.6rem;
    border-top: 1px solid var(--vigie-bordure);
}

.vigie-apropos-liens a {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    color: var(--vigie-texte-3);
    text-decoration: none;
    transition: color var(--vigie-transition);
}

.vigie-apropos-liens a:hover {
    color: var(--vigie-accent);
}

.vigie-nav-ico {
    font-size: 0.9rem !important;
    width: 0.9rem;
    height: 0.9rem;
    opacity: 0.75;
}

/* --- KPI agrégés (cartes et vue site) --------------------------------------- */

/* Couleurs de valeur partagées (taux CI, etc.) : mêmes teintes que les pastilles. */
.vigie-val-ok {
    color: var(--vigie-ok);
}

.vigie-val-attention {
    color: var(--vigie-attention);
}

.vigie-val-ko {
    color: var(--vigie-ko);
}

.vigie-val-inconnu {
    color: var(--vigie-texte-3);
}

/* Bandeau KPI compact des cartes : dispo, p95, visiteurs, TLS sur une ligne. */
.vigie-carte-kpi {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem 1rem;
    padding-top: 0.2rem;
}

.vigie-kpi-mesure {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
}

.vigie-kpi-valeur {
    font-family: var(--vigie-police-mono);
    font-size: 1.05rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    line-height: 1.1;
}

/* Badge de disponibilité : gros pourcentage coloré + fenêtre en dessous. */
.vigie-uptime {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
}

.vigie-uptime-valeur {
    font-family: var(--vigie-police-mono);
    font-size: 1.05rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    line-height: 1.1;
}

.vigie-uptime-ok .vigie-uptime-valeur {
    color: var(--vigie-ok);
}

.vigie-uptime-attention .vigie-uptime-valeur {
    color: var(--vigie-attention);
}

.vigie-uptime-ko .vigie-uptime-valeur {
    color: var(--vigie-ko);
}

.vigie-uptime-inconnu .vigie-uptime-valeur {
    color: var(--vigie-texte-3);
}

/* Chip d'échéance TLS : cartouche coloré par niveau. */
.vigie-chip-tls {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    font-family: var(--vigie-police-mono);
    font-size: 0.72rem;
    padding: 0.08rem 0.4rem;
    border-radius: var(--vigie-rayon-petit);
    border: 1px solid var(--vigie-bordure);
    white-space: nowrap;
}

.vigie-chip-tls .vigie-badge-ico {
    font-size: 0.85rem !important;
    width: 0.85rem;
    height: 0.85rem;
}

.vigie-chip-tls-ok {
    color: var(--vigie-texte-2);
}

.vigie-chip-tls-attention {
    color: var(--vigie-attention);
    border-color: color-mix(in srgb, var(--vigie-attention) 45%, transparent);
    background: color-mix(in srgb, var(--vigie-attention) var(--vigie-plaque), transparent);
}

.vigie-chip-tls-ko {
    color: var(--vigie-ko);
    border-color: color-mix(in srgb, var(--vigie-ko) 45%, transparent);
    background: color-mix(in srgb, var(--vigie-ko) var(--vigie-plaque), transparent);
}

/* Mini-série de visiteurs (barres CSS) : posée pleine largeur sous les KPI. La
   hauteur est la MÊME avec ou sans barres — c'est ce qui garde les cartes de la
   grille alignées entre elles. */
.vigie-vis-spark {
    display: flex;
    align-items: flex-end;
    gap: 2px;
    height: 26px;
    width: 100%;
}

.vigie-vis-barre,
.vigie-vis-trou {
    flex: 1 1 0;
    min-width: 2px;
}

.vigie-vis-barre {
    background: var(--vigie-accent);
    border-radius: 1px 1px 0 0;
    opacity: 0.75;
}

/* Journée SANS collecte : la colonne entière, hachurée. Même glyphe et même
   encre que les journées muettes de BandeJours (var(--vigie-texte-3)) — c'est
   le même « on ne sait pas » sur une autre mesure, et deux dessins pour une
   seule idée obligeraient l'opérateur à apprendre deux vocabulaires.

   Pleine hauteur, et sans se peindre en accent : la hauteur ne peut pas dire
   une quantité qu'on n'a pas mesurée, alors elle ne dit rien du tout — la
   colonne est simplement barrée. La teinte neutre l'exclut de la série bleue,
   la hachure la sépare au glyphe. PAS D'OPACITÉ RÉDUITE : à 0,3 le trait tombe
   à 1,59:1 sur la surface, sous le plancher de 3:1 des éléments graphiques
   porteurs de sens — estomper une information, c'est l'effacer.

   Dégradé répété et non motif SVG : cet instrument est en HTML/CSS (voir
   SparklineVisiteurs), et un `fill` SVG ne sait pas prendre un dégradé CSS. Le
   piège inverse vaut là-bas — voir le commentaire de .vigie-bande-jour. */
.vigie-vis-trou {
    height: 100%;
    background: repeating-linear-gradient(
        45deg,
        var(--vigie-texte-3) 0 1.4px,
        transparent 1.4px 4px);
}

/* Journée à zéro : un trait posé sur la ligne de base, pas une petite barre.
   Elle garde la MÊME encre que les jours pleins — l'estomper la ferait passer
   sous le seuil de contraste, c'est-à-dire l'effacer, et une journée à zéro est
   une mesure. Ce qui la sépare est sa forme : hauteur fixe de 2 px et pas de
   chapeau arrondi. L'audience ne rend aucun verdict : on reste sur l'accent,
   jamais sur une teinte de rôle. */
.vigie-vis-zero {
    border-radius: 1px;
}

/* Rien à tracer : la bande garde sa hauteur et laisse la place au composant
   Rien, qui dit LAQUELLE des trois causes. Une rangée de traits identiques
   occuperait le même espace en n'informant de rien. */
.vigie-vis-vide {
    display: flex;
    align-items: center;
    height: 26px;
    width: 100%;
    font-family: var(--vigie-police-mono);
    font-size: 0.72rem;
}

/* Groupes d'indicateurs de la vue site : un titre, des instruments dessous. */
.vigie-kpi-groupe {
    display: flex;
    flex-direction: column;
    gap: 0.65rem;
    min-width: 0;
}

/* Devenu un vrai <h3> : ces quatre intitulés découpent la rangée d'instruments,
   et un lecteur d'écran doit pouvoir sauter de l'un à l'autre. La marge est
   remise à zéro — la grammaire visuelle ne bouge pas d'un pixel, seule la
   sémantique est réparée. */
.vigie-kpi-groupe-titre {
    margin: 0;
    font-weight: 500;
}

/* Le titre du groupe et le sélecteur de fenêtre sur la MÊME ligne : le réglage
   est contre ce qu'il commande, pas au-dessus de la page. wrap parce qu'à 380 px
   « Disponibilité » et quatre touches ne tiennent pas sur un rang — elles
   passent dessous plutôt que de déborder. */
.vigie-kpi-groupe-tete {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.35rem 0.8rem;
    min-width: 0;
}

.vigie-bande-fenetres {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
}

/* La source de la bande, sous la bande : un rang de texte atone, taille de
   légende — il se lit quand on le cherche et ne dispute rien aux instruments. */
.vigie-bande-source {
    margin: 0;
    font-size: 0.72rem;
    line-height: 1.35;
}

.vigie-kpi-tuiles {
    display: flex;
    flex-wrap: wrap;
    gap: 1.2rem;
}

/* Disponibilité : le cadran de l'ÉTAT (24 h) et la bande de l'HISTOIRE (30 j)
   côte à côte, la bande alignée sur le bas du cadran — c'est le pied du cadran
   qui porte son libellé, et les deux étiquettes doivent se lire sur la même
   ligne. La bande prend le reste : elle a trente barres à loger, le cadran a une
   largeur fixe. */
.vigie-kpi-dispo {
    display: flex;
    /* NOWRAP, et c'est mesuré : à laisser la bande passer sous le cadran, le
       groupe « Disponibilité » montait de 106 à 164 px et emportait avec lui
       toute la rangée d'instruments — 46 px de bandeau sur la vue la plus
       consultée, pour deux instruments qui tiennent très bien côte à côte. La
       bande, dont le SVG est en width 100 %, se contente de rétrécir. */
    flex-wrap: nowrap;
    align-items: flex-end;
    gap: 0.6rem 1rem;
    min-width: 0;
}

/* Les deux jauges de latence s'empilent et partagent la même largeur : côte à
   côte, deux barres de charge se lisent comme une seule barre en deux morceaux. */
.vigie-kpi-jauges {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    min-width: 11rem;
    max-width: 16rem;
}

.vigie-kpi-tuile {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    min-width: 4.5rem;
}

.vigie-kpi-tuile-large {
    min-width: 9rem;
}

.vigie-kpi-tuile-valeur {
    font-family: var(--vigie-police-mono);
    font-size: 1.3rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    line-height: 1.15;
    letter-spacing: -0.02em;
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
}

.vigie-visiteurs-entete {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1.6rem;
    margin-bottom: 0.9rem;
}

/* Le lien vers le tableau de bord détaillé se pousse en bout de ligne. */
.vigie-lien-analytics {
    margin-left: auto;
    font-size: 0.86rem;
}

/* --- Tableau de bord d'audience (page Analytics) ---------------------------- */

/* Choix du site : la seule rescapée de l'ancienne barre de sélection, et elle
   n'a plus de barre — elle tient dans l'en-tête de page, en face du titre (la
   période est montée en zone D de la verrière, le pas est descendu au cartouche
   de sa courbe). Les onglets défilent horizontalement plutôt que de casser
   l'en-tête sur trois rangs.

   .vigie-entete-onglets est le MÊME objet sous son nom générique : une rangée
   d'onglets logée dans l'en-tête d'une page. /configuration l'emploie depuis le
   lot 11 pour ses quatre onglets de réglage. Un second bloc de règles identique
   aurait été exactement le doublon que ce lot retire ailleurs. */
.vigie-analytics-sites,
.vigie-entete-onglets {
    display: flex;
    align-items: center;
    gap: 0.3rem;
    min-width: 0;
    overflow-x: auto;
    /* Le liseré de focus d'un onglet ne doit pas être rogné par le défilement. */
    padding: 2px;
}

/* SAUF QUE LES QUATRE ONGLETS DE RÉGLAGE REPLIENT AU LIEU DE DÉFILER, et c'est
   mesuré : à 380 px ils valent 549 px pour une bande de 346. Avec le défilement
   hérité, ouvrir /configuration?onglet=apparence affichait le panneau Apparence
   pendant que la bande, restée à gauche, montrait « Alertes Discord » en tête —
   un repère qui contredit le contenu, c'est-à-dire le voyant qui ment. Deux
   rangs coûtent 38 px ; le lot en a rendu bien plus en retirant la ligne de
   recensement et la barre d'outils MudTabs. /analytics garde le défilement :
   huit sites y feraient trois rangs, et son H1 dit déjà quel site est ouvert. */
.vigie-entete-onglets {
    flex-wrap: wrap;
    overflow-x: visible;
}

/* Onglets de sélection (site, période) : des TOUCHES de console. Même relief que
   .vigie-commande — tôle, biseau haut, appui enfoncé — mais à la taille d'un
   onglet, parce qu'il y en a huit sur une ligne. */
.vigie-onglet {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.3rem 0.7rem;
    font-size: 0.84rem;
    color: var(--vigie-texte-2);
    text-decoration: none;
    background-color: var(--vigie-badge-fond);
    background-image: linear-gradient(180deg, var(--vigie-metal-2) 0%, var(--vigie-metal-1) 100%);
    border: 1px solid var(--vigie-coque-arete);
    border-radius: var(--vigie-rayon-petit);
    box-shadow: inset 0 1px 0 var(--vigie-biseau);
    white-space: nowrap;
    transition: color var(--vigie-transition), border-color var(--vigie-transition),
        box-shadow var(--vigie-transition), transform var(--vigie-transition);
}

.vigie-onglet:hover {
    color: var(--vigie-texte);
    border-color: var(--vigie-bordure-vive);
    box-shadow: inset 0 1px 0 var(--vigie-biseau), 0 0 0 1px var(--vigie-reglette);
}

.vigie-onglet:active {
    transform: translateY(1px);
    box-shadow: inset 0 2px 4px var(--vigie-ombre-forte);
}

/* LE HALO DU FOCUS, RENDU À LA TOUCHE. L'anneau de :focus-visible pose son trait
   ET un halo en box-shadow ; la touche écrase ce box-shadow avec son biseau
   (même spécificité, plus bas dans la feuille), et il ne restait donc que le
   trait. On réécrit les deux couches ensemble plutôt que de retirer le biseau :
   une touche sans son arête éclairée aurait l'air enfoncée dès qu'on la
   survole au clavier. Après :hover et :active, pour gagner à égalité. */
.vigie-onglet:focus-visible {
    box-shadow: inset 0 1px 0 var(--vigie-biseau),
        0 0 0 6px color-mix(in srgb, var(--vigie-accent) 22%, transparent);
}

/* Touche ENGAGÉE : rétroéclairée. Le fond teinté passe par-dessus la tôle et le
   biseau disparaît — une touche enfoncée n'a plus d'arête éclairée en haut. */
.vigie-onglet-actif {
    color: var(--vigie-accent);
    border-color: var(--vigie-accent);
    background-image: linear-gradient(180deg,
        color-mix(in srgb, var(--vigie-accent) 18%, transparent) 0%,
        color-mix(in srgb, var(--vigie-accent) 9%, transparent) 100%);
    box-shadow: inset 0 1px 4px color-mix(in srgb, var(--vigie-accent) 22%, transparent);
}

.vigie-onglet-periode {
    font-family: var(--vigie-police-mono);
    font-variant-numeric: tabular-nums;
}

/* --- Navigation de section (les trois pages de /configuration) ---------------
   La MÊME touche que ci-dessus — tôle, biseau, rétroéclairage à l'état engagé,
   donc aucune couleur d'identité de plus — mais sur deux lignes : le libellé,
   puis ce que la destination vaut aujourd'hui. Ce second rang est la raison
   d'être de l'objet ; sans lui, « Second facteur » est un mot de menu, avec lui
   c'est un voyant.

   flex: 1 1 13rem est ce qui la rend juste aux deux bouts sans point de rupture
   à écrire : à 1600 px les trois entrées se partagent la largeur sur un rang, à
   380 px la base dépasse la bande (346 px) et chacune prend son propre rang.
   min-width: 0 est obligatoire — sans lui, une base flex de 13rem ne se laisse
   pas comprimer et c'est le débordement horizontal de la page. */
.vigie-nav-section {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-bottom: 1rem;
    /* Le liseré de focus d'une entrée ne doit pas être rogné par le bord. */
    padding: 2px;
}

/* La touche hérite de .vigie-onglet : on ne redéfinit QUE ce qui change. */
.vigie-nav-section-lien {
    flex: 1 1 13rem;
    min-width: 0;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.1rem;
    padding: 0.45rem 0.7rem;
    /* .vigie-onglet interdit le retour à la ligne : un libellé de trois mots
       suivi de sa mesure en a besoin. */
    white-space: normal;
}

/* Ni couleur ni taille propres : la graisse suffit à séparer le nom de la
   mesure, et la couleur reste celle de la touche — donc l'accent quand elle est
   engagée, ce qu'un color: en dur aurait écrasé. */
.vigie-nav-section-tete {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-weight: 600;
}

/* La mesure : chiffrée comme toutes les lectures de la coque, et cassable —
   « inopérant · soupape ouverte » ne tient pas sur une entrée de 380 px. */
.vigie-nav-section-etat {
    font-family: var(--vigie-police-mono);
    font-size: 0.72rem;
    line-height: 1.3;
    color: var(--vigie-texte-3);
    overflow-wrap: anywhere;
}

/* Cartes de tête : grille auto-ajustée, une par métrique. */
.vigie-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
    gap: 0.75rem;
}

.vigie-stat {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    padding: 0.9rem 1.05rem;
}

.vigie-stat-valeur {
    font-family: var(--vigie-police-mono);
    font-size: 1.8rem;
    font-weight: 600;
    line-height: 1.1;
    letter-spacing: -0.03em;
    font-variant-numeric: tabular-nums;
}

/* L'AFFICHEUR de la carte : la valeur en segments, taillée sur la chaîne. La
   hauteur est fixe et la largeur suit le texte (viewBox calé sur la chaîne,
   preserveAspectRatio en xMin) — c'est ce qui garde des chiffres de MÊME taille
   d'une carte à l'autre, quel que soit le nombre de caractères. Calé à gauche
   comme l'était le grand nombre : la rangée se lit sur une colonne de départs
   alignés. */
.vigie-stat-ecran {
    display: block;
    line-height: 0;
    cursor: help;
}

.vigie-stat-sept {
    display: block;
    width: 100%;
    height: 1.9rem;
    color: var(--vigie-texte);
}

/* Comme dans le cadran : le texte ne porte que la valeur lisible et son
   identifiant de test, l'afficheur porte le dessin. */
.vigie-stat-sept-valeur {
    font-family: var(--vigie-police-mono);
    font-size: 12px;
    fill: transparent;
    dominant-baseline: middle;
}

/* La mini-série de comparaison, entre la valeur et la flèche. Positionnée pour
   porter le repère de la période comparée, qui la traverse. */
.vigie-carte-serie {
    position: relative;
    margin-top: 0.15rem;
}

/* Le niveau moyen de la fenêtre comparée, en travers de la bande. Pointillé et
   en teinte de texte sourde : l'audience ne rend aucun verdict, un repère vert
   ou rouge en rendrait un. Il ne capte pas la souris — l'infobulle des barres
   passe dessous. */
.vigie-carte-serie-repere {
    position: absolute;
    left: 0;
    right: 0;
    height: 0;
    border-top: 1px dashed var(--vigie-texte-3);
    pointer-events: none;
}

.vigie-stat-variation {
    display: inline-flex;
    align-items: center;
    gap: 0.2rem;
    font-family: var(--vigie-police-mono);
    font-size: 0.78rem;
    font-variant-numeric: tabular-nums;
    cursor: help;
    /* Collée au BAS de la carte : deux des cinq cartes n'ont pas de mini-série
       (les taux), et sans ce calage leurs flèches flotteraient à mi-hauteur au
       milieu d'une rangée qui, elle, s'aligne. */
    margin-top: auto;
}

.vigie-stat-fleche {
    font-size: 1.05rem !important;
    width: 1.05rem;
    height: 1.05rem;
}

.vigie-variation-ok {
    color: var(--vigie-ok);
}

.vigie-variation-ko {
    color: var(--vigie-ko);
}

.vigie-variation-neutre {
    color: var(--vigie-texte-3);
}

/* Grilles de panneaux : deux colonnes pour les classements, trois pour les
   donuts. Elles retombent à une colonne quand la place manque.

   Le plancher des pistes est ÉCRÊTÉ À LA LARGEUR DISPONIBLE (min(19rem, 100%)) :
   un minmax(19rem, …) nu promet 19 rem — soit 323 px à la racine de 106,25 % —
   même quand la colonne de page n'en offre que 286 à 320 px de viewport, et la
   piste unique débordait alors de 15 px hors de l'écran (#86), seule source du
   défilement horizontal de /analytics. Écrêté, le plancher vaut « 19 rem si la
   place y est, toute la place sinon » : même bascule à une colonne qu'avant,
   mais jamais plus large que la page. */
.vigie-grille-deux {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(19rem, 100%), 1fr));
    gap: 0.75rem;
}

.vigie-grille-trois {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
    gap: 0.75rem;
}

.vigie-classement,
.vigie-donut,
.vigie-carte-monde,
.vigie-calendrier {
    padding: 0.95rem 1.05rem 0.9rem;
    min-width: 0;
}

.vigie-classement-entete {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
    margin-bottom: 0.6rem;
}

.vigie-classement-vide {
    margin: 0.4rem 0 0.6rem;
}

.vigie-table-classement {
    background-color: transparent !important;
}

.vigie-table-classement .mud-table-container {
    background: transparent;
}

.vigie-table-classement th {
    font-family: var(--vigie-police-mono) !important;
    color: var(--vigie-texte-3) !important;
    font-size: 0.66rem !important;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    border-bottom: 1px solid var(--vigie-bordure) !important;
    white-space: nowrap;
}

.vigie-table-classement td {
    border-bottom: 1px solid var(--vigie-bordure) !important;
    font-size: 0.84rem;
    /* Les colonnes chiffrées ne doivent jamais casser « 49,5 % » en deux lignes. */
    white-space: nowrap;
}

.vigie-table-classement tr:last-child td {
    border-bottom: none !important;
}

/* Cellule à jauge : la barre de part vit en fond, le libellé passe devant et
   se tronque proprement plutôt que d'élargir la table. */
.vigie-classement-cellule {
    position: relative;
    max-width: 0;
    width: 100%;
}

.vigie-classement-jauge {
    position: absolute;
    left: 0;
    top: 3px;
    bottom: 3px;
    border-radius: 2px;
    background: color-mix(in srgb, var(--vigie-accent) 16%, transparent);
}

.vigie-classement-etiquette {
    position: relative;
    display: block;
    padding-left: 0.3rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-family: var(--vigie-police-mono);
    font-size: 0.8rem;
}

.vigie-donut {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.4rem;
}

.vigie-donut .vigie-sous-titre {
    align-self: flex-start;
}

.vigie-donut-squelette {
    margin: 1.2rem auto;
    opacity: 0.5;
}

/* Carte du monde : les couleurs viennent du thème, un fond légèrement contrasté
   distingue l'océan de la surface du panneau.

   BRIDÉE EN LARGEUR (lot 10). Étirée sur toute la section, elle occupait près de
   quatre cents pixels de haut sur un desktop pour dire « surtout la France » —
   son intérêt est secondaire et son encombrement était celui d'une mesure de
   tête. À 34 rem elle reste lisible (les bulles européennes ne se recouvrent
   pas) et rend la moitié de sa hauteur. Centrée, parce qu'un fond de carte
   collé au bord gauche d'un panneau vide se lit comme un défaut de mise en page. */
.vigie-monde {
    display: block;
    width: 100%;
    max-width: 34rem;
    margin-inline: auto;
    height: auto;
    border-radius: var(--vigie-rayon-petit);
    background: color-mix(in srgb, var(--vigie-accent) 6%, transparent);
}

/* La légende des provenances : elle porte les CHIFFRES, la carte ne porte que
   les positions. C'est elle qui reste quand la carte tombe au doigt. */
.vigie-monde-legende {
    list-style: none;
    margin: 0.7rem 0 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: 0.1rem 0.9rem;
    font-size: 0.78rem;
}

.vigie-monde-legende li {
    display: flex;
    align-items: baseline;
    gap: 0.4rem;
    min-width: 0;
}

.vigie-monde-pays {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* La pastille reprend l'encre des bulles : c'est ce qui rattache une ligne de
   légende à un disque de la carte quand les deux sont visibles. */
.vigie-monde-pastille {
    flex: none;
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 50%;
    background: var(--vigie-accent);
    opacity: 0.75;
}

/* Un pays sans position connue n'a pas de bulle : sa pastille est CREUSE, sinon
   la légende promettrait un disque que la carte ne montre pas. */
.vigie-monde-pastille-absente {
    background: none;
    border: 1px solid var(--vigie-texte-3);
}

.vigie-monde-terres path {
    fill: color-mix(in srgb, var(--vigie-texte-3) 28%, transparent);
    stroke: color-mix(in srgb, var(--vigie-texte-3) 45%, transparent);
    stroke-width: 0.5;
    stroke-linejoin: round;
}

/* Les mers intérieures reprennent exactement le fond de la carte : elles
   « effacent » la terre plutôt que de se dessiner en creux. */
.vigie-monde-mers path {
    fill: color-mix(in srgb, var(--vigie-accent) 6%, transparent);
    stroke: none;
}

.vigie-monde-bulles circle {
    fill: var(--vigie-accent);
    stroke: var(--vigie-accent-2);
    stroke-width: 0.6;
}

/* Calendrier de fréquentation : défilement horizontal en étroit plutôt qu'un
   écrasement des cases, qui rendrait les paliers illisibles. */
.vigie-calendrier-defilement {
    overflow-x: auto;
    padding-bottom: 0.3rem;
}

/* Le pas de la grille : une seule variable pour la case et la gouttière, pour
   que les libellés de mois restent alignés sur les colonnes de semaines. */
.vigie-cal {
    --vigie-cal-pas: 14px;
    display: flex;
    gap: 4px;
    width: max-content;
}

.vigie-cal-initiales {
    display: grid;
    grid-template-rows: repeat(7, var(--vigie-cal-pas));
    align-items: center;
    /* Décalage vers le bas de la hauteur des libellés de mois : sans lui, les
       initiales de jours seraient décalées d'une ligne par rapport aux cases. */
    padding-top: 1rem;
}

.vigie-cal-initiales span {
    font-family: var(--vigie-police-mono);
    font-size: 0.62rem;
    color: var(--vigie-texte-3);
    line-height: 1;
}

.vigie-cal-mois,
.vigie-cal-grille {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: var(--vigie-cal-pas);
}

.vigie-cal-mois {
    height: 1rem;
}

/* Un nom de mois est plus large qu'une colonne : il déborde à droite, comme
   sur un calendrier de contributions. */
.vigie-cal-mois span {
    font-family: var(--vigie-police-mono);
    font-size: 0.62rem;
    color: var(--vigie-texte-3);
    white-space: nowrap;
    line-height: 1;
}

.vigie-cal-grille {
    grid-template-rows: repeat(7, var(--vigie-cal-pas));
}

.vigie-cal-case {
    width: calc(var(--vigie-cal-pas) - 3px);
    height: calc(var(--vigie-cal-pas) - 3px);
    border-radius: 2px;
    border: 1px solid color-mix(in srgb, var(--vigie-texte-3) 20%, transparent);
    cursor: help;
}

.vigie-cal-pastille {
    display: inline-block;
    width: 0.7rem;
    height: 0.7rem;
    border-radius: 2px;
    border: 1px solid color-mix(in srgb, var(--vigie-texte-3) 20%, transparent);
}

/* Quatre paliers d'intensité sur l'accent, plus un niveau « aucune visite ».
   color-mix garde une seule source de vérité : la couleur d'accent du thème. */
.vigie-cal-n0 {
    fill: color-mix(in srgb, var(--vigie-texte-3) 14%, transparent);
    background: color-mix(in srgb, var(--vigie-texte-3) 14%, transparent);
}

.vigie-cal-n1 {
    fill: color-mix(in srgb, var(--vigie-accent) 28%, transparent);
    background: color-mix(in srgb, var(--vigie-accent) 28%, transparent);
}

.vigie-cal-n2 {
    fill: color-mix(in srgb, var(--vigie-accent) 50%, transparent);
    background: color-mix(in srgb, var(--vigie-accent) 50%, transparent);
}

.vigie-cal-n3 {
    fill: color-mix(in srgb, var(--vigie-accent) 74%, transparent);
    background: color-mix(in srgb, var(--vigie-accent) 74%, transparent);
}

.vigie-cal-n4 {
    fill: var(--vigie-accent);
    background: var(--vigie-accent);
}

/* Journée SANS collecte : la case hachurée, jamais un palier de l'échelle. Même
   encre, même angle et même pas que .vigie-vis-trou et que la hachure de
   BandeJours — c'est le même « on ne sait pas » sur une troisième mesure, et
   l'opérateur n'a qu'un vocabulaire à apprendre.
   Le pas est resserré (3 px au lieu de 4) : une case de 11 px ne loge que trois
   traits, et deux traits se lisent comme une croix, pas comme une hachure.
   PAS D'OPACITÉ RÉDUITE : estomper une information, c'est l'effacer. */
.vigie-cal-trou {
    background: repeating-linear-gradient(
        45deg,
        var(--vigie-texte-3) 0 1.1px,
        transparent 1.1px 3px);
}

/* La pastille « non collecté » de la légende se sépare des cinq paliers : sans
   cette respiration elle se lit comme un sixième cran de l'échelle. */
.vigie-cal-trou-legende {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    margin-left: 0.6rem;
}

.vigie-cal-legende {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.3rem;
    margin-top: 0.6rem;
}

.vigie-cal-max {
    margin-left: auto;
}

@media (max-width: 640px) {
    .vigie-stat-valeur {
        font-size: 1.55rem;
    }

    /* L'en-tête passe en colonne sous 640 px (titre puis onglets) : les onglets
       prennent toute la largeur et défilent, au lieu de se coller au titre. */
    .vigie-analytics-sites {
        width: 100%;
    }

    /* LA CARTE TOMBE AU DOIGT — même seuil que le cap. Un fond de 720 unités
       ramené à 340 px ne montre plus de continents mais des taches, et il
       coûterait cent-quarante pixels de défilement pour une information que la
       légende juste dessous donne en toutes lettres. C'est la carte qui tombe,
       PAS la section : la provenance reste lue, elle change seulement de forme. */
    .vigie-monde {
        display: none;
    }

    /* Une seule colonne de légende : à cette largeur, deux colonnes coupent les
       noms de pays au troisième caractère. */
    .vigie-monde-legende {
        grid-template-columns: 1fr;
    }
}

/* --- Panneau des alertes récentes (vue site) -------------------------------- */

.vigie-alertes {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
}

/* Apparié au graphe de latence (≥ 1200 px), le panneau est BORNÉ à la hauteur
   de son voisin : c'est lui qui étirait la rangée, et la grille alignant ses
   rangées sur la plus haute section, chaque alerte de plus creusait d'autant le
   vide sous le graphe. Le plafond vaut la hauteur du cadre du graphe (tracé de
   260 px + habillage, mesuré au navigateur) ; au-delà, la liste défile dans son
   cadre — c'est un journal, les plus récentes sont déjà en tête. En colonne
   unique, aucune rangée à étirer : la liste reprend sa hauteur naturelle. */
@media (min-width: 1200px) {
    .vigie-alertes {
        max-height: 18.5rem;
        overflow-y: auto;
    }
}

.vigie-alerte {
    display: flex;
    gap: 0.7rem;
    padding: 0.8rem 0.2rem;
    border-bottom: 1px dashed var(--vigie-bordure);
}

.vigie-alerte:last-child {
    border-bottom: none;
}

.vigie-alerte-resolue {
    opacity: 0.72;
}

.vigie-alerte-pastille {
    flex-shrink: 0;
    width: 0.55rem;
    height: 0.55rem;
    border-radius: 50%;
    margin-top: 0.35rem;
}

.vigie-alerte-ok {
    background: var(--vigie-ok);
}

.vigie-alerte-attention {
    background: var(--vigie-attention);
}

.vigie-alerte-ko {
    background: var(--vigie-ko);
}

.vigie-alerte-corps {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    min-width: 0;
}

.vigie-alerte-tete {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.vigie-alerte-type {
    font-family: var(--vigie-police-mono);
    font-weight: 600;
    font-size: 0.9rem;
}

.vigie-alerte-etat-active {
    color: var(--vigie-attention);
    border-color: color-mix(in srgb, var(--vigie-attention) 45%, transparent);
}

.vigie-alerte-etat-resolue {
    color: var(--vigie-ok);
    border-color: color-mix(in srgb, var(--vigie-ok) 40%, transparent);
}

.vigie-alerte-message {
    margin: 0;
    font-size: 0.86rem;
    line-height: 1.55;
    color: var(--vigie-texte-2);
    white-space: pre-line;
    word-break: break-word;
}

.vigie-alerte-meta {
    margin: 0;
}

/* --- Visualiseur de logs CI -------------------------------------------------- */

.vigie-logs-entete {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.7rem 1.2rem;
    padding: 0.8rem 1.1rem;
    margin-bottom: 1.2rem;
}

.vigie-logs-identite {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    min-width: 0;
}

.vigie-logs-identite .vigie-titre {
    font-size: 1.2rem;
}

.vigie-logs-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.86rem;
}

/* Sélecteur de job : des onglets carrés, un par job du run. */
.vigie-logs-jobs {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
}

.vigie-logs-job {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    padding: 0.35rem 0.75rem;
    font: inherit;
    font-size: 0.86rem;
    color: var(--vigie-texte-2);
    background: var(--vigie-badge-fond);
    border: 1px solid var(--vigie-bordure);
    border-radius: var(--vigie-rayon-petit);
    cursor: pointer;
    transition: border-color var(--vigie-transition), color var(--vigie-transition);
}

.vigie-logs-job:hover {
    color: var(--vigie-texte);
    border-color: var(--vigie-bordure-forte);
}

.vigie-logs-job-actif {
    color: var(--vigie-texte);
    border-color: var(--vigie-accent);
    background: color-mix(in srgb, var(--vigie-accent) var(--vigie-plaque), transparent);
}

.vigie-logs-job-nom {
    font-family: var(--vigie-police-mono);
    font-size: 0.8rem;
}

.vigie-logs-job-pastille {
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 50%;
    flex-shrink: 0;
    background: var(--vigie-pastille-neutre);
}

.vigie-logs-job-success {
    background: var(--vigie-ok);
}

.vigie-logs-job-failure,
.vigie-logs-job-cancelled,
.vigie-logs-job-timed_out {
    background: var(--vigie-ko);
}

.vigie-logs-job-in_progress,
.vigie-logs-job-queued {
    background: var(--vigie-attention);
}

/* Texte-2 et pas texte-3 : la durée est posée sur le fond teinté de l'onglet
   actif, où le gris le plus pâle passait sous AA. */
.vigie-logs-job-duree {
    font-family: var(--vigie-police-mono);
    font-size: 0.72rem;
    color: var(--vigie-texte-2);
}

/* LA RECHERCHE DE /logs, MONTÉE EN ZONE C DE LA VERRIÈRE (lot 12).
   ============================================================================

   La barre d'outils locale — .vigie-logs-barre, collante sous la bande — a
   disparu : elle faisait le travail de la zone C (les commandes de la vue
   courante) à côté de la zone C. Ce qu'elle coûtait, mesure faite au navigateur
   sur un run réel de 448 lignes : 70 px de haut à 1600 px, et 269 px à 380 px,
   où elle se repliait sur quatre rangs — le tiers d'un écran de téléphone,
   au-dessus du terminal.

   CE QUI EST MONTÉ, ET CE QUI EST RESTÉ. La zone C prend les deux commandes qui
   RETIENNENT des lignes : la recherche et le filtre d'erreurs. Les cinq autres
   (horodatages, repliage, copier, télécharger, rafraîchir) n'en retiennent
   aucune — elles habillent la sortie, l'emportent ou rechargent sa source — et
   elles ont rejoint la zone d'actions que le cartouche « Sortie » offrait déjà :
   aucun bandeau n'a été recréé ailleurs. Le compteur n/N n'est pas monté ; c'est
   de l'INFORMATION, et il vit désormais DANS le champ.

   POURQUOI DEUX, ET PAS SEPT — LA MESURE. Sur /logs à 380 px, la zone D ne porte
   qu'un bouton (la période ne s'affiche pas sur un run, qui est un INSTANT) et le
   cap est déjà réduit à sa feuille, « logs #111 », 70 px. En glissant en tête de
   tribord une sonde de largeur croissante, la feuille reste entière jusqu'à 96 px
   de zone C et se fait rogner dès 100. QUATRE-VINGT-SEIZE PIXELS : deux commandes
   de 44 px et leur gouttière, pas une de plus. À 1600 px la contrainte n'existe
   pas — 900 px de sonde ne touchent toujours pas le cap. C'est donc le doigt qui
   fixe la règle, exactement comme pour la zone D au lot 6.

   D'OÙ LA LOUPE QUI S'OUVRE. Au repos le champ a une largeur NULLE et la commande
   vaut 44 px, comme un bouton. Il s'ouvre au focus, et le RESTE tant qu'un terme
   est saisi — sinon la saisie se cacherait derrière la loupe. À 380 px, une
   recherche en cours ronge le cap : c'est assumé et réversible (on sait où l'on
   est quand on cherche), et la bande, elle, ne grandit d'aucun pixel. */
.vigie-logs-quete {
    /* Padding de gauche d'une commande ; à droite il tombe à zéro, le champ et
       le compteur fournissant leur propre respiration. */
    padding-right: 0;
    gap: 0.25rem;
    overflow: hidden;
}

.vigie-logs-quete-saisie {
    width: 0;
    min-width: 0;
    padding: 0;
    font: inherit;
    color: var(--vigie-texte);
    background: none;
    border: none;
    outline: none;
    transition: width var(--vigie-transition);
}

/* L'ANNEAU DE FOCUS VA SUR L'ENVELOPPE, et pas sur le champ : à largeur nulle,
   celui-ci n'aurait dessiné qu'un trait vertical de deux pixels. Il est posé EN
   DEDANS (offset négatif) parce que la zone C écrête ce qui dépasse — à
   l'extérieur, il serait rogné. Sans cet anneau, la seule prise au clavier de la
   commande n'aurait plus d'indicateur visible : WCAG 2.4.7. */
.vigie-logs-quete:focus-within {
    color: var(--vigie-texte);
    background: var(--vigie-champ-fond);
    border-color: var(--vigie-bordure);
    outline: 2px solid var(--vigie-accent);
    outline-offset: -2px;
}

/* 9 rem tient « ENOENT: no such » : au-delà, on cherche une phrase et plus un
   motif — et chaque rem se prend sur le cap. */
.vigie-logs-quete:focus-within .vigie-logs-quete-saisie,
.vigie-logs-quete-tenue .vigie-logs-quete-saisie {
    width: 9rem;
    padding-right: 0.25rem;
}

/* La croix native de type=search est retirée : Échap efface le champ, et le
   glyphe du navigateur ne suit ni la police ni les teintes de la bande. */
.vigie-logs-quete-saisie::-webkit-search-cancel-button {
    appearance: none;
}

/* Le compteur d'occurrences : de l'INFORMATION, donc au gris atone et jamais à
   la teinte d'une commande. Largeur plancher pour que « 9/12 » ne fasse pas
   sauter les flèches d'un pixel à chaque saut. */
.vigie-logs-quete-compte {
    min-width: 2.6rem;
    padding-right: 0.4rem;
    font-family: var(--vigie-police-mono);
    font-size: 0.68rem;
    color: var(--vigie-texte-3);
    text-align: right;
}

/* UNE COMMANDE ARMÉE — le filtre « erreurs seulement » pendant qu'il retient.
   Elle s'allume à l'ACCENT, la teinte de ce qui est actif dans la bande
   (l'invite du cap, le survol d'un maillon) : le ROUGE reste au coupe-circuit
   des alertes, seul fait d'AVARIE que la bande annonce. Un filtre qui filtre
   n'est pas une avarie.

   BORDURE À PLEINE OPACITÉ, et non à 55 % comme celle du coupe-circuit : mesurée
   au navigateur, la version diluée tombait à 2,4:1 contre la bande EN THÈME
   CLAIR — sous le plancher de 3:1 des éléments d'interface non textuels. À pleine
   opacité elle passe à 4,5:1 dans les deux thèmes, et le contour du bouton reste
   le repère non coloré de l'état (l'aria-pressed et l'infobulle le disent par
   ailleurs). */
.vigie-verriere-commande-armee,
.vigie-verriere-commande-armee:hover,
.vigie-verriere-commande-armee:focus-visible {
    color: var(--vigie-accent);
    border-color: var(--vigie-accent);
    background: color-mix(in srgb, var(--vigie-accent) 14%, transparent);
}

.vigie-logs-squelette {
    border-radius: var(--vigie-rayon);
    margin-bottom: 0.8rem;
    opacity: 0.5;
}

/* Le terminal lui-même : surface opaque, défilement vertical borné, défilement
   horizontal plutôt que césure pour ne jamais casser une trace d'erreur. */
.vigie-terminal {
    background: var(--vigie-terminal-fond);
    color: var(--vigie-terminal-texte);
    border: 1px solid var(--vigie-bordure);
    border-radius: var(--vigie-rayon);
    font-family: var(--vigie-police-mono);
    font-size: 0.8rem;
    line-height: 1.6;
    padding: 0.5rem 0;
    max-height: 70dvh;
    overflow: auto;
    /* Pas d'overscroll-behavior: contain ici — le terminal est plus haut que ce
       qui reste d'écran sous l'en-tête, et bloquer le chaînage empêcherait la
       molette d'atteindre le bas de la page une fois le log parcouru. */
}

.vigie-log-ligne {
    display: flex;
    align-items: flex-start;
    gap: 0.7rem;
    padding: 0 0.9rem 0 0;
    min-height: 1.2em;
    width: max-content;
    min-width: 100%;
}

.vigie-log-ligne:hover {
    background: var(--vigie-terminal-survol);
}

/* Gouttière des numéros : collée à gauche au défilement horizontal, sinon on
   perd le repère de ligne dès qu'on suit une trace un peu large. */
.vigie-log-num {
    position: sticky;
    left: 0;
    flex: 0 0 3.5rem;
    padding: 0 0.5rem 0 0;
    text-align: right;
    color: var(--vigie-terminal-numero);
    background: var(--vigie-terminal-fond);
    border-right: 1px solid var(--vigie-terminal-gouttiere);
    user-select: none;
}

.vigie-log-heure {
    flex: 0 0 auto;
    color: var(--vigie-terminal-numero);
    user-select: none;
    font-variant-numeric: tabular-nums;
}

.vigie-log-texte {
    white-space: pre;
    /* Indentation des groupes imbriqués : une variable posée en style inline,
       pour ne pas générer une classe par niveau. */
    padding-left: calc(var(--vigie-log-profondeur, 0) * 1.1rem);
}

.vigie-log-erreur {
    background: color-mix(in srgb, var(--vigie-ko) var(--vigie-plaque), transparent);
}

.vigie-log-erreur .vigie-log-texte {
    color: var(--vigie-ko);
}

.vigie-log-avertissement .vigie-log-texte {
    color: var(--vigie-attention);
}

.vigie-log-notice .vigie-log-texte {
    color: var(--vigie-accent);
}

/* En-tête de groupe : toute la ligne est le bouton de repliage. */
.vigie-log-bascule {
    display: inline-flex;
    align-items: baseline;
    gap: 0.4rem;
    font: inherit;
    color: var(--vigie-terminal-texte);
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
    text-align: left;
}

.vigie-log-groupe {
    font-weight: 600;
}

.vigie-log-chevron {
    color: var(--vigie-texte-3);
    width: 0.8rem;
    flex-shrink: 0;
}

.vigie-log-badge-erreur {
    font-size: 0.7rem;
    font-weight: 500;
    color: var(--vigie-ko);
    border: 1px solid color-mix(in srgb, var(--vigie-ko) 40%, transparent);
    border-radius: var(--vigie-rayon);
    padding: 0 0.3rem;
    margin-left: 0.5rem;
}

.vigie-log-surlignage {
    background: var(--vigie-terminal-surlignage);
    color: var(--vigie-terminal-texte);
    border-radius: var(--vigie-rayon-petit);
    padding: 0 1px;
}

/* L'occurrence courante se distingue des autres : sans ça, le compteur n/N
   navigue dans le vide. */
.vigie-log-surlignage-actif {
    background: var(--vigie-terminal-surlignage-actif);
    color: #101318;
    outline: 1px solid var(--vigie-texte);
}

.vigie-logs-pied {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.6rem;
    margin-top: 0.6rem;
}

.vigie-logs-total-erreurs {
    color: var(--vigie-ko);
}

@media (max-width: 640px) {
    .vigie-terminal {
        font-size: 0.74rem;
        max-height: 60dvh;
    }

    .vigie-log-num {
        flex-basis: 2.4rem;
    }

    .vigie-log-ligne {
        gap: 0.5rem;
    }
}

/* --- Page « État du système » (/etat) ---------------------------------------- */

/* LE VERDICT (lot 9). Il emprunte le bandeau de tête de la vue site — même
   surface, même filet vertical, même grammaire de lampe et de gros chiffre — et
   reprend jusqu'à sa PHRASE de mise en page : « voilà l'état, et voilà de quoi
   il est fait ». À gauche le verdict, à droite ce qui le démontre ; là-bas
   c'étaient les groupes de KPI, ici c'est la ventilation du compteur.
   .vigie-bandeau-verdict est donc repris SANS retouche — c'est lui qui porte le
   filet et son basculement à l'horizontale sous 900 px. */
.vigie-verdict-corps {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1rem 1.8rem;
}

/* Sauf quand il n'y a rien à sa droite (état inconnu, sans ventilation) : un
   filet qui ne sépare rien est une décoration, et cette coque n'en a pas. */
.vigie-verdict-corps > .vigie-bandeau-verdict:last-child {
    padding-right: 0;
    border-right: none;
}

.vigie-verdict-phrase {
    margin: 0;
    font-size: 1.05rem;
    color: var(--vigie-texte);
    min-width: 0;
}

/* La ventilation : trois chips qui expliquent le chiffre sans le répéter —
   critiques et attentions sont des OCCURRENCES, les familles aveugles sont des
   FAMILLES, et les mots le disent. */
.vigie-verdict-ventilation {
    display: flex;
    flex-wrap: wrap;
    gap: var(--vigie-pas-1);
}

/* La TRACE d'un filtre actif, sur la page que le filtre a vidée. Le contrôle vit
   dans la verrière : cette ligne est le seul endroit où la page peut dire qu'on
   lui a demandé de se taire. Discrète mais jamais atone au point de disparaître —
   c'est l'explication d'un vide, pas une note de bas de page. */
.vigie-filtre-trace {
    margin: 0.7rem 0 0;
    font-size: 0.84rem;
    font-style: italic;
}

/* LA CHRONOLOGIE. Trois colonnes de largeur fixe — quand, qui, quoi — et un
   liseré de gravité à gauche. Ce n'est PAS le terminal de la flotte : celui-là
   est une dalle sombre en column-reverse qui imite un tail -f, et le dupliquer
   dans une colonne de cinq douzièmes n'aurait donné qu'un petit terminal. Ici
   c'est une liste de page, elle se lit de haut en bas comme le reste.
   Bornée en hauteur et défilante : le service en rend jusqu'à soixante, et une
   page dont la moitié est du journal ne répond plus à « qu'est-ce qui brûle ». */
.vigie-chrono {
    list-style: none;
    margin: 0;
    padding: 0.4rem;
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    max-height: 26rem;
    overflow-y: auto;
    overscroll-behavior: contain;
}

.vigie-chrono-ligne {
    display: grid;
    grid-template-columns: 5.5rem 3rem minmax(0, 1fr);
    align-items: baseline;
    gap: 0.5rem;
    padding: 0.3rem 0.45rem 0.3rem 0.6rem;
    border-left: 3px solid transparent;
    border-radius: var(--vigie-rayon-petit);
    font-size: 0.82rem;
    line-height: 1.5;
}

.vigie-chrono-ligne:nth-child(odd) {
    background: color-mix(in srgb, var(--vigie-texte-3) 6%, transparent);
}

.vigie-chrono-ko {
    border-left-color: var(--vigie-ko);
}

.vigie-chrono-attention {
    border-left-color: var(--vigie-attention);
}

.vigie-chrono-ok {
    border-left-color: color-mix(in srgb, var(--vigie-ok) 70%, transparent);
}

.vigie-chrono-info {
    border-left-color: var(--vigie-bordure);
}

.vigie-chrono-quand,
.vigie-chrono-code {
    color: var(--vigie-texte-3);
    font-size: 0.76rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.vigie-chrono-message {
    color: var(--vigie-texte-2);
    overflow-wrap: anywhere;
}

/* En étroit, les trois colonnes deviennent deux lignes : à 380 px, 5,5 rem
   d'horodatage plus 3 rem de code ne laissaient au message que la largeur d'un
   mot, et chaque entrée s'étalait sur six lignes.
   FLEX et non grille : « quand » et « code » doivent se suivre à leur largeur de
   TEXTE. Deux colonnes auto y valaient 150 px chacune — le message, qui les
   enjambe, tire la répartition — et le code partait au milieu de nulle part. */
@media (max-width: 640px) {
    .vigie-chrono-ligne {
        display: flex;
        flex-wrap: wrap;
        column-gap: 0.55rem;
        row-gap: 0.1rem;
    }

    .vigie-chrono-message {
        flex-basis: 100%;
    }
}

/* LE REPLI du catalogue — un <details>/<summary> natif. Le marqueur par défaut
   du navigateur (triangle noir, taille et position hors de notre contrôle) est
   retiré et redessiné en chevron, comme partout ailleurs dans la coque.
   La cible fait 44 px de HAUT ici, et cette fois c'est possible : on est dans le
   flux de la page, pas dans une bande de châssis qu'on paierait sur chaque
   écran. */
.vigie-repli > summary {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    min-height: 44px;
    padding: 0 0.2rem;
    cursor: pointer;
    font-family: var(--vigie-police-mono);
    font-size: 0.82rem;
    color: var(--vigie-texte-2);
    list-style: none;
    user-select: none;
}

/* Safari/WebKit n'obéissent qu'à ce pseudo-élément pour retirer le triangle. */
.vigie-repli > summary::-webkit-details-marker {
    display: none;
}

.vigie-repli > summary:hover {
    color: var(--vigie-texte);
}

/* Le focus reste PLEINEMENT visible : c'est la seule prise au clavier du bloc. */
.vigie-repli > summary:focus-visible {
    outline: 2px solid var(--vigie-accent);
    outline-offset: 2px;
    border-radius: var(--vigie-rayon-petit);
}

/* Chevron en pur CSS (deux bords d'une boîte tournée) : aucun asset, aucune
   police d'icônes, rien d'inline — compatible CSP script-src 'self'. */
.vigie-repli-chevron {
    width: 0.45rem;
    height: 0.45rem;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(-45deg);
    transition: transform var(--vigie-transition);
    flex-shrink: 0;
}

.vigie-repli[open] > summary .vigie-repli-chevron {
    transform: rotate(45deg);
}

.vigie-repli[open] > .vigie-catalogue {
    margin-top: 0.7rem;
}

.vigie-familles {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(270px, 1fr));
    gap: 0.75rem;
}

.vigie-famille {
    padding: 0.95rem 1.05rem;
    display: flex;
    flex-direction: column;
    gap: 0.55rem;
    min-width: 0;
}

/* Liseré de gauche coloré : le niveau se lit avant même la lecture du texte. */
.vigie-famille-ok {
    border-left: 3px solid color-mix(in srgb, var(--vigie-ok) 65%, transparent);
}

.vigie-famille-attention {
    border-left: 3px solid color-mix(in srgb, var(--vigie-attention) 70%, transparent);
}

.vigie-famille-critique {
    border-left: 3px solid var(--vigie-ko);
}

.vigie-famille-attente {
    border-left: 3px solid var(--vigie-pastille-neutre);
}

.vigie-famille-tete {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.45rem;
}

.vigie-famille-ico {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.85rem;
    height: 1.85rem;
    border-radius: var(--vigie-rayon-petit);
    border: 1px solid var(--vigie-bordure);
    flex-shrink: 0;
}

.vigie-famille-description,
.vigie-famille-verdict,
.vigie-famille-meta {
    margin: 0;
}

.vigie-famille-incidents {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}

.vigie-famille-incidents li {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.3rem;
    font-size: 0.84rem;
    line-height: 1.5;
    overflow-wrap: anywhere;
}

.vigie-famille-incident-libelle {
    font-weight: 600;
    color: var(--vigie-texte);
}

/* Inventaire : une ligne par type d'incident du catalogue. */
.vigie-catalogue {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.45rem;
}

.vigie-catalogue-ligne {
    display: flex;
    align-items: flex-start;
    gap: 0.7rem;
    padding: 0.8rem 0.95rem;
    min-width: 0;
}

.vigie-catalogue-actif-critique {
    border-color: color-mix(in srgb, var(--vigie-ko) 50%, transparent);
}

.vigie-catalogue-actif-attention {
    border-color: color-mix(in srgb, var(--vigie-attention) 50%, transparent);
}

.vigie-catalogue-actif-info {
    border-color: color-mix(in srgb, var(--vigie-accent) 45%, transparent);
}

.vigie-catalogue-ico {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    border-radius: var(--vigie-rayon-petit);
    border: 1px solid var(--vigie-bordure);
    flex-shrink: 0;
}

.vigie-catalogue-corps {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}

.vigie-catalogue-tete {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.4rem;
}

.vigie-catalogue-libelle {
    font-weight: 600;
    font-size: 0.96rem;
}

.vigie-catalogue-description {
    margin: 0;
    font-size: 0.86rem;
    line-height: 1.55;
    color: var(--vigie-texte-2);
    overflow-wrap: anywhere;
}

.vigie-catalogue-meta {
    margin: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
    font-size: 0.8rem;
    overflow-wrap: anywhere;
}

/* Deux familles de teintes, une seule grammaire :
   · vigie-chip-<niveau> habille l'ÉTAT d'une famille (ok/attention/critique/attente) ;
   · vigie-severite-<gravité> habille la GRAVITÉ d'un incident, en version pleine
     (badge d'icône, marqueur « actif ») ou en contour (chip du catalogue). */
.vigie-severite-critique,
.vigie-chip-critique {
    color: var(--vigie-ko);
    border-color: color-mix(in srgb, var(--vigie-ko) 45%, transparent);
    background: color-mix(in srgb, var(--vigie-ko) var(--vigie-plaque), transparent);
}

.vigie-severite-attention,
.vigie-chip-attention {
    color: var(--vigie-attention);
    border-color: color-mix(in srgb, var(--vigie-attention) 45%, transparent);
    background: color-mix(in srgb, var(--vigie-attention) var(--vigie-plaque), transparent);
}

/* « info » tire vers l'accent, pas vers le vert : une info n'est pas un OK. */
.vigie-severite-info {
    color: var(--vigie-accent);
    border-color: color-mix(in srgb, var(--vigie-accent) 40%, transparent);
    background: color-mix(in srgb, var(--vigie-accent) var(--vigie-plaque), transparent);
}

.vigie-chip-ok {
    color: var(--vigie-ok);
    border-color: color-mix(in srgb, var(--vigie-ok) 40%, transparent);
    background: color-mix(in srgb, var(--vigie-ok) var(--vigie-plaque), transparent);
}

.vigie-chip-attente {
    color: var(--vigie-texte-3);
    background: var(--vigie-badge-fond);
}

.vigie-severite-chip-critique {
    color: var(--vigie-ko);
    border-color: color-mix(in srgb, var(--vigie-ko) 45%, transparent);
}

.vigie-severite-chip-attention {
    color: var(--vigie-attention);
    border-color: color-mix(in srgb, var(--vigie-attention) 45%, transparent);
}

.vigie-severite-chip-info {
    color: var(--vigie-accent);
    border-color: color-mix(in srgb, var(--vigie-accent) 40%, transparent);
}

/* --- Page « Configuration » (/configuration) --------------------------------- */

/* Les six règles qui repeignaient MudTabs (barre d'outils, onglet, onglet actif,
   glissière) sont parties avec MudTabs au lot 11 : les onglets de réglage sont
   désormais les TOUCHES .vigie-onglet du reste de l'application, qui n'ont rien
   à repeindre. */

/* Un bloc de réglages : une face-plate de module. Le titre passe en réglette
   mono, comme les libellés d'instrument, pour qu'on distingue au premier coup
   d'œil l'intitulé d'un module de celui d'un champ. */
.vigie-config-bloc {
    padding: 0.95rem 1.05rem;
    margin-bottom: 0.9rem;
}

/* Grille de champs auto-fit : deux ou trois colonnes en large, une en mobile,
   sans media query à maintenir.

   LES VALEURS SONT DES RELEVÉS (lot 11) : chiffres tabulaires, pour qu'une
   colonne de seuils s'aligne au caractère près d'un champ à l'autre. Posé sur la
   GRILLE et pas sur l'input, et c'est mesuré : MudBlazor écrit
   `font-variant-numeric: inherit` sur `.mud-input > input.mud-input-root`, plus
   spécifique que tout sélecteur raisonnable écrit sur l'input — une règle
   `.vigie-config-champs input` perd et ne fait RIEN. Cet `inherit` est une
   porte, pas un mur : on la pousse depuis le conteneur.

   Et le POTENTIOMÈTRE s'arrête là, délibérément : un curseur sur 30…86400 s ne
   permet pas de viser 900, et un curseur DOUBLÉ d'un champ fait deux contrôles
   pour une même clé — ce que cette page a déjà payé avec la cadence de veille. */
.vigie-config-champs {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
    gap: 0.8rem 1rem;
    align-items: start;
    font-variant-numeric: tabular-nums;
}

.vigie-champ {
    min-width: 0;
}

.vigie-champ-court {
    max-width: 22rem;
}

.vigie-config-bascule {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}

.vigie-config-aide {
    margin: 0;
    font-size: 0.8rem;
    line-height: 1.55;
    max-width: 62ch;
}

.vigie-config-actions {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.7rem;
    margin-top: 0.9rem;
}

/* --- Badge d'effectivité ----------------------------------------------------- */

.vigie-effectivite {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.1rem 0.45rem;
    border-radius: var(--vigie-rayon-petit);
    border: 1px solid var(--vigie-bordure);
    font-family: var(--vigie-police-mono);
    font-size: 0.74rem;
    font-weight: 500;
    line-height: 1.6;
    white-space: nowrap;
    cursor: help;
}

.vigie-effectivite-immediate {
    color: var(--vigie-ok);
    border-color: color-mix(in srgb, var(--vigie-ok) 45%, transparent);
    background: color-mix(in srgb, var(--vigie-ok) var(--vigie-plaque), transparent);
}

/* Un lecteur existe et applique : même vert que « effectif immédiatement »,
   parce que la promesse est la même — le clic a un effet. */
.vigie-effectivite-appliquee {
    color: var(--vigie-ok);
    border-color: color-mix(in srgb, var(--vigie-ok) 45%, transparent);
    background: color-mix(in srgb, var(--vigie-ok) var(--vigie-plaque), transparent);
}

/* Mesuré sans lecteur : de l'ambre, pas du rouge. Rien n'est en panne — le
   réglage est simplement sans effet, et c'est une information, pas une alarme. */
.vigie-effectivite-sans-lecteur {
    color: var(--vigie-attention);
    border-color: color-mix(in srgb, var(--vigie-attention) 45%, transparent);
    background: color-mix(in srgb, var(--vigie-attention) var(--vigie-plaque), transparent);
}

/* On ne sait pas. Volontairement ATONE et non coloré : une couleur d'état
   affirmerait un verdict là où il n'y en a aucun. */
.vigie-effectivite-inconnu {
    color: var(--vigie-texte-2);
    background: var(--vigie-badge-fond);
}

/* --- Matrice des règles d'alerting ------------------------------------------- */

.vigie-regles {
    display: flex;
    flex-direction: column;
    gap: 0.7rem;
}

.vigie-regle {
    padding: 0.9rem 1.05rem;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    min-width: 0;
    transition: opacity var(--vigie-transition);
}

/* Un type coupé reste lisible mais s'efface : on doit voir d'un coup d'œil ce
   qui est encore armé. */
.vigie-regle-coupee {
    opacity: 0.62;
}

.vigie-regle-tete {
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
    flex-wrap: wrap;
}

.vigie-regle-ico {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.85rem;
    height: 1.85rem;
    border-radius: var(--vigie-rayon-petit);
    border: 1px solid var(--vigie-bordure);
    flex-shrink: 0;
}

.vigie-regle-identite {
    flex: 1 1 16rem;
    min-width: 0;
}

.vigie-regle-intitule {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.4rem;
}

.vigie-regle-description {
    margin: 0.3rem 0 0;
    font-size: 0.82rem;
    line-height: 1.6;
    max-width: 78ch;
}

.vigie-regle-bascule {
    margin-left: auto;
    flex-shrink: 0;
}

.vigie-regle-champs {
    display: flex;
    flex-wrap: wrap;
    gap: 0.7rem 1rem;
}

.vigie-regle-sites {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.25rem 0.85rem;
    padding-top: 0.55rem;
    border-top: 1px dashed var(--vigie-bordure);
}

.vigie-regle-sites-libelle {
    font-family: var(--vigie-police-mono);
    font-size: 0.76rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
}

.vigie-regle-site .mud-switch-label {
    font-family: var(--vigie-police-mono);
    font-size: 0.8rem;
}

/* --- Banc d'essai des alertes Discord ----------------------------------------- */

/* La liste des envois REPREND la matrice des règles (.vigie-regle et sa tête) :
   ce sont les mêmes objets — un type d'incident, son icône, sa gravité — vus
   sous un autre angle. Un second habillage aurait fait croire à deux
   référentiels différents. */
.vigie-essais {
    display: flex;
    flex-direction: column;
    gap: 0.7rem;
}

/* Le bouton d'envoi et le sort du dernier essai, empilés à droite de la ligne :
   la cause et l'effet côte à côte, sans avoir à chercher le résultat ailleurs. */
.vigie-essai-commande {
    margin-left: auto;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 0.35rem;
    flex-shrink: 0;
}

/* L'état de la page quand il n'y a rien à joindre : la grammaire d'absence du
   lot 1 (∅ atone) et sa phrase, pas une avarie rouge — ne pas être branché
   n'est pas une panne. */
.vigie-essais-etat {
    display: flex;
    flex-direction: column;
    gap: var(--vigie-pas-1);
    padding: 0.9rem 1.05rem;
    margin-bottom: var(--vigie-pas-2);
}

/* Le sort d'un envoi : code HTTP et durée, en chiffres tabulaires pour que
   deux lignes se comparent d'un coup d'œil. Même forme que .vigie-chip-tls —
   c'est la même idée (un verdict court, teinté). */
.vigie-essai-resultat {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font-family: var(--vigie-police-mono);
    font-size: 0.74rem;
    font-variant-numeric: tabular-nums;
    padding: 0.08rem 0.4rem;
    border: 1px solid var(--vigie-bordure);
    border-radius: var(--vigie-rayon-petit);
    white-space: nowrap;
}

.vigie-essai-resultat .vigie-badge-ico {
    font-size: 0.85rem !important;
    width: 0.85rem;
    height: 0.85rem;
    color: inherit;
}

.vigie-essai-resultat-ok {
    color: var(--vigie-ok);
    border-color: color-mix(in srgb, var(--vigie-ok) 45%, transparent);
    background: color-mix(in srgb, var(--vigie-ok) var(--vigie-plaque), transparent);
}

.vigie-essai-resultat-ko {
    color: var(--vigie-ko);
    border-color: color-mix(in srgb, var(--vigie-ko) 45%, transparent);
    background: color-mix(in srgb, var(--vigie-ko) var(--vigie-plaque), transparent);
}

/* --- Interrupteurs de site ---------------------------------------------------- */

.vigie-interrupteurs {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 0.75rem;
}

.vigie-interrupteur {
    padding: 0.95rem 1.05rem;
    display: flex;
    flex-direction: column;
    gap: 0.7rem;
    min-width: 0;
}

/* Liseré dès qu'un interrupteur n'est plus au « tout ouvert » : l'écart se voit
   sans lire les trois bascules. La MÊME langue que le chip « restreint » de la
   grille (rouge tireté = coupé par décision, voir .vigie-chip-restreint) :
   deux teintes pour le même fait seraient deux écrans qui se contredisent. */
.vigie-interrupteur-restreint {
    border-left: 3px dashed color-mix(in srgb, var(--vigie-ko) 70%, transparent);
}

.vigie-interrupteur-tete {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.vigie-interrupteur-identite {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.45rem;
    min-width: 0;
}

.vigie-interrupteur-bascules {
    display: flex;
    flex-direction: column;
    gap: 0.05rem;
}

.vigie-interrupteur-bascules .mud-switch-label {
    font-size: 0.86rem;
}

.vigie-interrupteur-pied {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: auto;
    padding-top: 0.45rem;
    border-top: 1px dashed var(--vigie-bordure);
}

.vigie-interrupteur-meta {
    font-size: 0.78rem;
}

/* --- Interrupteurs « analogiques » (comportement capricieux) ----------------- */

/* Les bascules MudBlazor prennent un grain d'instrument physique : un
   scintillement DISCRET au repos, un pré-allumage au survol avec un bref
   flinch, un clac net à l'appui puis un léger rebond au relâcher. Purement
   visuel — on ne touche ni aux pointer-events ni à la case sous-jacente, le
   comportement fonctionnel reste intact. Cohérent dans les deux thèmes (la
   lueur emprunte l'accent). Cibles tactiles portées à 44 px minimum. */
.mud-switch {
    min-height: 44px;
}

/* Le bouton (base) porte le mouvement du pouce ; la transition qui DÉPASSE
   légèrement donne le rebond au relâcher. */
.mud-switch .mud-switch-thumb,
.mud-switch .mud-switch-thumb-medium {
    transition: box-shadow 140ms ease, transform 260ms cubic-bezier(0.34, 1.7, 0.5, 1);
    animation: vigie-inter-scintille 6s ease-in-out infinite;
}

/* Scintillement au repos : à peine perceptible, deux micro-vacillements par
   cycle — le « courant » qui n'est pas tout à fait stable. */
@keyframes vigie-inter-scintille {
    0%,
    46%,
    52%,
    74%,
    80%,
    100% {
        opacity: 1;
    }

    49%,
    77% {
        opacity: 0.88;
    }
}

/* Pré-allumage au survol + un bref flinch (joué UNE fois à l'entrée du survol,
   il se pose et ne se répète pas tant qu'on ne ressort pas). */
.mud-switch:hover .mud-switch-thumb,
.mud-switch:hover .mud-switch-thumb-medium {
    box-shadow: 0 0 6px color-mix(in srgb, var(--vigie-accent) 70%, transparent);
    animation: vigie-inter-flinch 300ms ease-out 1;
}

@keyframes vigie-inter-flinch {
    0% {
        transform: translate(0, 0);
    }

    30% {
        transform: translate(0.5px, -0.4px);
    }

    60% {
        transform: translate(-0.4px, 0.3px);
    }

    100% {
        transform: translate(0, 0);
    }
}

/* Clac net à l'appui : la bascule s'enfonce ; au relâcher, la transition à
   rebond la fait revenir en dépassant un peu — le déclic d'un vrai bouton. */
.mud-switch .mud-switch-base:active .mud-switch-thumb,
.mud-switch .mud-switch-base:active .mud-switch-thumb-medium {
    transform: scale(0.8);
    box-shadow: 0 0 9px color-mix(in srgb, var(--vigie-accent) 85%, transparent);
    animation: none;
}

/* --- Purges et journal -------------------------------------------------------- */

.vigie-purges {
    display: flex;
    flex-direction: column;
    gap: 0.45rem;
    margin-top: 0.7rem;
}

.vigie-purge {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.45rem 0.7rem;
    padding: 0.45rem 0.6rem;
    border: 1px solid var(--vigie-bordure);
    border-radius: var(--vigie-rayon-petit);
    background: var(--vigie-surface-2);
}

.vigie-purge-libelle {
    flex: 1 1 14rem;
    font-size: 0.88rem;
    min-width: 0;
}

.vigie-journal-quand {
    white-space: nowrap;
}

.vigie-journal-apres {
    font-weight: 600;
}

/* Affichage compact : réglage d'apparence de /configuration, posé en classe
   vigie-compact sur <html> par theme.js (donc AVANT le premier paint, comme le
   thème). Il vaut pour TOUTES les pages de la coque et pas seulement pour celle
   qui porte l'interrupteur — un réglage sans effet visible ailleurs est ce qui
   fait dire « la config ne fonctionne pas ». On resserre ce qui gagne vraiment
   des lignes à l'écran : le padding de page, les respirations entre sections et
   les gouttières des grilles de cartes. */
.vigie-compact .vigie-page {
    padding-top: 0.5rem;
    padding-bottom: 1rem;
}

.vigie-compact .vigie-entete {
    margin-bottom: 0.6rem;
    padding-bottom: 0.4rem;
}

.vigie-compact .vigie-section {
    margin-bottom: 0.9rem;
}

.vigie-compact .vigie-grille-deux,
.vigie-compact .vigie-grille-trois {
    gap: 0.5rem;
}

@media (max-width: 700px) {
    /* En étroit, la bascule d'un type passe sous son titre plutôt que de
       comprimer le libellé à deux caractères par ligne. Le couple
       « envoyer + résultat » du banc d'essai suit la même règle. */
    .vigie-regle-bascule,
    .vigie-essai-commande {
        margin-left: 0;
    }

    .vigie-essai-commande {
        flex-direction: row;
        align-items: center;
        flex-wrap: wrap;
    }

    .vigie-champ-court {
        max-width: none;
    }

    .vigie-effectivite {
        white-space: normal;
    }
}

/* --- Repli mobile de la coque ------------------------------------------------ */

/* Sous 900 px, la barre d'activité se replie en RAIL HORIZONTAL collé en haut :
   mêmes destinations, mêmes libellés, un défilement latéral au doigt. La barre
   d'état se condense — c'est elle qui doit survivre en 380 px, pas la
   décoration. */
@media (max-width: 900px) {
    .vigie-coque {
        grid-template-columns: minmax(0, 1fr);
    }

    .vigie-zone {
        grid-column: 1;
    }

    /* Le rail replié devient le bord HAUT du bezel. Hauteur fixe et non plus
       « auto » : le cadre d'encastrement a besoin d'une géométrie connue pour
       poser son ombre au ras de la tôle, et une hauteur qui dépend du contenu
       la ferait glisser d'un pixel à l'autre.

       IL NE COLLE PLUS : c'est la VERRIÈRE qui est épinglée en haut à sa place.
       Deux bandes collantes en mobile, c'était 102 px de châssis permanent sur
       un écran de 800 — pour une navigation qu'on ouvre trois fois par session,
       alors que le verdict et la palette, eux, servent en permanence. Le rail
       remonte d'un geste de défilement, et la palette (épinglée, elle) mène de
       toute façon à n'importe quelle destination. */
    .vigie-activite {
        position: static;
        bottom: auto;
        width: 100%;
        height: var(--vigie-h-rail-mobile);
        flex-direction: row;
        align-items: stretch;
        gap: 0;
        padding: 0;
        border-right: none;
        border-bottom: 1px solid var(--vigie-bordure);
        overflow-x: auto;
        overflow-y: hidden;
        scrollbar-width: none;
    }

    .vigie-activite::-webkit-scrollbar {
        height: 0;
    }

    /* La marque est ÉPINGLÉE au bord gauche du défilement. Sans ça, avec dix
       entrées en 380 px, le bouton « La Vigie » (le retour à la flotte) partait
       hors écran dès qu'on faisait défiler le rail d'un centimètre : le seul
       chemin de retour vers l'accueil devenait introuvable. Fond opaque et
       filet à droite, pour que les entrées ne passent pas visuellement dessous —
       symétrique exact du groupe thème/déconnexion épinglé à droite. */
    .vigie-appbar-titre {
        position: sticky;
        left: 0;
        z-index: 2;
        padding: 0.3rem 0.55rem;
        justify-content: center;
        flex-shrink: 0;
        background-color: var(--vigie-chrome);
        background-image: linear-gradient(180deg, var(--vigie-metal-1) 0%, var(--vigie-metal-2) 100%);
        border-right: 1px solid var(--vigie-coque-arete);
    }

    .vigie-activite-sep {
        width: 1px;
        height: auto;
        margin: 0.45rem 0.3rem;
        flex-shrink: 0;
    }

    /* Les titres de groupe (« SITES ») n'ont pas de place en rangée : le
       séparateur suffit à marquer la coupure. */
    .vigie-activite-groupe-titre {
        display: none;
    }

    .vigie-activite-lien {
        padding: 0.35rem 0.5rem;
        flex-shrink: 0;
    }

    /* Rail couché : la diode du voyant se couche avec lui. Elle passe du bord
       gauche vertical au bord BAS horizontal, là où l'œil attend le marqueur
       d'onglet courant, et le rétroéclairage monte depuis ce bord. */
    .vigie-activite-lien::before {
        left: 22%;
        right: 22%;
        top: auto;
        bottom: 3px;
        width: auto;
        height: 3px;
    }

    .vigie-activite-actif {
        background-image: linear-gradient(0deg,
            color-mix(in srgb, var(--vigie-accent) 22%, transparent) 0%,
            color-mix(in srgb, var(--vigie-accent) 6%, transparent) 55%,
            transparent 100%);
    }

    /* Le bezel n'a plus de bord gauche (le rail est passé en haut) : le cadre
       d'encastrement court d'un bord à l'autre. Il repart du HAUT du viewport et
       non plus de sous le rail : celui-ci défile désormais avec la page, et un
       cadre calé sur une bande qui bouge se serait retrouvé à flotter au milieu
       de l'écran au premier geste de défilement. */
    .vigie-encastrement {
        left: 0;
    }

    /* Thème et déconnexion COLLÉS au bord droit du rail. « margin-left: auto »
       seul ne suffisait pas : le rail défile horizontalement et, avec dix
       entrées en 380 px, le groupe partait hors écran — le bouton jour/nuit
       n'était atteignable qu'en faisant défiler la barre jusqu'au bout, donc en
       pratique introuvable. « position: sticky; right: 0 » l'épingle au bord du
       défilement ; le fond opaque et le filet à gauche évitent que les entrées
       passent visuellement dessous. */
    /* En rail horizontal, l'épinglage passe du bas à la DROITE. */
    .vigie-activite-bas {
        position: sticky;
        right: 0;
        bottom: auto;
        z-index: 2;
        margin-top: 0;
        padding-top: 0;
        border-top: none;
        margin-left: auto;
        display: flex;
        flex-direction: row;
        align-items: center;
        justify-content: center;
        flex-shrink: 0;
        gap: 0;
        padding: 0 0.1rem;
        background-color: var(--vigie-chrome);
        background-image: linear-gradient(180deg, var(--vigie-metal-1) 0%, var(--vigie-metal-2) 100%);
        border-left: 1px solid var(--vigie-coque-arete);
    }

    /* Cible tactile pleine : 44 px, la règle du doigt. Elle valait 40. */
    .vigie-activite .mud-icon-button {
        min-width: 44px;
        min-height: 44px;
    }

    /* Icônes du rail encore un cran au-dessus au doigt. */
    .vigie-activite-lien .mud-icon-root {
        font-size: 1.7rem !important;
        width: 1.7rem;
        height: 1.7rem;
    }

    .vigie-activite-bas .mud-icon-root {
        font-size: 1.55rem !important;
        width: 1.55rem;
        height: 1.55rem;
    }

    /* LA VERRIÈRE EST LE PLAFOND DE LA PAGE au doigt : elle passe devant le rail
       qui défile sous elle, et la barre d'outils des logs se cale juste dessous
       — c'est-à-dire au même endroit qu'en desktop. */
    .vigie-verriere {
        z-index: 41;
    }

    /* Au doigt, le panneau prend toute la largeur de la bande, et sa hauteur
       tient compte du rail qui le précède tant qu'on n'a pas fait défiler. */
    .vigie-verdict-panneau {
        width: 100%;
        max-height: calc(100dvh - var(--vigie-h-rail-mobile) - var(--vigie-h-verriere)
            - var(--vigie-h-etat) - 0.8rem);
    }

    /* LES TROIS COLONNES TOMBENT au doigt : centrer une loupe sur une bande de
       380 px ne raconte rien, et les deux colonnes latérales égales laissaient
       au cap 70 px — de quoi afficher « f… / confi ». En flex simple, le cap
       récupère tout ce que les commandes ne prennent pas, soit près du triple,
       et « › flotte / configuration » tient en entier. */
    .vigie-verriere {
        display: flex;
    }

    .vigie-verriere-babord {
        flex: 1 1 auto;
    }

    .vigie-verriere-tribord {
        flex: 0 0 auto;
    }

    /* En 380 px, la palette perd son libellé et son rappel de raccourci : il n'y
       a pas de Ctrl+K sur un téléphone, et la loupe suffit à dire « chercher ».
       Elle garde 44 px de large — la règle du doigt. */
    .vigie-verriere-palette {
        width: auto;
        min-width: 44px;
        justify-content: center;
    }

    /* TOUS les mots des commandes tombent au doigt, « coupées » compris. Mesuré
       sur le banc, à 380 px et sur la route au cap le plus long
       (/configuration) : garder ce mot ramenait le cap de 138 px à 65 px, et
       sur /analytics/bw il l'écrasait à 1,7 px — c'est-à-dire qu'il faisait
       DISPARAÎTRE « où suis-je » pour redire ce qui est déjà écrit ailleurs.
       Car l'alerting coupé n'est pas muet pour autant, à aucune largeur :
       · la zone A écrit « coupé » en rouge, en toutes lettres, à gauche ;
       · le bouton reste ROUGE et porte une cloche barrée, glyphe unique dans
         la bande ;
       · son aria-label dit la phrase entière, lue quelle que soit la largeur.
       Le mot était le quatrième porteur du même message, et le seul à coûter le
       cap entier. */
    .vigie-verriere-palette-libelle,
    .vigie-verriere-palette-touche,
    .vigie-verriere-reglages-libelle,
    .vigie-verriere-reglages-coupe {
        display: none;
    }

    /* La barre d'état garde le +25 % en mobile : c'est précisément là qu'il a
       été demandé. 2 rem de haut au lieu de 1,6. */
    :root {
        --vigie-h-etat: 2rem;
    }

    /* En 380 px, la barre d'état garde le pouls (pastilles, alertes, horloge) et
       laisse tomber le DÉTAIL du build — version et commit. Le badge
       « la-vigie », lui, RESTE : depuis la disparition du pied de page, il est
       le seul chemin vers la signature, le build servi et les liens externes.
       Le masquer au doigt les rendrait inatteignables sur téléphone. */
    .vigie-etat-barre {
        gap: 0.45rem;
        font-size: 0.76rem;
    }

    .vigie-etat-detail {
        display: none;
    }

    .vigie-pastille-etat {
        padding: 0.2rem 0.25rem;
    }
}

/* LE CAP NE GARDE QUE SA FEUILLE. C'est la règle qu'un lecteur pressé voudra
   « simplifier » en remettant le chemin complet, alors la voici mesurée plutôt
   qu'affirmée. Elle a son propre seuil, et c'est voulu : elle ne suit PAS les
   900 px du passage au doigt, parce que le chemin complet, lui, tient encore
   très bien jusque vers 525 px — le masquer à 800 serait une perte sèche.
   Mesuré : à 641 px les dix routes affichent leur chemin ENTIER, alertes
   coupées comprises, et le pire cas (/configuration/essais-alertes, alertes
   coupées) ne commence à rogner que vers 525. Le seuil est donc posé cent
   pixels AU-DESSUS de la gêne : il n'ôte jamais un maillon qui tenait.

   CE QUE LE CHEMIN COMPLET COÛTAIT À 380 PX. Un maillon amont ne descend pas
   sous son plancher de 1,2 rem : avec son séparateur et ses deux gouttières, il
   prend 38 px pour afficher UNE lettre et des points de suspension.
   /analytics/<code> s'écrivait donc « › f… / a… / bw » et réclamait 132 px de
   cap pour 124 disponibles alertes ARMÉES, 96 seulement alertes COUPÉES — le
   badge de verdict s'élargit alors pour écrire « coupé ». La feuille y était
   rognée à 7 px sur 15 : le seul maillon qui disait quelque chose était le seul
   à ne pas tenir. Et ce n'était pas propre aux analytics : alertes coupées, la
   feuille perdait aussi des caractères sur /analytics, /logs/<run>,
   /configuration et /configuration/essais-alertes.

   CE QUE LA FEUILLE SEULE DEMANDE. L'invite, sa gouttière et le mot : 121 px au
   pire (« essais-alertes ») pour 160 disponibles, et 82 px pour « analytics »
   sur les routes à période, qui n'en offrent que 96. Les dix routes tiennent
   alors leur feuille ENTIÈRE dans les deux états d'alerting.

   CE QU'ON NE PERD PAS. La section est déjà marquée dans le rail, qui est en
   haut de l'écran au doigt ; la page nomme son site dans son propre h1 ; et la
   palette mène partout, ce qui rend la navigation par le chemin superflue là où
   elle ne coûterait que des pixels à « où suis-je ».

   CE QU'ON NE PERD SURTOUT PAS. Le chemin ENTIER reste ANNONCÉ : le rappel hors
   écran prend le relais des maillons exactement là où ils tombent. Le tronçage
   est visuel, il ne devient jamais sémantique. */
@media (max-width: 640px) {
    .vigie-verriere-cap a,
    .vigie-verriere-sep {
        display: none;
    }

    .vigie-verriere-cap-amont {
        display: inline;
    }
}

/* NOM DES PASTILLES : il ne s'affiche QUE là où la barre entière tient, et ce
   n'était plus vrai. La règle disait « les noms tombent sous 520 px et entre 901
   et 1250, ailleurs ils tiennent » ; la bande 521-900 avait donc le droit de les
   écrire. Mesuré au lot 14, à 560 px : les cinq pastilles nommées réclament
   447 px, la barre en réclame 720 au total pour 560 disponibles, et le résultat
   n'est pas un tronçage propre — c'est « visiteurs (2:1alertes) », l'horloge
   ÉCRITE PAR-DESSUS les deux dernières pastilles. Illisible sur toute une
   tablette.
   Le seuil de tenue est 720 px, à 0,05 px près : ce n'est pas un seuil, c'est
   une coïncidence, et le premier compteur qui passe à deux chiffres la reprend.
   Les noms rejoignent donc le seuil qu'ils avaient déjà de l'autre côté — 1250 —
   ce qui ne laisse plus qu'UNE règle au lieu de deux, et plus aucune bande où la
   barre se recouvre elle-même.
   Aucune pastille n'est muette pour autant : l'infobulle au survol et le panneau
   titré qui s'ouvre au tap la nomment, à toute largeur. */
@media (max-width: 1250px) {
    .vigie-pastille-etat-nom {
        display: none;
    }
}

/* #75 — L'HORLOGE PERDAIT SON « UTC » À 380 PX, et ce n'était pas un détail
   d'alignement : le journal de bord est en Europe/Paris, l'horloge en UTC, et
   « 22:01:0 » sans son suffixe devient une heure qu'on ne sait plus lire.
   MESURÉ : la barre réclame 432,95 px de contenu naturel pour 380 disponibles.
   Le flex-shrink absorbe les 53 px manquants en rognant TROIS blocs à la fois —
   le badge (89 → 76), les pastilles (160 → 137) et l'horloge (121 → 104) — et
   c'est l'horloge qui se voit parce que « UTC » est en bout de ligne. La gêne
   commence donc sous 433 px, pas à 380.
   CE QUI TOMBE : les deux ICÔNES, l'arbre du badge et le cadran de l'horloge.
   Toutes deux aria-hidden, toutes deux redondantes avec le mot qu'elles
   accompagnent — « la-vigie » et « HH:MM:SS UTC » se lisent sans elles. 46 px
   récupérés, plus 8,5 px sur les gouttières : 378,4 px de contenu pour 380, tout
   à sa largeur naturelle, plus un seul bloc rogné. C'est la décoration qui cède,
   pas l'information — la règle de la barre depuis le lot 4.
   ET L'HORLOGE NE SE ROGNE PLUS, jamais : flex-shrink à 0. Le contenu de la
   barre est FIXE sous ce seuil (badge, cinq pastilles-icônes, horloge
   monospace), donc le calcul tient ; mais si une police substituée l'élargit
   d'un pixel sur un autre poste, c'est autre chose qui cédera.
   SEUIL À 520 ET NON 433 : la gêne est à 433, et entre les deux il n'y a que
   deux glyphes décoratifs à perdre. 520 était déjà le seuil des noms de
   pastilles avant que ceux-ci ne montent à 1250 — on ne crée pas un nombre de
   plus dans la feuille pour 87 px de marge. */
@media (max-width: 520px) {
    .vigie-etat-barre {
        gap: 0.3rem;
    }

    /* La cinquième barre coûte 5 px (3 de trait, 2 de gouttière) : on les
       reprend ici plutôt que de la retirer. Gouttière à 1 px et padding nul
       rendent 8,8 px — l'escalier reste lisible, il serre seulement. */
    .vigie-signal {
        gap: 1px;
        padding: 0;
    }

    .vigie-etat-apropos .mud-icon-root,
    .vigie-etat-horloge .mud-icon-root {
        display: none;
    }

    .vigie-etat-droite {
        flex-shrink: 0;
    }
}

/* --- Lueur d'hologramme (quart de nuit uniquement) ---------------------------- */

/* LA LUEUR EST DÉSORMAIS RÉSERVÉE À CE QUI EST COLORÉ, c'est-à-dire aux ÉTATS.
   C'est la conséquence directe du monochrome, et elle se démontre en une
   phrase : un halo gris posé autour d'un texte gris ne se lit pas comme une
   lueur, il se lit comme un défaut de rendu de police. Le cyan portait ces
   effets ; le gris ne le peut pas, parce qu'une lueur a besoin d'une teinte
   pour se distinguer du flou.
   Ce qui disparaît ne part donc pas sans remplaçant — c'est la règle de
   substitution du dépôt. Ce que la lueur faisait (dire « ceci est actif,
   ceci est la valeur importante »), la VALEUR le fait maintenant : le blanc
   atténué des métriques héro et les crochets de coin à #dfe3e7 sont plus
   contrastés que tout le reste de la carte. La présence vient du contraste et
   de la géométrie, plus du rayonnement.
   Restent allumés : les trois états et eux seuls — le journal de bord, lui,
   n'a jamais éteint son phosphore, mais c'est un tube qui rend sa propre
   lumière, pas un halo posé par la charte. Dans un écran où états et emblème
   sont les seuls objets colorés de la planche de bord, la lueur cesse d'être
   un effet d'ambiance pour devenir un signal — elle ne se pose plus que là où
   il y a quelque chose à voir. */
:root.vigie-sombre .vigie-pastille-etat-ok .mud-icon-root,
:root.vigie-sombre .vigie-pastille-etat-attention .mud-icon-root,
:root.vigie-sombre .vigie-pastille-etat-critique .mud-icon-root {
    filter: drop-shadow(0 0 3px color-mix(in srgb, currentColor 55%, transparent));
}

/* Les instruments s'allument : l'arc de remplissage, l'index et le front de
   charge reçoivent le même halo que les métriques héro. Le filtre porte sur les
   TRACÉS, jamais sur le texte du cadran — une valeur floutée serait un comble
   pour un instrument de mesure. */
:root.vigie-sombre .vigie-cadran-remplissage,
:root.vigie-sombre .vigie-cadran-index,
:root.vigie-sombre .vigie-seg-front,
/* En sombre, le point garde une lueur de phosphore — mais RASE : elle donne au
   pixel son épaisseur de tube, elle ne fait pas une auréole. Le vrai halo, lui,
   n'apparaît qu'au passage du faisceau (.vigie-radar-halo). */
:root.vigie-sombre .vigie-radar-point {
    filter: drop-shadow(0 0 1.5px color-mix(in srgb, currentColor 40%, transparent));
}

/* Les pastilles de statut s'allument comme des diodes de pupitre. */
:root.vigie-sombre .vigie-statut-up {
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--vigie-ok) 22%, transparent), 0 0 8px color-mix(in srgb, var(--vigie-ok) 60%, transparent);
}

:root.vigie-sombre .vigie-statut-down {
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--vigie-ko) 25%, transparent), 0 0 8px color-mix(in srgb, var(--vigie-ko) 65%, transparent);
}

/* L'onglet actif et la palette portaient un halo cyan. En achromatique, le halo
   devient un flou ; ce qui le remplace à fonction égale, c'est une ARÊTE — un
   liseré net d'un pixel, qui dit « cet objet est au-dessus » aussi clairement
   qu'un rayonnement et sans rien brouiller. */
:root.vigie-sombre .vigie-onglet-actif {
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--vigie-accent) 42%, transparent);
}

:root.vigie-sombre .vigie-palette {
    border-color: color-mix(in srgb, var(--vigie-accent) 40%, var(--vigie-bordure-forte));
    box-shadow: 0 20px 48px var(--vigie-ombre-forte),
        0 0 0 1px color-mix(in srgb, var(--vigie-accent) 22%, transparent);
}

/* --- Mouvement réduit -------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
    /* ══ LE SOCLE PAR FAMILLE ══════════════════════════════════════════════════

       POURQUOI IL A FALLU L'AJOUTER. La garde de cette feuille fonctionnait PAR
       ÉNUMÉRATION : elle ne coupait que ce qu'elle nommait. La Vigie porte dix-
       sept déclarations « infinite » ; la garde en nommait trois. Toute animation
       neuve lui échappait EN SILENCE — c'est la « garde incomplète » : elle
       rassure sans couvrir. Depuis, une porte VOIT cette régression : la porte
       e2e balaie les animations en boucle dans un vrai navigateur, sous
       « no-preference » PUIS sous « reduce », en CI
       (tests/e2e/parcours/05-mouvement-reduit.mjs, balayage exhaustif du DOM
       dans lib/audits.mjs). Le socle reste la correction ; la porte est le
       témoin qui rougirait si on le retirait.

       LES TROIS PROPRIÉTÉS, ET PAS DEUX. animation-iteration-count est
       indispensable et n'est pas décoratif : une animation « infinite » ramenée à
       0,01 ms reste une animation qui rejoue sans fin — elle tourne simplement
       plus vite. Sur dix-sept déclarations infinite, en oublier une seule des
       trois lignes ne coupe rien du tout. Même correctif que celui livré sur
       creances (src/CreancesApp/wwwroot/app.css).

       0,01 ms ET NON 0 : à zéro, transitionend ne se déclenche pas, et des
       composants MudBlazor s'appuient dessus pour refermer un dialogue ou un
       menu. Un centième de milliseconde est instantané pour l'œil et reste un
       événement pour le navigateur.

       CE QUE CE SOCLE IMPOSE AU DESSIN. Sous ce régime, chaque animation SAUTE À
       SON IMAGE DE 100 %. Toute keyframe de cette feuille doit donc avoir pour
       100 % son état de repos correct. Les trois cas où c'est impossible sont
       traités nommément plus bas.

       CE QUE CE SOCLE NE REMPLACE PAS. Il sait dire « ça ne bouge plus ». Il ne
       sait pas dire QUEL REPOS EST LE BON — et « display: none » sur un halo ou
       un voile n'est pas une coupure de mouvement, c'est le choix d'un état de
       repos. C'est pour cela que le bloc par énumération qui suit reste entier :
       le supprimer serait une régression que personne n'aurait vue.

       Verrous : GardeMouvementTests, verrou de PRÉSENCE — il atteste que la
       règle est écrite, et rougit sans navigateur ni publish — et le parcours
       05-mouvement-reduit de la porte e2e, verrou de COMPORTEMENT : il mesure
       qu'un navigateur en « reduce » cesse réellement d'animer. Les deux se
       complètent, aucun ne remplace l'autre (voir le commentaire de tête du
       fichier de tests). */
    *,
    *::before,
    *::after {
        scroll-behavior: auto !important;
        transition-duration: 0.01ms !important;
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
    }

    /* ── LES TROIS REPOS QUE LA FAMILLE NE SAIT PAS CHOISIR ───────────────────

       Chacun de ces trois cas donnerait, sous le seul socle, soit une PERTE
       D'INFORMATION, soit un ARTEFACT. C'est la démonstration concrète de
       pourquoi une garde par famille et une garde par énumération ne font pas
       le même travail. */

    /* PERTE D'INFORMATION. Le liseré de déploiement s'anime par-dessus une règle
       de base à « opacity: 0 » (il ne sert au survol que dans ce cas). Figé, il
       DISPARAÎTRAIT : quelqu'un en mouvement réduit perdrait purement et
       simplement le repère « cette carte est en train de se déployer ». L'état
       reste, seul le mouvement part. */
    .vigie-carte-deploiement::before {
        opacity: 1;
        background: var(--vigie-attention);
        animation: none;
    }

    /* ARTEFACT. Une tête de comète arrêtée n'est plus un signal, c'est une
       traînée ambre en travers de la rangée de gates — et elle masquerait les
       pas qu'elle est censée survoler. Elle disparaît COMPLÈTEMENT. Le pas en
       cours reste identifiable par son ambre plein et sa durée écoulée, qui ne
       doivent rien au mouvement. */
    .vigie-gates-encours::after {
        display: none;
    }

    /* ARTEFACT. Même raison : une ligne de scan figée est une barre morte posée
       en travers de la sparkline, exactement là où l'on vient lire une courbe. */
    .vigie-carte-deploiement .vigie-carte-spark::after {
        display: none;
    }

    /* Le halo de la carte et le retournement du badge, eux, n'ont besoin de rien
       de plus : leur image de 100 % EST leur état de repos (halo à son amplitude
       haute, badge à rotateX(0)). C'est une contrainte que le socle impose au
       dessin, et elle a été tenue à l'écriture plutôt que rattrapée ici. */

    .vigie-pastille-etat-critique,
    .vigie-statut-down,
    .vigie-gate-encours {
        animation: none;
    }

    /* Le ciel se fige et le radar cesse de balayer : les étoiles restent (elles
       ne bougeaient déjà presque pas), c'est la DÉRIVE qui est coupée. Le radar
       garde ses cercles, son réticule et ses échos — seule la ligne de balayage
       s'arrête, à sa position de départ.
       LES TROIS PREMIERS SÉLECTEURS PORTENT LE MÊME ANCRAGE :has() QUE LA RÈGLE
       QUI ALLUME LA DÉRIVE, et ce n'est pas de la copie décorative. Depuis que
       le ciel ne vit plus que sur l'écluse, la règle qui l'anime pèse (0,3,2) ;
       une coupure écrite comme avant, en (0,2,1), aurait perdu la cascade — et
       elle l'aurait perdue EN SILENCE, personne ne testant un média de
       préférence. Une coupure de mouvement doit être au moins aussi spécifique
       que ce qu'elle coupe. */
    :root.vigie-sombre body:has(.connexion-ecran) .vigie-espace-nebuleuse,
    :root.vigie-sombre body:has(.connexion-ecran) .vigie-espace-poussiere,
    :root.vigie-sombre body:has(.connexion-ecran) .vigie-espace-etoiles,
    .vigie-radar-balayage,
    .vigie-activite-actif::before {
        animation: none;
    }

    /* Radar : la rémanence et le flare s'arrêtent ; les échos restent affichés
       STATIQUES et pleinement allumés. Le halo DISPARAÎT au lieu de se figer :
       sans balayage, il ne raconte plus rien et ne serait qu'une auréole
       permanente autour de chaque point. */
    .vigie-radar-echo {
        animation: none;
        opacity: 1;
    }

    .vigie-radar-halo {
        display: none;
    }

    /* Journal : ni auto-défilement animé, ni fondu d'apparition, ni curseur
       clignotant — la liste reste lisible, le curseur simplement présent. */
    .vigie-journal-ligne {
        animation: none;
    }

    .vigie-journal-curseur {
        animation: none;
        opacity: 1;
    }

    /* Cadran : l'aiguille se pose directement à sa valeur, sans rebond. */
    .vigie-cadran-aiguille {
        transition: none;
    }

    /* Navigation de section : les entrées cessent de fondre et de s'enfoncer.
       La coupure est écrite pour CETTE rangée et non pour toutes les touches
       .vigie-onglet : ce lot n'a pas à changer le comportement des onglets
       d'/analytics et de /configuration, qui existaient avant lui. Le
       rétroéclairage de l'entrée engagée reste, lui — c'est un état, pas un
       mouvement, et c'est le seul repère de position de la rangée. */
    .vigie-nav-section-lien {
        transition: none;
    }

    .vigie-nav-section-lien:active {
        transform: none;
    }

    /* Interrupteurs : plus de scintillement, de flinch ni de rebond — la bascule
       reste une bascule, elle change d'état sans mise en scène. */
    .mud-switch .mud-switch-thumb,
    .mud-switch .mud-switch-thumb-medium {
        animation: none;
        transition: none;
    }

    /* Le balayage d'allumage disparaît COMPLÈTEMENT (et pas seulement son
       animation) : sans mouvement, il ne resterait qu'un voile figé en haut de
       chaque page. */
    .vigie-page::after {
        display: none;
    }

    /* Ni allumage ni texture d'écran : la surface est simplement là. */
    .vigie-surface {
        animation: none;
        background-image: none;
    }

    .vigie-palette {
        background-image: none;
    }

    .vigie-carte,
    .vigie-carte::before,
    .vigie-activite-lien,
    .vigie-pastille-etat,
    .vigie-quickopen,
    .vigie-verriere-commande,
    .vigie-logs-quete-saisie,
    .vigie-repli-chevron,
    .vigie-apropos-liens a,
    .vigie-logs-job,
    .vigie-apropos-build a.vigie-badge-mono {
        transition: none;
    }
}

/* --- Plomberie Blazor -------------------------------------------------------- */

#blazor-error-ui {
    display: none;
    position: fixed;
    bottom: var(--vigie-h-etat);
    left: 0;
    right: 0;
    background: var(--vigie-attention);
    color: var(--vigie-accent-contraste);
    padding: 0.5rem 1.25rem;
    z-index: 90;
}

#blazor-error-ui .dismiss {
    cursor: pointer;
    float: right;
}

.blazor-error-boundary {
    background: #b32121;
    padding: 1rem;
    color: white;
    border-radius: var(--vigie-rayon-petit);
}

.blazor-error-boundary::after {
    content: "Une erreur est survenue.";
}

/* Texte réservé aux lecteurs d'écran : retiré du flux visuel sans être masqué
   (display:none et visibility:hidden le retireraient aussi de l'arbre
   d'accessibilité). Sert aux explications que le title seul ne restitue pas :
   il est inaccessible au clavier et ignoré sur mobile. */
.vigie-hors-ecran {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* ══ EMBLÈME, UN PAR QUART ════════════════════════════════════════════════════

   C'est ici que la couche « factions » se voit le plus : chaque tour de garde
   porte son insigne, et les deux ne se ressemblent pas — voir EmblemeVigie.razor
   pour la grammaire opposée des deux dessins et le test d'originalité.

   L'EMBLÈME EST LA SEULE COULEUR D'IDENTITÉ DE L'APPLICATION, et il est ROUGE
   dans les DEUX quarts. C'est le renversement du lot : les accents sont passés
   en gris, donc l'insigne ne partage plus sa teinte avec quoi que ce soit.
   · le quart de JOUR, immaculé impérial, porte l'insigne RADIAL — anneau
     segmenté, huit branches centripètes, cœur fermé. Un ordre qui converge ;
   · le quart de NUIT, monochrome rebelle, porte l'insigne AXIAL — deux
     croissants qui montent et s'ouvrent, l'œil libre entre eux.
   Voir EmblemeVigie.razor pour la grammaire des deux dessins et le test
   d'originalité.

   DEUX ROUGES, ET LA MESURE L'EXIGE. Un rouge profond (#c8102e) tient sur les
   surfaces claires du jour (Lc 66,2 / 5,01:1 au pire) et s'effondre sur du
   sombre ; un rouge clair (#ff6a5e) tient sur les gris de nuit (Lc 45,9 /
   5,55:1 au pire) et serait délavé sur du blanc. Le premier réflexe — une
   seule valeur pour « la couleur de la marque » — ne survit pas au premier
   fond qui change.
   Le plancher retenu est Lc 45 et non le seul 3:1 des graphiques : #e13b3b,
   l'ancien rouge de nuit, PASSAIT le 3:1 (3,62:1) en ne tenant que Lc 30,7, et
   rendu à 16 px les pointes des croissants et l'évidement de l'œil s'empâtaient.
   WCAG 2 sur-évalue les rouges saturés sur fond sombre, et le défaut mord plus
   fort sur un dessin qui s'affine en pointe que sur une masse compacte.

   LA TEINTE RESTE DÉDIÉE, dans les deux quarts : ni #c8102e ni #ff6a5e n'est le
   rouge d'état critique. Une marque ne se lit jamais comme une alerte, et un
   test le verrouille — c'est de nuit que le risque est réel, l'emblème
   (#ff6a5e) et l'état (#ff9eae) y étant deux rouges clairs voisins. */
:root {
    --vigie-embleme: #c8102e;
    /* Le rouge profond du jour s'effondre à 2,52:1 sur la console sombre
       (#23282e), la seule surface encastrée de ce quart. La variante claire y
       remonte à 5,28:1. Aucune vue ne pose aujourd'hui l'insigne sur la
       console : cette valeur est une GARDE, posée parce que la surface existe
       et que la charte doit répondre avant qu'on l'y mette, pas après. */
    --vigie-embleme-sur-console: #ff6a5e;
    /* LE FOND DE DÉCOUPE (revue PR #94). Les deux insignes évident un motif —
       cœur hexagonal du radial, perçage de l'œil de l'axial — en le REMPLISSANT
       d'une couleur de fond, pas en trouant le SVG. Rempli en dur avec
       --vigie-fond, l'évidement laissait une pastille de la mauvaise teinte
       partout où l'emblème ne se pose pas sur la dalle (rail d'activité, sas
       de secours, carte de connexion). La variable suit donc le CONTEXTE :
       elle vaut le fond par défaut, et chaque conteneur qui a son propre fond
       la recale plus bas, à côté de son background. */
    --vigie-embleme-fond: var(--vigie-fond);
}

.vigie-sombre {
    --vigie-embleme: #ff6a5e;
    --vigie-embleme-sur-console: #ff6a5e;
}

/* Le rail d'activité et la barre du sas peignent une tôle métal par-dessus leur
   chrome : au droit de l'emblème (centre du rail, milieu de la barre), c'est la
   valeur médiane de la rampe qui affleure — pas le fond de la dalle. */
.vigie-activite {
    --vigie-embleme-fond: var(--vigie-metal-2);
}

.vigie-sas-barre {
    --vigie-embleme-fond: color-mix(in srgb, var(--vigie-metal-1) 50%, var(--vigie-metal-2));
}

/* La carte de connexion est une surface opaque : la découpe s'aligne dessus. */
.connexion-carte {
    --vigie-embleme-fond: var(--vigie-surface-opaque);
}

/* La console porte la même dalle sombre dans les deux quarts : l'insigne qu'on
   y poserait doit donc suivre la dalle, pas le quart — teinte ET découpe. */
.vigie-journal .vigie-embleme,
.vigie-terminal .vigie-embleme {
    color: var(--vigie-embleme-sur-console);
    --vigie-embleme-fond: var(--log-fond, var(--vigie-terminal-fond));
}

.vigie-embleme {
    display: inline-block;
    vertical-align: -0.18em;
    flex: none;
    color: var(--vigie-embleme);
}

/* L'ALTERNANCE. Les deux dessins sont dans le DOM, un seul s'affiche. Le choix
   se fait sur la classe que theme.js pose sur <html> avant le premier paint,
   donc l'insigne est déjà le bon à la première frame — aucun clignotement, et
   rien à recalculer côté serveur, qui ne sait pas quel quart le poste affiche.
   display:none plutôt qu'une opacité : le dessin masqué sort AUSSI de l'arbre
   d'accessibilité, sans quoi les deux aria-label seraient annoncés à la suite.
   LA RÈGLE NE FAIT QUE MASQUER, jamais ré-afficher — d'où le :not() plutôt
   qu'une paire masquer/rétablir. L'insigne visible n'a donc AUCUN display
   imposé par ce bloc : il garde celui que le reste de la feuille lui donne
   dans son contexte (le rail d'activité est un conteneur flex, qui bloque
   l'inline-block en block). Rétablir une valeur en dur ici, c'est parier sur
   ce contexte — et le pari se perdrait le jour où un seul des deux quarts
   changerait de conteneur, en décalant la mise en page d'un quart sur deux. */
:root:not(.vigie-sombre) .vigie-embleme-nuit,
:root.vigie-sombre .vigie-embleme-jour {
    display: none;
}

.vigie-embleme-marque {
    width: 1.7em;
    height: 1.7em;
}

.connexion-embleme {
    width: 1.25em;
    height: 1.25em;
}


/* ══ ÉCRANS DE L'ÉCLUSE (/connexion et /connexion/code) ══════════════════════

   Ces règles vivaient dans Connexion.razor.css, en CSS scopé. Elles sont
   remontées ICI le jour où l'écluse a eu DEUX écrans : le CSS scopé de Blazor
   attache un attribut différent par composant, donc une seconde page repeignant
   la même fenêtre d'outil aurait dû recopier deux cents lignes — et deux copies
   d'une charte divergent toujours, en général du côté qu'on regarde le moins.

   Le préfixe .connexion- les tient à l'écart du reste : aucune de ces classes
   n'existe ailleurs dans la coque. Le thème suit les variables --vigie-*
   commutées par la classe vigie-sombre que theme.js pose sur <html>.
   Page SSR statique de bout en bout — tout est CSS, aucun JS. */

/* Fond TRANSPARENT et non plus --vigie-fond : le ciel étoilé est posé par
   App.razor sous toutes les pages, y compris celle-ci. On arrive donc devant
   l'écluse du vaisseau, pas devant un formulaire posé sur du gris. */
.connexion-cadre {
    min-height: 100dvh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.5rem;
    background: transparent;
}

.connexion-carte {
    position: relative;
    background: var(--vigie-surface-opaque);
    background-image: var(--vigie-scanlines);
    border: 1px solid var(--vigie-bordure-forte);
    border-radius: var(--vigie-rayon);
    box-shadow: 0 12px 32px var(--vigie-ombre-forte);
    padding: 0;
    width: 100%;
    max-width: 21.5rem;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/* Cadre de module : crochets lumineux aux coins + balayage d'accent sur
   l'arête haute — la même grammaire « console » que les tuiles de l'app. */
.connexion-carte::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    border-radius: inherit;
    background:
        linear-gradient(90deg, transparent, var(--vigie-arete) 30%, var(--vigie-arete) 70%, transparent) no-repeat top center / 60% 1px,
        linear-gradient(var(--vigie-coin), var(--vigie-coin)) no-repeat left top / 11px 2px,
        linear-gradient(var(--vigie-coin), var(--vigie-coin)) no-repeat left top / 2px 11px,
        linear-gradient(var(--vigie-coin), var(--vigie-coin)) no-repeat right top / 11px 2px,
        linear-gradient(var(--vigie-coin), var(--vigie-coin)) no-repeat right top / 2px 11px,
        linear-gradient(var(--vigie-coin), var(--vigie-coin)) no-repeat left bottom / 11px 2px,
        linear-gradient(var(--vigie-coin), var(--vigie-coin)) no-repeat left bottom / 2px 11px,
        linear-gradient(var(--vigie-coin), var(--vigie-coin)) no-repeat right bottom / 11px 2px,
        linear-gradient(var(--vigie-coin), var(--vigie-coin)) no-repeat right bottom / 2px 11px;
}

/* En sombre, la fenêtre de connexion émet un léger halo d'hologramme. */
:root.vigie-sombre .connexion-carte {
    box-shadow: 0 12px 32px var(--vigie-ombre-forte), 0 0 32px -12px var(--vigie-accent);
}

/* Barre de titre de la « fenêtre » : c'est elle qui fait lire la carte comme un
   panneau d'outil plutôt que comme un formulaire de site vitrine. */
.connexion-barre {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    padding: 0.45rem 0.65rem;
    background: var(--vigie-chrome);
    border-bottom: 1px solid var(--vigie-bordure);
    font-family: var(--vigie-police-mono);
    font-size: 0.72rem;
    letter-spacing: 0.04em;
    color: var(--vigie-texte-3);
}

.connexion-pastille {
    width: 0.55rem;
    height: 0.55rem;
    border-radius: 50%;
    background: var(--vigie-pastille-neutre);
    flex-shrink: 0;
}

.connexion-corps {
    display: flex;
    flex-direction: column;
    gap: 0.95rem;
    padding: 1.7rem 1.5rem 1.6rem;
}

.connexion-titre {
    margin: 0;
    font-size: 1.4rem;
    font-weight: 600;
    text-align: center;
    color: var(--vigie-texte);
    letter-spacing: -0.02em;
}

.connexion-marque {
    color: var(--vigie-accent);
}

.connexion-sous-titre {
    margin: -0.4rem 0 0.25rem;
    text-align: center;
    color: var(--vigie-texte-3);
    font-family: var(--vigie-police-mono);
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.16em;
}

.connexion-message {
    margin: 0;
    padding: 0.45rem 0.6rem;
    color: var(--vigie-ko);
    background: color-mix(in srgb, var(--vigie-ko) var(--vigie-plaque), transparent);
    border: 1px solid color-mix(in srgb, var(--vigie-ko) 40%, transparent);
    border-radius: var(--vigie-rayon-petit);
    font-size: 0.84rem;
    line-height: 1.5;
    text-align: center;
}

/* Le bloc « auth non configurée » porte deux paragraphes et les commandes à
   copier : il déborde du gabarit d'une ligne d'erreur, d'où ces règles. Donner
   la marche à suivre vaut mieux qu'un nom de variable seul — c'est ce qui évite
   qu'on contourne la sécurité faute de savoir la configurer. */
.connexion-message p {
    margin: 0;
}

.connexion-message p + p {
    margin-top: 0.45rem;
}

.connexion-message-aide {
    color: var(--vigie-texte-3);
    font-size: 0.74rem;
    text-align: left;
}

.connexion-message code {
    font-family: var(--vigie-police-mono);
    font-size: 0.7rem;
    color: var(--vigie-texte-2);
    overflow-wrap: anywhere;
}

/* Une commande à copier : bloc à part, à gauche, sur son propre fond. Elle ne
   doit jamais élargir la fenêtre de connexion — d'où le retour à la ligne
   plutôt qu'un défilement horizontal, sur une carte de 21 rem. */
.connexion-message-commande {
    display: block;
    margin: 0.3rem 0;
    padding: 0.3rem 0.4rem;
    background: var(--vigie-champ-fond);
    border-radius: var(--vigie-rayon-petit);
    text-align: left;
    white-space: pre-wrap;
}

.connexion-champ {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    font-family: var(--vigie-police-mono);
    font-size: 0.76rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--vigie-texte-3);
}

.connexion-champ input {
    background: var(--vigie-champ-fond);
    border: 1px solid var(--vigie-bordure);
    border-radius: var(--vigie-rayon-petit);
    color: var(--vigie-texte);
    padding: 0.55rem 0.7rem;
    font-family: var(--vigie-police-mono);
    font-size: 0.95rem;
    letter-spacing: 0;
    text-transform: none;
    transition: border-color var(--vigie-transition), box-shadow var(--vigie-transition);
}

.connexion-champ input:focus {
    outline: none;
    border-color: var(--vigie-accent);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--vigie-accent) 25%, transparent);
}

.connexion-bouton {
    background: var(--vigie-accent);
    color: var(--vigie-accent-contraste);
    border: 1px solid var(--vigie-accent);
    border-radius: var(--vigie-rayon-petit);
    padding: 0.6rem;
    font-weight: 600;
    font-size: 0.95rem;
    cursor: pointer;
    margin-top: 0.2rem;
    transition: filter var(--vigie-transition), box-shadow var(--vigie-transition);
}

/* Halo au survol en sombre : le bouton « prend tension ». */
:root.vigie-sombre .connexion-bouton:hover:enabled {
    box-shadow: 0 0 14px -4px var(--vigie-accent);
}

.connexion-bouton:hover:enabled {
    filter: brightness(1.12);
}

.connexion-bouton:disabled {
    background: var(--vigie-badge-fond);
    border-color: var(--vigie-bordure);
    color: var(--vigie-texte-3);
    cursor: not-allowed;
}

/* Pied de « fenêtre » : la version servie, comme la barre d'état de l'app. */
.connexion-pied {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.4rem 0.65rem;
    background: var(--vigie-chrome);
    border-top: 1px dashed var(--vigie-bordure);
    font-family: var(--vigie-police-mono);
    font-size: 0.68rem;
    color: var(--vigie-texte-3);
}

@media (prefers-reduced-motion: reduce) {
    .connexion-champ input,
    .connexion-bouton {
        transition: none;
    }

    .connexion-carte {
        background-image: none;
    }
}

/* ── Second facteur ─────────────────────────────────────────────────────────
   Le champ de code est le seul endroit de l'application où l'on recopie des
   caractères depuis un autre écran : il est donc plus grand, espacé, et centré.
   Les six chiffres se relisent avant validation, ce qui évite le refus qu'on ne
   comprend pas. Il accepte AUSSI un code de secours (« abcd-efgh ») — d'où une
   largeur en caractères et non en chiffres, et aucun masquage de saisie. */
.connexion-code-saisie {
    text-align: center;
    font-size: 1.35rem;
    letter-spacing: 0.32em;
    /* Le décalage compense l'espacement de la dernière lettre, qui pousse
       visuellement le contenu vers la gauche dans un champ centré. */
    text-indent: 0.32em;
}

/* L'aide sous le champ : c'est elle qui dit qu'un code de secours passe par le
   même endroit. Atone, mais pas cachée — quelqu'un dont le téléphone vient de
   mourir la cherche exactement là. */
.connexion-aide-secours {
    margin: -0.35rem 0 0;
    color: var(--vigie-texte-3);
    font-size: 0.74rem;
    line-height: 1.5;
    text-align: left;
}

.connexion-aide-secours strong {
    color: var(--vigie-texte-2);
    font-weight: 600;
}

/* ══ SECOND FACTEUR (/configuration/second-facteur) ══════════════════════════

   L'écran d'enrôlement est le seul de l'application où l'on RECOPIE : un QR
   code vers un téléphone, une clé de trente-deux caractères à la main, dix
   codes de secours sur un papier. Tout ce qui suit sert cette lecture-là —
   monospace, groupes courts, interlignage large. Ce n'est pas une page qu'on
   scanne en deux secondes comme la flotte : c'est une page qu'on suit ligne à
   ligne, une fois tous les deux ans, et souvent sous stress. */

/* Le voyant d'état. Trois teintes, un seul rôle chacune, et surtout : la
   troisième existe pour que « enrôlé » ne se confonde jamais avec « protégé ».
   Le libellé porte déjà la distinction — la couleur ne fait que confirmer. */
.vigie-securite-etat {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.2rem 0.5rem;
    border: 1px solid;
    border-radius: var(--vigie-rayon-petit);
    font-family: var(--vigie-police-mono);
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.vigie-securite-etat-actif {
    color: var(--vigie-ok);
    border-color: color-mix(in srgb, var(--vigie-ok) 45%, transparent);
    background: color-mix(in srgb, var(--vigie-ok) var(--vigie-plaque), transparent);
}

/* Rouge, comme une avarie, et c'est voulu : la soupape ouverte n'est pas un
   réglage tiède, c'est une protection débranchée. */
.vigie-securite-etat-inoperant {
    color: var(--vigie-ko);
    border-color: color-mix(in srgb, var(--vigie-ko) 45%, transparent);
    background: color-mix(in srgb, var(--vigie-ko) var(--vigie-plaque), transparent);
}

.vigie-securite-etat-absent {
    color: var(--vigie-texte-3);
    border-color: var(--vigie-bordure);
    background: var(--vigie-badge-fond);
}

.vigie-securite-resume {
    display: flex;
    flex-direction: column;
    gap: var(--vigie-pas-2);
}

.vigie-securite-faits {
    display: flex;
    flex-wrap: wrap;
    gap: var(--vigie-pas-3);
    margin: 0;
}

.vigie-securite-faits dt {
    color: var(--vigie-texte-3);
    font-family: var(--vigie-police-mono);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.09em;
}

.vigie-securite-faits dd {
    margin: 0.2rem 0 0;
    color: var(--vigie-texte);
    font-family: var(--vigie-police-mono);
    font-size: 0.95rem;
}

/* Les trois étapes de l'enrôlement, numérotées : c'est une procédure, pas une
   liste de réglages, et l'ordre y est contraignant. */
.vigie-enrolement-etapes {
    margin: 0;
    padding-left: 1.3rem;
    display: flex;
    flex-direction: column;
    gap: var(--vigie-pas-3);
    color: var(--vigie-texte-2);
    line-height: 1.6;
}

.vigie-enrolement-etapes p {
    margin: 0 0 var(--vigie-pas-1);
}

/* FOND BLANC EN DUR, thème sombre compris. Un lecteur de QR attend des modules
   sombres sur clair ; inverser les deux rend le code illisible sur une partie
   des téléphones. Le padding EST la zone de silence — sans elle, un QR collé au
   bord d'une surface colorée n'est pas décodé. */
.vigie-qr {
    display: inline-block;
    padding: var(--vigie-pas-2);
    background: #ffffff;
    border: 1px solid var(--vigie-bordure);
    border-radius: var(--vigie-rayon);
    line-height: 0;
}

.vigie-qr svg {
    display: block;
    /* Assez grand pour être scanné à bout de bras, assez petit pour tenir sur
       un écran de 380 px sans déborder la carte. */
    width: min(13rem, 60vw);
    height: auto;
}

/* La clé en clair : le SEUL contenu de l'application qu'on recopie caractère
   par caractère. Interlettrage ouvert, groupes de quatre séparés par des
   espaces (voir CodeTotp.SecretLisible), et retour à la ligne autorisé — elle
   ne doit jamais forcer un défilement horizontal sur mobile. */
.vigie-secret {
    margin: 0;
    padding: var(--vigie-pas-1) var(--vigie-pas-2);
    background: var(--vigie-champ-fond);
    border: 1px dashed var(--vigie-bordure-forte);
    border-radius: var(--vigie-rayon-petit);
    font-family: var(--vigie-police-mono);
    font-size: 1rem;
    letter-spacing: 0.14em;
    color: var(--vigie-texte);
    overflow-wrap: anywhere;
    user-select: all;
}

.vigie-enrolement-validation {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--vigie-pas-2);
}

.vigie-champ-code {
    max-width: 12rem;
}

.vigie-secours {
    display: flex;
    flex-direction: column;
    gap: var(--vigie-pas-2);
    align-items: flex-start;
}

/* Les dix codes, en grille : ils se recopient en colonne, et une liste à puces
   sur toute la largeur ferait perdre la ligne. auto-fill plutôt qu'un nombre
   fixe de colonnes — à 380 px il en reste une seule, sans media query. */
.vigie-secours-liste {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
    gap: var(--vigie-pas-1);
    width: 100%;
    margin: 0;
    padding: 0;
    list-style: none;
}

.vigie-secours-liste li {
    padding: 0.35rem 0.55rem;
    background: var(--vigie-champ-fond);
    border: 1px solid var(--vigie-bordure);
    border-radius: var(--vigie-rayon-petit);
    font-family: var(--vigie-police-mono);
    font-size: 0.95rem;
    letter-spacing: 0.08em;
    color: var(--vigie-texte);
    text-align: center;
    user-select: all;
}

/* --- Configuration : panneaux d'onglet et modules de lecture (lot 11) --------- */

/* Le panneau de l'onglet ouvert. Aucune règle ne masque les trois autres :
   l'attribut `hidden` s'en charge, et pour tout le monde à la fois (pourquoi ils
   restent montés : en tête de VueConfiguration.razor). */
.vigie-onglet-panneau {
    margin-top: var(--vigie-pas-3);
}

/* La LECTURE glissée sous un bloc de réglages (la cadence de veille, réglée
   ailleurs) : elle emprunte tout à .vigie-config-aide et n'ajoute que le tiret
   vertical. Pas de cadre — un cadre en aurait refait un champ, ce qu'elle n'est
   justement plus. */
.vigie-config-lecture {
    margin-top: var(--vigie-pas-2);
    padding-left: 0.6rem;
    border-left: 2px solid var(--vigie-bordure-forte);
}

/* LES DEUX MODULES DE LECTURE, sous les onglets. Le filet de tête sépare « ce
   que je règle » de « ce que je constate » : sans lui, deux cartouches de plus
   à la suite des panneaux se lisaient comme un cinquième et un sixième onglet
   déplié. */
.vigie-lectures {
    margin-top: var(--vigie-pas-4);
    padding-top: var(--vigie-pas-3);
    border-top: 1px solid var(--vigie-bordure);
}

.vigie-lectures-legende {
    margin: 0 0 var(--vigie-pas-3);
    font-family: var(--vigie-police-mono);
    font-size: 0.7rem;
    color: var(--vigie-texte-3);
    text-transform: uppercase;
    letter-spacing: 0.12em;
}

/* Le journal ne doit pas repousser le bas de page hors de portée : il est
   consulté, pas audité. Cinquante lignes restent LUES par un lecteur d'écran
   et atteignables au clavier — le cadre défile, il ne tronque rien. */
.vigie-journal-defilement {
    max-height: 26rem;
    overflow-y: auto;
}
