/*
 * Dash — la feuille de style, entière.
 *
 * La grammaire est celle de hPanel : une barre latérale sombre, un fond clair
 * très légèrement teinté, des cartes blanches à bord fin, et des pastilles d'état
 * qui portent la seule couleur vive de la page. Trois principes tiennent le reste :
 *
 *   · **La couleur est réservée à l'état.** Tout le châssis est gris ; le vert,
 *     l'orange et le rouge ne servent qu'à dire « en ligne », « attention »,
 *     « tombé ». Une interface où le bouton est aussi coloré que l'alerte oblige
 *     à lire pour trouver ce qui va mal.
 *   · **Les chiffres sont tabulaires.** Une colonne de nombres qui tremble à
 *     chaque rafraîchissement est illisible ; `tabular-nums` fige la largeur des
 *     chiffres, et la colonne reste immobile pendant que les valeurs changent.
 *   · **Le thème suit le système**, avec les deux variantes définies en
 *     variables : rien n'est codé en dur dans une règle.
 */

:root {
  color-scheme: light dark;

  /*
   * Deux fonds, et c'est toute la grammaire de la page.
   *
   * `--page` est le violet profond sur lequel flotte l'application ; `--fond`
   * est le gris très clair à l'intérieur de la boîte. Le titre et le fil
   * d'Ariane vivent sur le premier, tout le contenu sur le second : on sait
   * d'un coup d'œil ce qui situe et ce qui informe.
   */
  --page: #2f1c6a;
  --page-texte: #cfc4ef;
  --page-texte-fort: #ffffff;

  --fond: #f4f5f7;
  --carte: #ffffff;
  --carte-alt: #f8f9fb;
  /* Le gris d'un service éteint. Il doit être franchement plus sombre que
     `--fond`, sinon la carte se dissout dans la page et l'arrêt se lit comme un
     bug d'affichage ; et rester assez clair pour que son texte reste net. */
  --carte-eteinte: #e8eaef;
  --carte-eteinte-tete: #dfe2e9;

  /* L'infobulle prend le contre-pied du fond : posée sur du clair, elle est
     sombre. C'est ce qui la fait lire comme une couche au-dessus de la page
     plutôt que comme un bloc de plus dedans. */
  --bulle: #262b38;
  --bulle-texte: #eef0f6;
  --bord: #e3e6ec;
  --texte: #171a21;
  --texte-doux: #5b6472;
  --texte-tres-doux: #8b94a3;

  /* La navigation : un gris à peine plus soutenu que le fond du contenu.
     Assez pour la distinguer, pas assez pour couper la fenêtre en deux. */
  --nav: #eceef3;
  --nav-survol: #e2e5ec;

  --accent: #4c5fd7;
  --accent-doux: #eef0fe;
  --vert: #1f9d55;
  --orange: #c97a0e;
  --rouge: #d33b3b;
  --rouge-doux: #fdeeee;

  /*
   * Une échelle d'espacement, et rien en dehors.
   *
   * Quatre valeurs suffisent à toute la page, et les déclarer ici évite la
   * dérive : sans elles, chaque bloc finit avec ses propres 9, 11 ou 13 px
   * choisis au jugé, et l'interface paraît tassée sans qu'on puisse dire où.
   * Le rapport entre elles compte plus que leur valeur absolue.
   */
  --e1: 8px;    /* entre deux éléments d'un même groupe */
  --e2: 14px;   /* respiration interne d'une carte */
  --e3: 20px;   /* rembourrage d'une carte, écart entre cartes */
  --e4: 30px;   /* entre deux sections */

  --r: 10px;
  --ombre: 0 1px 2px rgb(16 24 40 / 5%), 0 1px 3px rgb(16 24 40 / 6%);
  --police: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
}

@media (prefers-color-scheme: dark) {
  :root {
    /* En sombre, le violet reste mais se retire : sur un écran de nuit, un fond
       saturé fatigue et fait vibrer le texte clair posé dessus. */
    --page: #1a1030;
    --page-texte: #9b8fc4;
    --page-texte-fort: #f2eeff;

    --fond: #0f1117;
    --carte: #171a23;
    --carte-alt: #1c202b;
    /* En sombre, éteint veut dire plus SOMBRE que la carte allumée : c'est le
       retrait qui compte, pas la direction. */
    --carte-eteinte: #10131a;
    --carte-eteinte-tete: #0c0e14;

    /* En sombre elle ne peut plus contraster par l'obscurité : elle s'éclaircit
       juste assez pour se détacher, et garde son texte clair. */
    --bulle: #333a4d;
    --bulle-texte: #f2f4fa;
    --bord: #272c39;
    --texte: #e9ecf2;
    --texte-doux: #98a1b2;
    --texte-tres-doux: #6b7485;

    --nav: #13161e;
    --nav-survol: #1b1f2a;

    --accent: #7b8bff;
    --accent-doux: #1b2040;
    --vert: #35c977;
    --orange: #e0a33c;
    --rouge: #ef6060;
    --rouge-doux: #2a1618;

    --ombre: 0 1px 2px rgb(0 0 0 / 30%);
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font: 14px/1.45 var(--police);
  color: var(--texte);
  background: var(--page);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { margin: 0; letter-spacing: -0.011em; }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
.num { text-align: right; font-variant-numeric: tabular-nums; }
.rouge { color: var(--rouge); }

/* ─── Connexion ───────────────────────────────────────────────────────────── */

.connexion {
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: 24px;
}

.carte-connexion {
  width: 100%;
  max-width: 340px;
  background: var(--carte);
  border: 1px solid var(--bord);
  border-radius: 14px;
  box-shadow: var(--ombre);
  padding: 28px 24px;
}

.marque { text-align: center; color: var(--accent); margin-bottom: 22px; }
.marque h1 { font-size: 21px; margin-top: 8px; color: var(--texte); }
.marque p { margin: 2px 0 0; font-size: 13px; color: var(--texte-tres-doux); }

label { display: block; margin-bottom: 16px; }
label > span { display: block; font-size: 12.5px; font-weight: 600; color: var(--texte-doux); margin-bottom: 6px; }

input[type=password], input[type=text], input[type=search] {
  width: 100%;
  padding: 10px 12px;
  font: inherit;
  color: var(--texte);
  background: var(--carte-alt);
  border: 1px solid var(--bord);
  border-radius: 8px;
}
input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

button {
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  border: 0;
  border-radius: 8px;
  padding: 10px 16px;
  color: #fff;
  background: var(--accent);
}
button.secondaire { background: var(--carte-alt); color: var(--texte); border: 1px solid var(--bord); }
.carte-connexion button { width: 100%; }

.alerte {
  margin: 0 0 16px;
  padding: 9px 12px;
  font-size: 13px;
  color: var(--rouge);
  background: var(--rouge-doux);
  border: 1px solid color-mix(in oklab, var(--rouge) 25%, transparent);
  border-radius: 8px;
}

/* ─── Châssis : une barre sur le violet, puis UNE fenêtre ────────────────── */

/*
 * La page ne défile pas. La fenêtre occupe la hauteur de l'écran, et c'est le
 * contenu **à l'intérieur** qui défile : la barre latérale et le titre restent
 * donc visibles en permanence, comme dans une application de bureau. Une page
 * qui défile en entier emporterait la navigation hors de l'écran au premier
 * mouvement.
 */
html, body { height: 100%; overflow: hidden; }

.barre-haute {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 11px 22px 10px;
  color: var(--page-texte);
}

.marque-haute {
  display: flex; align-items: center; gap: 8px;
  color: var(--page-texte-fort);
  font-weight: 650; font-size: 14.5px;
}

.fil {
  display: flex; align-items: center; gap: 5px;
  margin: 0;
  font-size: 12.5px; font-weight: 550;
}
.fil svg { width: 13px; height: 13px; opacity: 0.5; }
.fil-actif { color: var(--page-texte-fort); }

.barre-haute-fin { margin-left: auto; }
.quitter {
  background: rgb(255 255 255 / 10%);
  color: var(--page-texte-fort);
  font-size: 12.5px; font-weight: 600;
  padding: 6px 13px; border-radius: 7px;
}
.quitter:hover { background: rgb(255 255 255 / 18%); }

/*
 * La fenêtre : navigation et contenu dans le même cadre.
 *
 * `overflow: hidden` sur la fenêtre et `overflow-y: auto` sur le contenu : c'est
 * ce couple qui fait que le cadre garde ses coins arrondis pendant que le
 * contenu glisse dessous. Sans le premier, le contenu déborderait par-dessus les
 * angles au moindre défilement.
 */
.fenetre {
  display: flex;
  height: calc(100dvh - 46px);
  margin: 0 14px 14px;
  background: var(--carte);
  border-radius: 16px;
  overflow: hidden;
  box-shadow: 0 6px 30px rgb(0 0 0 / 22%);
}

/*
 * La barre latérale est grise, pas sombre : dans une fenêtre unique, un pan
 * noir collé à un pan blanc découpe l'objet en deux au lieu de le faire tenir.
 * Le gris la distingue sans la détacher.
 */
.lateral {
  width: 212px;
  flex: 0 0 212px;
  background: var(--nav);
  border-right: 1px solid var(--bord);
  padding: var(--e3) 12px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  overflow-y: auto;
}

.lien {
  display: flex; align-items: center; gap: 11px;
  padding: 10px 12px;
  border-radius: 8px;
  color: var(--texte-doux);
  font-size: 13.5px; font-weight: 550;
  text-decoration: none;
  background: none; border: 0; width: 100%; cursor: pointer;
  transition: background 120ms ease, color 120ms ease;
}
.lien:hover { background: var(--nav-survol); color: var(--texte); }
/* L'onglet courant est blanc, comme la zone de contenu : il s'y raccorde, et
   c'est ce raccord qui dit « c'est cette section que tu regardes ». */
.lien.actif {
  background: var(--carte);
  color: var(--texte);
  box-shadow: var(--ombre);
  font-weight: 650;
}
.lien-icone { width: 18px; text-align: center; font-size: 15px; }
.lien-nom { flex: 1; text-align: left; }
.lien-compte {
  font-size: 11.5px; font-variant-numeric: tabular-nums;
  color: var(--texte-tres-doux);
  background: var(--fond);
  padding: 1px 6px; border-radius: 20px;
}

.principal {
  flex: 1;
  min-width: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--fond);
  padding: var(--e4) var(--e4) 48px;
}

.entete { margin-bottom: var(--e3); }
.entete h1 { font-size: 24px; letter-spacing: -0.02em; }
.sous-titre { margin: 3px 0 0; font-size: 12.5px; color: var(--texte-tres-doux); }

/* ─── Cartes de métriques ─────────────────────────────────────────────────── */

/*
 * Trois cartes par ligne, donc deux rangées de trois.
 *
 * C'était auparavant `auto-fit` : le nombre de colonnes suivait la largeur, et
 * les six cartes s'alignaient en une seule bande sur un grand écran — six
 * vignettes basses et larges qu'on parcourt de gauche à droite sans jamais
 * savoir où s'arrête la lecture. À trois, chaque carte garde une forme stable
 * quelle que soit la fenêtre, et les deux rangées se lisent comme un bloc.
 */
.jauges {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--e2);
  width: 100%;
}

.metrique {
  background: var(--carte);
  border: 1px solid var(--bord);
  border-radius: 14px;
  box-shadow: var(--ombre);
  padding: var(--e3) var(--e3) calc(var(--e3) + 2px);
  min-height: 124px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}

.metrique > header {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  font-size: 13px;
  font-weight: 550;
  color: var(--texte-doux);
  line-height: 1.3;
}
.metrique-chevron { width: 14px; height: 14px; flex: 0 0 14px; margin-top: 2px; color: var(--texte-tres-doux); }

/*
 * La valeur à gauche, le graphique à droite, et le graphique prend ce qui reste.
 *
 * `min-width: 0` sur le graphique est indispensable : un enfant de flex refuse
 * par défaut de descendre sous sa taille intrinsèque, et le SVG pousserait la
 * carte jusqu'à déborder de la grille.
 */
.metrique-corps {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--e2);
  margin-top: var(--e2);
}

.metrique-valeur {
  margin: 0;
  font-size: 27px;
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.metrique-valeur small {
  font-size: 14px;
  font-weight: 500;
  color: var(--texte-tres-doux);
  letter-spacing: 0;
}

.metrique-graphe {
  flex: 1 1 auto;
  min-width: 0;
  max-width: 130px;
  height: 34px;
  align-self: center;
}
.metrique-graphe svg { width: 100%; height: 100%; display: block; overflow: visible; }

/* `vector-effect` garde le trait à 1,6 px malgré l'étirement non uniforme du
   viewBox : sans lui, la courbe est épaisse sur une carte large et fine sur une
   étroite, alors que c'est la même courbe. */
.spark-trait {
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.6;
  stroke-linejoin: round;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}
.spark-attente {
  stroke: var(--bord);
  stroke-width: 1.4;
  stroke-dasharray: 3 3;
  vector-effect: non-scaling-stroke;
}
.spark-haut { stop-color: var(--accent); stop-opacity: 0.26; }
.spark-bas  { stop-color: var(--accent); stop-opacity: 0; }

.metrique-anneau { flex: 0 0 52px; width: 52px; height: 52px; align-self: center; }
.metrique-anneau svg { width: 100%; height: 100%; transform: rotate(-90deg); display: block; }
.anneau-piste { fill: none; stroke: var(--fond); stroke-width: 5; }
.anneau-arc {
  fill: none;
  stroke: var(--accent);
  stroke-width: 5;
  stroke-linecap: round;
  transition: stroke-dasharray 500ms ease;
}
/* Au-delà de 85 %, l'anneau passe au rouge : c'est le seuil auquel le dashboard
   enverra une alerte, et la page doit dire la même chose que la notification. */
.metrique-anneau.chaud .anneau-arc { stroke: var(--rouge); }

.bandeau-hors-ligne:empty { display: none; }
.bandeau-hors-ligne {
  margin: 0 0 14px; padding: 9px 13px; font-size: 13px;
  color: var(--rouge); background: var(--rouge-doux);
  border: 1px solid color-mix(in oklab, var(--rouge) 25%, transparent);
  border-radius: 8px;
}

/* ─── Panneau : une carte par application ─────────────────────────────────── */

.panneau { margin-top: var(--e4); }
.panneau-entete { margin-bottom: var(--e2); }
.panneau-entete h2 { font-size: 16px; }

.groupe + .groupe { margin-top: var(--e4); }
.groupe-titre {
  font-size: 11.5px; font-weight: 650;
  text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--texte-tres-doux);
  margin-bottom: var(--e2);
}

.cartes { display: grid; gap: var(--carte-ecart); }

/*
 * Chaque application est une carte, et pourtant l'ensemble se lit encore comme
 * un tableau.
 *
 * Le secret tient en une variable : toutes les lignes, dans toutes les cartes,
 * partagent `--colonnes`. Comme les cartes ont la même largeur et le même
 * rembourrage, les colonnes tombent au même endroit d'une carte à l'autre et
 * l'œil les suit de haut en bas — sans qu'aucune balise <table> ne l'impose.
 */
.groupe {
  /*
   * La dernière colonne est de largeur FIXE, pas `auto`.
   *
   * C'est tout l'enjeu de l'alignement : une ligne seule porte quatre boutons,
   * une ligne imbriquée deux ou trois, et un `auto` faisait alors varier la
   * largeur des actions d'une carte à l'autre — ce qui repoussait toutes les
   * colonnes précédentes et décalait « en ligne », le CPU et la mémoire d'une
   * ligne sur deux. La largeur est donc celle de quatre boutons, une fois pour
   * toutes : 4 × 27 px + 3 × 4 px d'écart.
   */
  --colonnes: 18px minmax(150px, 1fr) 86px 58px 76px 62px 66px 138px;

  /*
   * Le rembourrage des cartes, déclaré ici et nulle part ailleurs.
   *
   * Il ne suit pas l'échelle générale parce qu'il ne fait pas le même travail :
   * `--e2` sépare deux éléments, celui-ci porte la ligne la plus regardée de la
   * page. Serré à quatorze pixels, dix-sept applications formaient un pavé où
   * l'œil ne trouvait plus le début d'une ligne. Les trois mesures vivent
   * ensemble parce qu'elles se règlent ensemble : l'air DANS la carte et l'air
   * ENTRE les cartes doivent bouger du même geste.
   */
  --carte-y: 18px;    /* au-dessus et en dessous d'une ligne */
  --carte-x: 24px;    /* à gauche et à droite, et entre deux colonnes */
  --carte-ecart: 12px; /* entre deux cartes de la pile */
}

/*
 * Les titres de colonnes.
 *
 * Ils sont posés sur le fond de la page, pas dans une carte, et reprennent la
 * grille du groupe au pixel près : le `+ 1px` de rembourrage compense la bordure
 * des cartes en dessous, sans quoi chaque titre tomberait un pixel à gauche de
 * sa colonne — un décalage qu'on ne nomme pas mais qu'on voit.
 */
.entete-colonnes {
  display: grid;
  grid-template-columns: var(--colonnes);
  gap: var(--carte-x);
  padding: 0 calc(var(--carte-x) + 1px) 9px;
  font-size: 11px;
  font-weight: 650;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--texte-tres-doux);
  white-space: nowrap;
}
.entete-colonnes .droite { text-align: right; }

/*
 * Un titre de colonne qui explique ce qu'il mesure.
 *
 * Le pointillé sous le mot est la seule promesse faite à l'œil : sans lui,
 * personne ne survole un titre de tableau, et l'explication n'existerait que
 * pour qui la cherche par hasard. `cursor: help` confirme au passage.
 */
.titre-col {
  font: inherit;
  cursor: help;
  border-bottom: 1px dotted color-mix(in oklab, var(--texte-tres-doux) 70%, transparent);
  padding-bottom: 1px;
  transition: color 120ms ease, border-color 120ms ease;
}
.titre-col:hover,
.titre-col:focus-visible { color: var(--texte-doux); border-bottom-color: var(--texte-doux); }
.titre-col:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 2px; }

/*
 * La bulle : une seule pour toute la page, placée par le JavaScript.
 *
 * `pointer-events: none` n'est pas un détail de confort : sans lui, la bulle qui
 * apparaît sous le curseur devient elle-même la cible du survol, le titre cesse
 * d'être survolé, la bulle se ferme — et se rouvre aussitôt, indéfiniment.
 */
.infobulle {
  position: fixed;
  top: 0; left: 0;
  z-index: 60;
  max-width: 320px;
  padding: 10px 13px;
  font-size: 12.5px;
  line-height: 1.5;
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  white-space: normal;
  color: var(--bulle-texte);
  background: var(--bulle);
  border-radius: 10px;
  box-shadow: 0 10px 30px rgb(8 10 16 / 30%);
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  transition: opacity 130ms ease, transform 130ms ease, visibility 130ms;
}
.infobulle.visible { opacity: 1; visibility: visible; transform: none; }

/* La flèche est un carré pivoté du même fond ; sa position horizontale est
   calculée avec celle de la bulle, pour qu'elle vise toujours le mot même quand
   la bulle a dû glisser pour ne pas sortir de l'écran. */
.infobulle::before {
  content: "";
  position: absolute;
  top: -4px;
  left: var(--fleche-x, 18px);
  width: 10px; height: 10px;
  background: inherit;
  border-radius: 2px;
  transform: rotate(45deg);
}
/* Bulle passée au-dessus faute de place en dessous : la flèche change de bord,
   sinon elle pointerait vers le vide au lieu du bouton. */
.infobulle.dessus::before { top: auto; bottom: -4px; }
.infobulle.dessus { transform: translateY(4px); }
.infobulle.dessus.visible { transform: none; }

@media (prefers-reduced-motion: reduce) {
  .infobulle { transition: none; }
}

/* ─── Notifications d'action ────────────────────────────────────────────────
 *
 * Une action peut durer : un arrêt pm2 prend une seconde, un déploiement trente.
 * Entre le clic et la réponse, la seule chose que voyait l'utilisateur était un
 * bouton légèrement pâli — trop discret pour un serveur qu'on vient d'arrêter.
 * La notification dit ce qui est en cours, puis ce qui s'est passé, et s'en va.
 *
 * Le détail, lui, reste sous le tableau : la notification annonce, elle
 * n'explique pas. Une erreur de déploiement tient en dix lignes de sortie, et
 * une bulle qui disparaît toute seule n'est pas l'endroit pour les lire.
 */

.notifs {
  position: fixed;
  top: 58px;
  right: 26px;
  z-index: 70;
  display: grid;
  gap: 10px;
  justify-items: end;
  pointer-events: none; /* la pile ne bloque pas les clics sur ce qu'elle survole */
}

.notif {
  pointer-events: auto; /* la notification, elle, se ferme au clic */
  display: flex;
  align-items: flex-start;
  gap: 10px;
  width: min(340px, calc(100vw - 32px));
  padding: 12px 14px;
  background: var(--carte);
  border: 1px solid var(--bord);
  border-left: 3px solid var(--texte-tres-doux);
  border-radius: var(--r);
  box-shadow: 0 10px 30px rgb(8 10 16 / 18%);
  cursor: pointer;
  animation: notif-entre 200ms ease both;
}
.notif.ok { border-left-color: var(--vert); }
.notif.echec { border-left-color: var(--rouge); }
.notif.part { animation: notif-sort 220ms ease both; }

@keyframes notif-entre {
  from { opacity: 0; transform: translateX(18px) scale(0.98); }
  to   { opacity: 1; transform: none; }
}
@keyframes notif-sort {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: translateX(18px); }
}

.notif-corps { flex: 1; min-width: 0; }
.notif-titre { font-size: 13px; font-weight: 650; overflow-wrap: anywhere; }
.notif-detail { margin: 2px 0 0; font-size: 12px; color: var(--texte-doux); }

/*
 * L'anneau qui tourne. Il ne mesure rien — on ignore combien de temps prendra un
 * `pm2 stop` — et c'est précisément pourquoi il tourne au lieu de se remplir :
 * une barre de progression qui avancerait à l'aveugle serait un mensonge poli.
 */
.notif-etat {
  flex: 0 0 16px;
  width: 16px; height: 16px;
  margin-top: 1px;
  display: grid; place-items: center;
  font-size: 13px; line-height: 1;
}
.notif-rouet {
  width: 14px; height: 14px;
  border: 2px solid color-mix(in oklab, var(--texte-tres-doux) 35%, transparent);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: rouet 700ms linear infinite;
}
@keyframes rouet { to { transform: rotate(360deg); } }

.notif.ok .notif-etat { color: var(--vert); }
.notif.echec .notif-etat { color: var(--rouge); }

@media (prefers-reduced-motion: reduce) {
  .notif { animation: none; }
  .notif-rouet { animation-duration: 2s; }
}

@media (max-width: 780px) {
  /* Sous le pouce, la pile prend la largeur et se range sous la barre du haut. */
  .notifs { top: 52px; right: 10px; left: 10px; justify-items: stretch; }
  .notif { width: auto; }
}

.carte-app {
  background: var(--carte);
  border: 1px solid var(--bord);
  border-radius: 11px;
  box-shadow: var(--ombre);
  overflow: hidden;
}
/*
 * Allumé : blanc. Éteint : gris. C'est tout, et c'est voulu.
 *
 * La version précédente cerclait de rouge toute carte « en anomalie » — et comme
 * l'anomalie comptait aussi les redémarrages, presque toutes les cartes étaient
 * cerclées. Un écran où tout alarme n'alarme plus, et le rouge ne disait même
 * pas la bonne chose : une application qu'on a arrêtée soi-même n'est pas une
 * panne. Le fond, lui, se lit sans vocabulaire : ce qui est pâle ne tourne pas.
 *
 * Un dossier partiel (2/3 en ligne) reste blanc : il tourne. Ce sont ses lignes
 * éteintes qui se grisent, une à une, plus bas.
 */
.carte-app.etat-arrete,
.carte-app.etat-erreur {
  background: var(--carte-eteinte);
  box-shadow: none;
}
.carte-app.etat-arrete .carte-nom,
.carte-app.etat-erreur .carte-nom { color: var(--texte-doux); }
.carte-app.etat-arrete .carte-tete,
.carte-app.etat-erreur .carte-tete { background: var(--carte-eteinte-tete); }

.carte-tete,
.ligne {
  display: grid;
  grid-template-columns: var(--colonnes);
  align-items: center;
  /*
   * L'écart entre colonnes fait plus pour la lisibilité qu'aucune bordure : des
   * chiffres séparés de dix pixels se lisent comme un seul bloc, et l'œil ne
   * sait plus à quelle colonne appartient « 68 Mo ».
   */
  gap: var(--carte-x);
  padding: var(--carte-y) var(--carte-x);
  font-size: 13.5px;
}

/* L'en-tête d'une carte à plusieurs process : légèrement teinté, pour qu'on
   voie d'un coup où commence une application et où finit la précédente. */
.carte-tete {
  background: var(--fond);
  border-bottom: 1px solid var(--bord);
  font-weight: 650;
}
/*
 * L'en-tête d'un dossier occupe exactement les mêmes colonnes que ses lignes :
 * « 0/2 » sous « arrêté », le CPU total sous les CPU, la mémoire totale sous les
 * mémoires. Le résumé se lit alors dans le prolongement du détail, au lieu de
 * flotter à droite sans rapport avec ce qu'il totalise.
 */
.carte-compte {
  font-size: 12.5px; font-weight: 650;
  font-variant-numeric: tabular-nums;
  color: var(--texte-doux);
  white-space: nowrap;
}
.carte-agregat {
  font-size: 12.5px; font-weight: 650;
  font-variant-numeric: tabular-nums;
  color: var(--texte-doux);
  text-align: right;
  white-space: nowrap;
}

.ligne + .ligne { border-top: 1px solid var(--bord); }

/* Une ligne éteinte dans une carte allumée : le gris descend au niveau du
   process, pour qu'un dossier 2/3 montre lequel des trois manque. */
.ligne.etat-arrete,
.ligne.etat-erreur { background: var(--carte-eteinte); }
.ligne.etat-arrete .nom-proc,
.ligne.etat-erreur .nom-proc { color: var(--texte-doux); }

/*
 * Le survol passe APRÈS les états, et pas par hasard : à spécificité égale
 * c'est la dernière règle qui gagne, et écrite plus haut elle n'aurait plus
 * rien fait sur une ligne éteinte — le pointeur aurait survolé sans réponse
 * exactement les lignes qu'on vient cliquer pour redémarrer.
 */
.ligne:hover { background: color-mix(in oklab, var(--texte) 6%, transparent); }

/*
 * Le retrait des process d'un dossier porte sur le NOM seulement, jamais sur la
 * ligne entière.
 *
 * Un `padding-left` sur la ligne déplaçait le début de la grille, donc toutes
 * ses colonnes : les chiffres d'une ligne imbriquée tombaient douze pixels à
 * droite de ceux d'une ligne seule. Le décalage doit dire « ceci appartient à ce
 * qui précède » sans déranger la lecture en colonnes.
 */
.carte-tete ~ .ligne .col-nom { padding-left: var(--carte-y); }

.col-etat { display: flex; align-items: center; }
.col-nom { min-width: 0; display: flex; align-items: baseline; gap: 7px; }
.carte-nom { font-weight: 650; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nom-proc {
  font-size: 12px;
  color: var(--texte-tres-doux);
  font-family: var(--mono);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ligne.seule .nom-proc { flex: 0 1 auto; }

.col-statut { font-size: 12.5px; color: var(--texte-doux); white-space: nowrap; }
.col-num {
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-size: 12.5px;
  color: var(--texte-doux);
  white-space: nowrap;
}
.col-actions { display: flex; justify-content: flex-end; min-width: 0; }

.pastille {
  display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  background: var(--texte-tres-doux); flex: 0 0 8px;
}
.pastille.en-ligne { background: var(--vert); box-shadow: 0 0 0 3px color-mix(in oklab, var(--vert) 20%, transparent); }
.pastille.arrete   { background: var(--texte-tres-doux); }
.pastille.partiel  { background: var(--orange); box-shadow: 0 0 0 3px color-mix(in oklab, var(--orange) 20%, transparent); }
.pastille.erreur   { background: var(--rouge); box-shadow: 0 0 0 3px color-mix(in oklab, var(--rouge) 20%, transparent); }

.actions { display: flex; gap: 6px; justify-content: flex-end; }
.ic {
  display: inline-grid; place-items: center;
  width: 30px; height: 30px; padding: 0;
  font-size: 13px; line-height: 1;
  color: var(--texte-doux); background: var(--fond);
  border: 1px solid var(--bord); border-radius: 7px;
  text-decoration: none; cursor: pointer;
  transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}
.ic:hover { background: var(--accent-doux); color: var(--accent); border-color: color-mix(in oklab, var(--accent) 35%, transparent); }
.ic.danger:hover { background: var(--rouge-doux); color: var(--rouge); border-color: color-mix(in oklab, var(--rouge) 35%, transparent); }
.ic.vert:hover { background: color-mix(in oklab, var(--vert) 12%, transparent); color: var(--vert); border-color: color-mix(in oklab, var(--vert) 35%, transparent); }
.ic[aria-busy=true] { opacity: 0.45; pointer-events: none; }
.rouge { color: var(--rouge); }

/* ─── Onglet vide ─────────────────────────────────────────────────────────── */

.vide {
  background: var(--carte); border: 1px solid var(--bord);
  border-radius: var(--r); box-shadow: var(--ombre);
  padding: 56px 28px; text-align: center;
}
.vide-icone { display: block; font-size: 30px; margin-bottom: 10px; opacity: 0.5; }
.vide h2 { font-size: 17px; margin-bottom: 6px; }
.vide p { margin: 0 auto; max-width: 46ch; font-size: 13.5px; color: var(--texte-doux); }
.vide-note { margin-top: 14px !important; font-size: 12px !important; color: var(--texte-tres-doux) !important; }

/* ─── Retour d'action, logs ───────────────────────────────────────────────── */

#retour:empty { display: none; }
#retour { margin-top: var(--e3); display: grid; gap: var(--e2); }

.retour {
  background: var(--carte); border: 1px solid var(--bord);
  border-left: 3px solid var(--texte-tres-doux);
  border-radius: var(--r); box-shadow: var(--ombre);
  padding: var(--e2) var(--e3); font-size: 13px;
}
.retour.ok { border-left-color: var(--vert); }
.retour.echec { border-left-color: var(--rouge); }
.retour-tete { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.retour p { margin: 6px 0 0; color: var(--texte-doux); }
.rangee { display: flex; gap: 8px; justify-content: flex-end; margin-top: 16px; }
.rangee-serree { display: flex; gap: 8px; align-items: center; }

.etapes { margin: 10px 0 0; padding: 0; list-style: none; display: grid; gap: 8px; }
.etapes li { border: 1px solid var(--bord); border-radius: 8px; padding: 8px 10px; background: var(--carte-alt); }
.etapes li.echec { border-color: color-mix(in oklab, var(--rouge) 35%, transparent); }
.etape-tete { display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap; }
.etape-titre { font-weight: 650; }
.etape-tete code { font: 12px var(--mono); color: var(--texte-tres-doux); }
.etape-duree { margin-left: auto; font-size: 11.5px; color: var(--texte-tres-doux); font-variant-numeric: tabular-nums; }

pre {
  margin: 7px 0 0; padding: 9px 11px;
  font: 12px/1.5 var(--mono);
  white-space: pre-wrap; word-break: break-word;
  background: var(--fond); border: 1px solid var(--bord); border-radius: 7px;
  max-height: 280px; overflow: auto;
}
.journal { max-height: 420px; }
.logs-titre { font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--texte-tres-doux); margin-top: 12px; }
.filtre { display: flex; align-items: center; gap: 5px; margin: 0; font-size: 12.5px; color: var(--texte-doux); }
.filtre > input { width: auto; }
.logs input[type=search] { width: 180px; padding: 5px 9px; font-size: 12.5px; }
/* Une ligne masquée par la recherche : `hidden` ne suffit pas sur un <pre>,
   dont le white-space conserverait le saut de ligne et laisserait un trou. */
.journal .masque { display: none; }
.journal mark { background: color-mix(in oklab, var(--accent) 30%, transparent); color: inherit; border-radius: 2px; }

/* ─── Dialogue TOTP ───────────────────────────────────────────────────────── */

dialog {
  border: 1px solid var(--bord); border-radius: 14px;
  background: var(--carte); color: var(--texte);
  box-shadow: 0 12px 40px rgb(16 24 40 / 22%);
  padding: 22px; width: min(92vw, 320px);
}
dialog::backdrop { background: rgb(8 10 16 / 55%); backdrop-filter: blur(2px); }
dialog h2 { font-size: 16px; }
#totp-code {
  margin-top: 14px; text-align: center;
  font: 600 24px/1 var(--mono); letter-spacing: 0.22em;
  padding: 12px;
}

/* ─── Téléphone : la barre latérale descend sous le pouce ─────────────────── */



/* ─── Largeurs intermédiaires ─────────────────────────────────────────────── */

/*
 * La grille complète réclame environ 1 175 px : huit colonnes (654 px), leurs
 * sept écarts, le rembourrage des cartes et les 213 px de barre latérale. En
 * dessous, elle déborderait — et comme les cartes coupent ce qui dépasse, ce
 * sont les boutons d'action qui disparaîtraient, sans rien pour le signaler.
 *
 * Les seuils sont donc calculés, pas choisis. Le palier large bascule à 1 240 px
 * pour un besoin de 1 173 ; le palier intermédiaire, resserré à 16 px de
 * rembourrage, tient dans 1 045 px et bascule en blocs à 1 080. Élargir le
 * rembourrage des cartes, c'est augmenter ces besoins — les trois chiffres se
 * relisent ensemble, sinon la largeur juste sous le seuil se met à rogner les
 * boutons d'action sans rien dire.
 *
 * Deux paliers, et ils répondent à deux questions différentes : celui-ci à
 * « le tableau tient-il ? », celui d'au-dessus à « est-ce un téléphone ? ».
 * Les confondre — ce que faisait la première version — laissait un trou entre
 * 780 et 1 100 px, précisément la taille d'une fenêtre de portable à moitié
 * ouverte.
 */


/* ─── Onglets Réseau, Bases, Système, Jeux, Agents ────────────────────────── */

/*
 * Chaque onglet a sa propre grille, mais toutes suivent la même règle : la
 * pastille, le nom, puis ce qui est propre au sujet, et les actions à droite en
 * largeur fixe. C'est ce dernier point qui garde les colonnes alignées d'une
 * carte à l'autre — voir la note sur `--colonnes` plus haut.
 */

.col-domaines { display: flex; flex-wrap: wrap; gap: 6px; min-width: 0; }
.domaine {
  font-size: 12px; font-family: var(--mono);
  color: var(--texte-doux);
  background: var(--fond);
  border: 1px solid var(--bord);
  padding: 1px 7px; border-radius: 20px;
  white-space: nowrap;
}
.col-cert { font-weight: 600; }
.col-description {
  font-size: 12.5px; color: var(--texte-tres-doux);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0;
}
.vide-cell { font-size: 12.5px; color: var(--texte-tres-doux); font-style: italic; }

.etiquette {
  font-size: 11px; font-weight: 600;
  color: var(--accent); background: var(--accent-doux);
  padding: 1px 7px; border-radius: 20px; white-space: nowrap;
}
.etiquette-alerte { color: var(--orange); background: color-mix(in oklab, var(--orange) 12%, transparent); }

.pied-onglet {
  display: flex; align-items: center; gap: var(--e2);
  margin-top: var(--e3); flex-wrap: wrap;
}
.pied-danger { padding-top: var(--e3); border-top: 1px solid var(--bord); }
.note { margin: 0; font-size: 12.5px; color: var(--texte-tres-doux); }
.note-bloc { margin-top: var(--e2); }
.bouton-doux {
  background: var(--carte); color: var(--texte);
  border: 1px solid var(--bord); font-size: 13px; padding: 8px 14px;
}
.bouton-doux:hover { border-color: var(--accent); color: var(--accent); }
.bouton-danger { background: var(--rouge); font-size: 13px; padding: 8px 16px; }

/* ─── Serveurs de jeux ────────────────────────────────────────────────────── */

.carte-jeu { margin-bottom: var(--e1); }
.tete-jeu { grid-template-columns: 18px 1fr auto; }
.corps-jeu { padding: var(--e3); display: grid; gap: var(--e2); }

.joueurs { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.joueurs-compte { font-size: 25px; font-weight: 700; font-variant-numeric: tabular-nums; }
.joueurs-compte small { font-size: 14px; font-weight: 500; color: var(--texte-tres-doux); }
.joueurs-libelle { font-size: 12.5px; color: var(--texte-doux); }
.joueurs-liste { display: flex; gap: 6px; list-style: none; margin: 0; padding: 0; flex-wrap: wrap; }
.joueurs-liste li {
  font-size: 12.5px; font-weight: 600;
  background: color-mix(in oklab, var(--vert) 12%, transparent);
  color: var(--vert); padding: 2px 9px; border-radius: 20px;
}

.console { display: flex; gap: var(--e1); }
.console input { flex: 1; font-family: var(--mono); font-size: 13px; }
.console button { white-space: nowrap; }

/* ─── Conversation d'un agent ─────────────────────────────────────────────── */

/*
 * La page occupe toute la hauteur, et c'est le fil SEUL qui défile.
 *
 * Sans cela, la conversation allongeait la page et poussait le champ de saisie
 * toujours plus bas : pour répondre, il fallait d'abord faire défiler jusqu'en
 * bas. Trois bandes empilées — en-tête, fil, saisie — dont seule celle du milieu
 * bouge. `:has` porte la contrainte jusqu'au conteneur qui défile d'habitude ;
 * là où il manque, la page retombe simplement sur son défilement normal, qui
 * reste utilisable.
 */
.principal:has(.chat-vue) { overflow: hidden; display: flex; flex-direction: column; }
.principal:has(.chat-vue) #panneau { min-height: 0; flex: 1; display: flex; }
.principal:has(.chat-vue) #retour:empty { display: none; }

.chat-vue {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: var(--e2);
}

.chat-entete {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--e2); flex-wrap: wrap;
}
.chat-entete h2 { font-size: 16px; }
.chat-qui { min-width: 0; }

.fil-chat {
  flex: 1;
  min-height: 120px;
  display: grid;
  align-content: start;
  gap: 10px;
  overflow-y: auto;
  padding: var(--e2) 6px var(--e2) 0;
  border-top: 1px solid var(--bord);
  border-bottom: 1px solid var(--bord);
}

/* Le fil hérité de l'ancien encart sous le tableau : borné en hauteur, puisqu'il
   partageait la page avec autre chose. */
.chat .fil-chat {
  max-height: 460px;
  margin-top: var(--e2);
  border: 0;
  flex: none;
}

/*
 * La saisie ne défile pas avec le fil : elle reste sous les yeux, à sa place,
 * qu'on soit au début de la conversation ou à sa fin.
 */
.chat-saisie {
  display: flex;
  align-items: flex-end;
  gap: var(--e1);
}
.chat-saisie textarea {
  flex: 1;
  min-height: 42px;
  max-height: 180px;
  padding: 11px 13px;
  font: 13.5px/1.5 var(--police);
  color: var(--texte);
  background: var(--carte);
  border: 1px solid var(--bord);
  border-radius: 11px;
  resize: none;
  overflow-y: auto;
}
.chat-saisie textarea:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-color: transparent;
}
.chat-envoyer {
  flex: 0 0 42px;
  width: 42px; height: 42px;
  display: grid; place-items: center;
  color: #fff;
  background: var(--accent);
  border: 0; border-radius: 11px;
  cursor: pointer;
  transition: filter 120ms ease;
}
.chat-envoyer:hover { filter: brightness(1.08); }
.chat-envoyer[aria-busy=true] { opacity: 0.5; pointer-events: none; }

.chat-pied { margin: 0; font-size: 11.5px; color: var(--texte-tres-doux); }
.chat-pied-mute {
  padding: 11px 13px;
  background: var(--fond);
  border: 1px dashed var(--bord);
  border-radius: 11px;
  color: var(--texte-doux);
  font-size: 12.5px;
}

/*
 * Les bulles ne prennent jamais toute la largeur : une ligne de texte de 1 200 px
 * est illisible, l'œil perd le début de la ligne suivante. 68 caractères est la
 * mesure habituelle du confort de lecture.
 */
.bulle { max-width: min(68ch, 88%); }
.bulle.user { justify-self: end; }
.bulle-tete {
  display: flex; justify-content: space-between; gap: 12px;
  font-size: 11px; font-weight: 650; color: var(--texte-tres-doux);
  margin-bottom: 3px;
}
.bulle-tete span { font-weight: 500; font-variant-numeric: tabular-nums; }
.bulle-texte {
  padding: 9px 13px; border-radius: 12px;
  font-size: 13px; line-height: 1.5;
  white-space: pre-wrap; word-break: break-word;
  background: var(--carte); border: 1px solid var(--bord);
}
.bulle.user .bulle-texte {
  background: var(--accent-doux);
  border-color: color-mix(in oklab, var(--accent) 25%, transparent);
}

/* Un appel d'outil est un repère de rythme, pas un message : il tient sur une
   ligne discrète et ne porte jamais ses arguments. */
.bulle.outil { justify-self: center; max-width: none; }
.outil-nom {
  font-size: 11px; font-family: var(--mono);
  color: var(--texte-tres-doux);
  background: var(--fond); border: 1px solid var(--bord);
  padding: 2px 9px; border-radius: 20px;
}

.alerte-douce {
  color: var(--orange);
  background: color-mix(in oklab, var(--orange) 9%, transparent);
  border-color: color-mix(in oklab, var(--orange) 30%, transparent);
}


/* ─── Icônes Lucide ───────────────────────────────────────────────────────── */

/*
 * Les icônes sortent du binaire sans dimension : c'est ici qu'elles en prennent
 * une, selon l'endroit où elles se posent. `em` plutôt que `px` — une icône
 * suit alors la taille du texte qui l'accompagne, et un bouton qu'on agrandit
 * sur téléphone emporte son icône avec lui.
 */
.lucide {
  width: 1.15em;
  height: 1.15em;
  flex: 0 0 auto;
  display: block;
  /* Le trait de Lucide est prévu pour 24 px ; à 14 px il paraît épais. */
  stroke-width: 2;
}

.ic .lucide { width: 15px; height: 15px; stroke-width: 1.9; }
.lien-icone .lucide { width: 17px; height: 17px; stroke-width: 1.9; }
.marque-haute .lucide { width: 18px; height: 18px; }
.fil .lucide { width: 13px; height: 13px; opacity: 0.5; }
.vide-icone .lucide { width: 30px; height: 30px; margin: 0 auto; stroke-width: 1.6; }
.metrique-chevron { width: 14px; height: 14px; }

/* Un bouton de pied de page porte son icône à gauche de son libellé. */
.bouton-doux, .bouton-danger { display: inline-flex; align-items: center; gap: 7px; }
.bouton-doux .lucide, .bouton-danger .lucide { width: 15px; height: 15px; }

/* ─── Colonnes génériques du tableau commun ───────────────────────────────── */

/*
 * Une seule règle pour toutes les cellules, quel que soit l'onglet : c'est tout
 * l'objet de la factorisation. Ce qui varie d'un onglet à l'autre — la largeur
 * des colonnes — est passé en variable par le gabarit, et rien d'autre.
 */
.cellule {
  font-size: 12.5px;
  color: var(--texte-doux);
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.cellule.droite { text-align: right; font-variant-numeric: tabular-nums; }
.cellule.faible { color: var(--texte-tres-doux); }
.cellule.alerte-texte { color: var(--rouge); font-weight: 650; }

/* Plusieurs valeurs courtes dans une cellule : domaines, joueurs. Elles vont à
   la ligne plutôt que d'être tronquées — c'est la seule cellule qui le fait. */
.puces { display: flex; flex-wrap: wrap; gap: 5px; overflow: visible; }
.cellule:has(.puces) { white-space: normal; }
.puce {
  font-size: 11.5px; font-family: var(--mono);
  color: var(--texte-doux);
  background: var(--fond);
  border: 1px solid var(--bord);
  padding: 1px 7px; border-radius: 20px;
  white-space: nowrap;
}

/* ─── Adaptation, du plus large au plus étroit ───────────────────────────────
 *
 * L'ordre n'est pas décoratif. Ces requêtes ont la même spécificité : à égalité,
 * c'est la dernière écrite qui gagne. Rangées du plus grand au plus petit seuil,
 * un écran de 390 px reçoit donc bien les règles les plus étroites en dernier.
 * Dans l'autre sens, la règle « grand écran » écraserait la règle « téléphone »
 * sur un téléphone — le genre de panne qui ne se voit qu'au bon format. */

@media (max-width: 1240px) {
  .groupe {
    --colonnes: 16px minmax(130px, 1fr) 78px 52px 70px 56px 60px 138px;
    --carte-y: 15px;
    --carte-x: 16px;
  }
}

@media (max-width: 1080px) {
  /*
   * Les colonnes ne tiennent plus. Chaque ligne devient un petit bloc de deux
   * rangées : identité en haut, chiffres et boutons en dessous. Les cartes
   * restent, et c'est leur séparation qui porte la lecture.
   */
  .groupe {
    --colonnes: 16px 1fr auto;
    --carte-y: 13px;
    --carte-x: 15px;
  }
  .carte-tete, .ligne { gap: 10px; row-gap: 7px; }

  /* Trois colonnes dont deux sans titre : l'en-tête ne coiffe plus rien et
     n'est qu'une rangée de mots au-dessus du vide. */
  .entete-colonnes { display: none; }

  /* Les jauges aussi perdent une colonne : trois cartes réclament près de sept
     cents pixels, et en dessous leur chiffre et leur courbe se chevauchent. */
  .jauges { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .carte-tete ~ .ligne .col-nom { padding-left: 10px; }

  .col-statut { grid-column: 2; font-size: 11.5px; }
  .col-num { grid-column: 2; text-align: left; font-size: 11.5px; display: inline; }
  .ligne .col-num + .col-num::before { content: " · "; color: var(--texte-tres-doux); }
  .col-actions { grid-column: 1 / -1; justify-content: flex-start; margin-top: 2px; }
  .carte-agregat { grid-column: 2; text-align: left; }
  /* Les cellules vides de l'en-tête d'un dossier n'ont plus de colonne où aller :
     les laisser ajouterait deux rangées blanches au milieu de la carte. */
  .carte-tete > span:empty { display: none; }
}

@media (max-width: 780px) {
  /*
   * Sur téléphone, la fenêtre va de bord à bord et la navigation descend sous le
   * pouce. La marge violette autour coûterait quatorze pixels de largeur de
   * chaque côté pour un effet qu'on ne voit plus à cette taille.
   */
  .barre-haute { padding: 10px 14px 9px; gap: 10px; }
  .marque-haute span { display: none; }

  .fenetre {
    flex-direction: column;
    height: calc(100dvh - 40px);
    margin: 0;
    border-radius: 14px 14px 0 0;
  }

  .lateral {
    order: 2;
    width: auto; flex: none;
    flex-direction: row; gap: 0;
    overflow-x: auto; overflow-y: hidden;
    padding: 6px 6px calc(6px + env(safe-area-inset-bottom));
    border-right: 0;
    border-top: 1px solid var(--bord);
  }
  .lien { flex-direction: column; gap: 3px; padding: 7px 4px; font-size: 10.5px; }
  .lien.actif { box-shadow: none; background: var(--accent-doux); color: var(--accent); }
  .lien-icone { font-size: 17px; }
  .lien-nom { text-align: center; }
  .lien-compte { display: none; }

  /* L'échelle se resserre d'un cran sur téléphone : les mêmes rapports, des
     valeurs plus petites. Garder 30 px de marge sur 390 px de large mangerait
     un sixième de l'écran. */
  .principal {
    order: 1; flex: 1;
    --e3: 14px;
    --e4: 22px;
    padding: 18px 16px 28px;
  }
  .entete h1 { font-size: 21px; }

  /* Deux cartes de métrique par ligne : six empilées demanderaient de faire
     défiler avant d'atteindre les applications, qui sont ce qu'on vient voir. */
  .jauges { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  .metrique { padding: 14px 15px; min-height: 100px; border-radius: 12px; }
  .metrique > header { font-size: 11.5px; }
  .metrique-chevron { display: none; }
  .metrique-valeur { font-size: 21px; }
  .metrique-valeur small { font-size: 11.5px; }
  .metrique-corps { gap: 9px; margin-top: 10px; }
  .metrique-graphe { height: 28px; max-width: 72px; }
  .metrique-anneau { flex-basis: 40px; width: 40px; height: 40px; }

  .ic { width: 36px; height: 36px; font-size: 14.5px; }
}
