/* mcdash — console de commandement Prominence II
   Palette : Shadow Grey / Vivid Orchid / Teal / Celadon / Deep Crimson.
   Les surfaces sont dérivées de Shadow Grey en ne bougeant que la clarté, donc
   toute l'ossature garde sa teinte.

   Deux couleurs de la palette sont inutilisables telles quelles sur ce fond :
   Deep Crimson y tombe à 1,47:1 (illisible) et Teal à 3,02:1 (limite pour du
   texte). Chacune a donc une variante « -ink » éclaircie à teinte constante
   jusqu'à 4,5:1, réservée au texte et aux traits fins ; les teintes d'origine
   restent pour les aplats et les bordures, où 3:1 suffit. */

:root {
  color-scheme: dark;

  --plane:       #1C1D2C;   /* fond de page */
  --surface:     #272838;   /* Shadow Grey — surface des panneaux */
  --surface-2:   #303141;   /* survol / champs */
  --rule:        #363748;   /* filet */
  --rule-strong: #434456;

  --ink:         #ECEDF5;
  --ink-2:       #8D8FA3;   /* 4,55:1 */
  --ink-3:       #6F7184;   /* 3,02:1 */

  --accent:      #C140AA;   /* Vivid Orchid — accent d'interface */
  --accent-ink:  #E05EC7;   /* 4,56:1 — orchidée lisible en texte */
  --info:        #087E8B;   /* Teal */
  --info-ink:    #3B9DAA;   /* 4,55:1 */
  --good:        #73E2A7;   /* Celadon — 9,10:1, lisible partout */
  --bad:         #C64649;   /* 3,02:1 — bordures, aplats */
  --bad-ink:     #EA6766;   /* 4,58:1 — texte d'alerte */
  --crimson:     #8C001A;   /* Deep Crimson — grands aplats uniquement */

  --sans: "Fira Sans Condensed", "Fira Sans", system-ui, sans-serif;
  --mono: "JetBrainsMono Nerd Font", "JetBrains Mono", "Hack", ui-monospace, monospace;

  --well:       #141520;   /* fond creusé : console, champs, infobulles */

  --rail: 208px;

  /* Une seule courbe d'accélération pour toute l'interface : des durées qui
     diffèrent d'un élément à l'autre donnent une impression de bricolage. */
  --ease: cubic-bezier(.2, .7, .3, 1);
  --fast: 120ms;
  --mid:  220ms;
  --slow: 420ms;
}

/* Respecte le réglage système : ces animations sont du confort, pas de
   l'information — personne ne doit avoir mal au cœur à cause du panneau. */
@media (prefers-reduced-motion: reduce) {
  :root { --fast: 0ms; --mid: 0ms; --slow: 0ms; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

* { box-sizing: border-box; }

/* Sans ça, les zones défilantes affichent la barre claire par défaut, qui
   tranche violemment sur le fond sombre. */
* { scrollbar-width: thin; scrollbar-color: var(--rule-strong) transparent; }
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--rule-strong); border: 3px solid var(--surface); }
::-webkit-scrollbar-thumb:hover { background: var(--ink-3); }

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--plane);
  color: var(--ink);
  font: 15px/1.5 var(--sans);
  -webkit-font-smoothing: antialiased;
}

/* Grain tres discret sur tout le fond : casse le plat sans jamais gener la
   lecture. Pur SVG genere, aucun asset, quasi invisible tant qu'on ne le
   cherche pas — c'est le but. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: .035;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
@media (prefers-reduced-motion: reduce) { body::before { opacity: .02; } }

/* ============================================================ ossature */

.shell { position: relative; z-index: 1; display: grid; grid-template-columns: var(--rail) 1fr; min-height: 100vh; }

.rail {
  border-right: 1px solid var(--rule);
  background: var(--surface);
  display: flex; flex-direction: column;
  position: sticky; top: 0; height: 100vh;
}

.brand { padding: 20px 18px 16px; border-bottom: 1px solid var(--rule); }
.brand b {
  display: block; font-size: 15px; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase;
  text-shadow: 0 0 22px color-mix(in oklab, var(--accent) 40%, transparent);
}
.brand span { display: block; font-family: var(--mono); font-size: 10.5px; color: var(--ink-3); margin-top: 3px; }

.nav { padding: 10px 0; flex: 1; }
.nav button {
  width: 100%; display: flex; align-items: center; gap: 11px;
  background: none; border: 0; border-left: 2px solid transparent;
  color: var(--ink-2); cursor: pointer;
  padding: 9px 18px; font: inherit; font-size: 13.5px;
  letter-spacing: .06em; text-transform: uppercase;
  transition: color .12s, background .12s, border-color .12s;
}
.nav button i { font-style: normal; width: 15px; text-align: center; opacity: .85; }
.nav button kbd {
  margin-left: auto; font-family: var(--mono); font-size: 10px;
  color: var(--ink-3); border: 1px solid var(--rule); border-radius: 2px;
  padding: 0 4px; line-height: 15px;
}
.nav button:hover { color: var(--ink); background: var(--surface-2); }
.nav button[aria-current="true"] {
  color: var(--accent); border-left-color: var(--accent); background: var(--surface-2);
}

.rail footer { padding: 14px 18px; border-top: 1px solid var(--rule); font-size: 12px; color: var(--ink-3); }

main { min-width: 0; padding: 0 0 60px; }

/* ============================================================ bandeau */

.strip {
  display: flex; align-items: center; gap: 22px;
  padding: 14px 28px; border-bottom: 1px solid var(--rule);
  background: var(--surface); position: sticky; top: 0; z-index: 20;
}

.live { display: flex; align-items: center; gap: 9px; font-size: 13.5px; letter-spacing: .07em; text-transform: uppercase; }
.beat { width: 8px; height: 8px; border-radius: 50%; background: var(--ink-3); flex: none; }
.beat.on { background: var(--good); animation: pulse 2.4s ease-in-out infinite; }
.beat.off { background: var(--bad-ink); }
.beat.busy { background: var(--accent); animation: pulse 1s ease-in-out infinite; }
@keyframes pulse { 0%,100% { box-shadow: 0 0 0 0 currentColor; opacity: 1 } 50% { opacity: .55 } }

.strip .sep { width: 1px; height: 26px; background: var(--rule); }
.strip .metric { display: flex; align-items: baseline; gap: 7px; }
.strip .metric u {
  text-decoration: none; font-size: 10.5px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink-3);
}
.strip .metric b { font-family: var(--mono); font-size: 15px; font-weight: 500; }

.addr { margin-left: auto; display: flex; align-items: center; gap: 8px; }
.addr code { font-family: var(--mono); font-size: 12px; color: var(--ink-2); }

/* ============================================================ vues */

.view { display: none; padding: 26px 28px 0; }
.view[data-active] { display: block; }

.head { display: flex; align-items: center; gap: 14px; margin: 0 0 18px; }
.head h1 {
  margin: 0; font-size: 13px; font-weight: 600;
  letter-spacing: .16em; text-transform: uppercase; color: var(--ink-2);
  white-space: nowrap;
}
.head .line { flex: 1; height: 1px; background: var(--rule); }
.head .tools { display: flex; gap: 8px; align-items: center; }

.grid { display: grid; gap: 1px; background: var(--rule); border: 1px solid var(--rule); margin-bottom: 22px; }
.grid > * { background: var(--surface); padding: 18px 20px; }
.g4 { grid-template-columns: repeat(4, 1fr); }
.g3 { grid-template-columns: repeat(3, 1fr); }
.g2 { grid-template-columns: repeat(2, 1fr); }
@media (max-width: 1100px) { .g4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 760px)  { .g4, .g3, .g2 { grid-template-columns: 1fr; } }

.tile u {
  display: block; text-decoration: none; font-size: 10.5px;
  letter-spacing: .12em; text-transform: uppercase; color: var(--ink-3); margin-bottom: 9px;
}
.tile .val { font-family: var(--mono); font-size: 30px; font-weight: 500; line-height: 1; letter-spacing: -.02em; }
.tile .val small { font-size: 15px; color: var(--ink-3); margin-left: 3px; }
.tile .note { font-size: 12.5px; color: var(--ink-3); margin-top: 7px; min-height: 18px; }

.panel { background: var(--surface); border: 1px solid var(--rule); padding: 18px 20px; margin-bottom: 22px; }
.panel.flush { padding: 0; }

/* ============================================================ boutons */

button.act {
  background: var(--surface-2); color: var(--ink);
  border: 1px solid var(--rule-strong); border-radius: 2px;
  padding: 9px 15px; font: inherit; font-size: 12.5px; font-weight: 500;
  letter-spacing: .07em; text-transform: uppercase; cursor: pointer;
  transition: border-color .12s, color .12s, background .12s;
}
button.act:hover:not(:disabled) { border-color: var(--ink-3); background: var(--rule); }
button.act:disabled { opacity: .35; cursor: not-allowed; }
button.act.accent:hover:not(:disabled) { color: var(--accent); border-color: var(--accent); }
button.act.good:hover:not(:disabled)  { color: var(--good);  border-color: var(--good); }
button.act.bad:hover:not(:disabled) { color: var(--bad-ink); border-color: var(--bad-ink); }
button.act.tiny { padding: 4px 9px; font-size: 11px; }

.actions { display: flex; flex-wrap: wrap; gap: 8px; }

.chips { display: flex; gap: 6px; }
.chips button {
  background: none; border: 1px solid var(--rule); color: var(--ink-3);
  border-radius: 2px; padding: 4px 11px; font: inherit; font-size: 11.5px;
  letter-spacing: .06em; cursor: pointer;
}
.chips button:hover { color: var(--ink-2); border-color: var(--rule-strong); }
.chips button[aria-pressed="true"] { color: var(--accent); border-color: var(--accent); }

/* ============================================================ console */

.console {
  height: min(58vh, 560px); overflow-y: auto; padding: 14px 18px;
  font-family: var(--mono); font-size: 12.5px; line-height: 1.7;
  background: var(--well);
}
.console div { white-space: pre-wrap; word-break: break-word; }
.console .t { color: #565873; margin-right: 9px; }
.console .log   { color: var(--ink-2); }
.console .warn  { color: var(--accent); }
.console .error { color: var(--bad-ink); }
.console .chat  { color: var(--info-ink); }
.console .join  { color: var(--good); }
.console .me    { color: var(--accent); font-weight: 600; }

.cmd { display: flex; gap: 8px; padding: 12px 14px; border-top: 1px solid var(--rule); }
.cmd input {
  flex: 1; background: var(--well); border: 1px solid var(--rule); color: var(--ink);
  border-radius: 2px; padding: 9px 12px; font-family: var(--mono); font-size: 12.5px;
}
.cmd input:focus { outline: none; border-color: var(--rule-strong); }

/* ============================================================ graphiques */

.chart { position: relative; }
.chart svg { display: block; width: 100%; overflow: visible; }
.chart .cap { display: flex; align-items: baseline; gap: 10px; margin-bottom: 12px; }
.chart .cap u { text-decoration: none; font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-3); }
.chart .cap b { font-family: var(--mono); font-size: 17px; font-weight: 500; margin-left: auto; }

.tip {
  position: absolute; pointer-events: none; z-index: 5;
  background: var(--well); border: 1px solid var(--rule-strong);
  padding: 7px 10px; font-size: 12px; white-space: nowrap;
  transform: translate(-50%, -100%); opacity: 0; transition: opacity .1s;
}
.tip b { font-family: var(--mono); font-size: 13px; display: block; }
.tip span { color: var(--ink-3); font-size: 11px; }

/* ============================================================ tableaux */

table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
thead th {
  text-align: left; font-size: 10.5px; font-weight: 600;
  letter-spacing: .12em; text-transform: uppercase; color: var(--ink-3);
  padding: 10px 14px; border-bottom: 1px solid var(--rule); white-space: nowrap;
}
tbody td { padding: 9px 14px; border-bottom: 1px solid var(--rule); vertical-align: middle; }
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover { background: var(--surface-2); }
td.num, th.num { text-align: right; font-family: var(--mono); font-variant-numeric: tabular-nums; }
.tag {
  display: inline-block; font-size: 10px; letter-spacing: .08em; text-transform: uppercase;
  border: 1px solid currentColor; border-radius: 2px; padding: 1px 5px; margin-right: 4px;
}
.tag.op    { color: var(--accent); }
.tag.white { color: var(--info-ink); }
.tag.ban   { color: var(--bad-ink); }
.tag.on    { color: var(--good); }

.search {
  background: var(--well); border: 1px solid var(--rule); color: var(--ink);
  border-radius: 2px; padding: 7px 11px; font: inherit; font-size: 13px; width: 240px;
}
.search:focus { outline: none; border-color: var(--rule-strong); }

.scroll { max-height: 62vh; overflow-y: auto; }

/* ============================================================ fichiers */

.files { display: grid; grid-template-columns: 300px 1fr; gap: 1px; background: var(--rule); border: 1px solid var(--rule); }
@media (max-width: 900px) { .files { grid-template-columns: 1fr; } }
.files > * { background: var(--surface); min-width: 0; }

.tree { max-height: 66vh; overflow-y: auto; padding: 8px 0; }
.tree button {
  width: 100%; text-align: left; background: none; border: 0; color: var(--ink-2);
  font: inherit; font-size: 13px; padding: 6px 16px; cursor: pointer;
  display: flex; align-items: center; gap: 9px; overflow: hidden;
}
.tree button i { font-style: normal; color: var(--ink-3); width: 14px; flex: none; }
.tree button span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tree button:hover { background: var(--surface-2); color: var(--ink); }
.tree button[aria-current="true"] { color: var(--accent); background: var(--surface-2); }
.tree .up { color: var(--ink-3); border-bottom: 1px solid var(--rule); margin-bottom: 6px; padding-bottom: 10px; }

.editor { display: flex; flex-direction: column; min-height: 66vh; }
.editor .bar {
  display: flex; align-items: center; gap: 10px; padding: 11px 16px;
  border-bottom: 1px solid var(--rule);
}
.editor .bar code { font-family: var(--mono); font-size: 12px; color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; }
.editor .bar .right { margin-left: auto; display: flex; gap: 8px; align-items: center; }
.editor textarea {
  flex: 1; width: 100%; resize: none; background: var(--well); color: var(--ink);
  border: 0; padding: 14px 16px; font-family: var(--mono); font-size: 12.5px;
  line-height: 1.65; tab-size: 2;
}
.editor textarea:focus { outline: none; }
.editor .empty { flex: 1; display: grid; place-items: center; color: var(--ink-3); font-size: 13.5px; }

/* ============================================================ divers */

.kv { display: flex; justify-content: space-between; gap: 12px; padding: 8px 0; border-bottom: 1px solid var(--rule); font-size: 13.5px; }
.kv:last-child { border-bottom: 0; }
.kv span { color: var(--ink-3); }
.kv b { font-family: var(--mono); font-weight: 500; }

.people { display: flex; flex-wrap: wrap; gap: 7px; }
.person {
  display: flex; align-items: center; gap: 8px; font-size: 13px;
  background: var(--surface-2); border: 1px solid var(--rule); border-radius: 2px; padding: 5px 6px 5px 11px;
}
.person i { width: 6px; height: 6px; border-radius: 50%; background: var(--good); flex: none; }
.person select { background: none; border: 0; color: var(--ink-3); font: inherit; font-size: 12px; cursor: pointer; }
.person select option { background: var(--surface-2); color: var(--ink); }

.empty { color: var(--ink-3); font-size: 13.5px; font-style: italic; }

.switch { display: flex; align-items: center; gap: 10px; font-size: 13.5px; }
.switch input[type=checkbox] { width: 16px; height: 16px; accent-color: var(--accent); }
.switch input[type=time] {
  background: var(--well); border: 1px solid var(--rule); color: var(--ink);
  border-radius: 2px; padding: 6px 9px; font-family: var(--mono); font-size: 13px;
}

#toasts { position: fixed; right: 20px; bottom: 20px; display: flex; flex-direction: column; gap: 8px; z-index: 60; }
.toast {
  background: var(--surface-2); border: 1px solid var(--rule-strong); border-left: 2px solid var(--info-ink);
  padding: 10px 15px; font-size: 13px; max-width: 340px;
  box-shadow: 0 10px 30px rgba(0,0,0,.55); animation: slide .2s ease;
}
.toast.join  { border-left-color: var(--good); }
.toast.leave { border-left-color: var(--ink-3); }
.toast.error { border-left-color: var(--bad-ink); }
@keyframes slide { from { opacity: 0; transform: translateX(14px); } }

/* ============================================================ connexion */

.login { max-width: 330px; margin: 16vh auto; }
.login form { display: flex; flex-direction: column; gap: 12px; margin-top: 16px; }
.login input {
  background: var(--well); border: 1px solid var(--rule); color: var(--ink);
  border-radius: 2px; padding: 11px 13px; font: inherit; font-size: 14px;
}
.login input:focus { outline: none; border-color: var(--accent); }
.login .err { color: var(--bad-ink); font-size: 13px; min-height: 18px; }

@media (max-width: 720px) {
  .shell { grid-template-columns: 1fr; }
  .rail { position: static; height: auto; flex-direction: row; align-items: center; overflow-x: auto; }
  .brand { border-bottom: 0; border-right: 1px solid var(--rule); white-space: nowrap; }
  .nav { display: flex; padding: 0; }
  .nav button { border-left: 0; border-bottom: 2px solid transparent; white-space: nowrap; }
  .nav button[aria-current="true"] { border-left: 0; border-bottom-color: var(--accent); }
  .nav button kbd, .rail footer { display: none; }
  .view, .strip { padding-left: 16px; padding-right: 16px; }
}

/* ============================================================ mouvement

   Règles du jeu : rien ne bouge plus de ~200 ms, rien ne se déplace de plus de
   quelques pixels, et aucune animation ne se déclenche sur un rafraîchissement
   périodique — le panneau se repeint toutes les 3 s, une entrée animée à chaque
   cycle rendrait la page épileptique. Côté JS, les listes ne sont reconstruites
   que si leur contenu a réellement changé (voir `changed()` dans app.js). */

/* -- entrée de vue -------------------------------------------------------- */
.view[data-active] { animation: view-in var(--mid) var(--ease) both; }
@keyframes view-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}

/* -- navigation ----------------------------------------------------------- */
.nav button {
  transition: color var(--fast) var(--ease), background var(--fast) var(--ease),
              border-color var(--fast) var(--ease), padding-left var(--fast) var(--ease);
}
.nav button:hover { padding-left: 22px; }
.nav button[aria-current="true"] i { transform: scale(1.15); }
.nav button i { transition: transform var(--mid) var(--ease); }

/* -- boutons -------------------------------------------------------------- */
button.act {
  position: relative;
  transition: border-color var(--fast) var(--ease), color var(--fast) var(--ease),
              background var(--fast) var(--ease), transform var(--fast) var(--ease),
              opacity var(--fast) var(--ease);
}
button.act:active:not(:disabled) { transform: translateY(1px) scale(.985); }
button.act:disabled { opacity: .32; cursor: not-allowed; }

/* Le focus clavier doit rester parfaitement visible : c'est le seul repère
   pour qui n'utilise pas la souris. */
button.act:focus-visible, .chips button:focus-visible, .nav button:focus-visible,
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: 2px solid var(--accent-ink); outline-offset: 2px;
}

/* Bouton en attente d'une réponse serveur : on remplace le libellé par un
   témoin qui tourne, pour qu'un clic n'ait jamais l'air ignoré. */
button.act[data-pending] { color: transparent !important; pointer-events: none; }
button.act[data-pending]::after {
  content: ""; position: absolute; inset: 0; margin: auto;
  width: 12px; height: 12px; border-radius: 50%;
  border: 2px solid var(--ink-3); border-top-color: var(--accent-ink);
  animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.chips button { transition: color var(--fast) var(--ease), border-color var(--fast) var(--ease), background var(--fast) var(--ease); }
.chips button:active { transform: translateY(1px); }

/* -- tuiles et panneaux --------------------------------------------------- */
.tile { transition: background var(--mid) var(--ease), box-shadow var(--mid) var(--ease), transform var(--mid) var(--ease); }
.tile:hover { background: var(--surface-2); box-shadow: 0 10px 24px -16px #000; transform: translateY(-1px); }
.tile .val { transition: color var(--mid) var(--ease); }

/* Une valeur qui change se signale une fraction de seconde, sinon on rate les
   variations en regardant ailleurs. */
.flash { animation: flash var(--slow) var(--ease); }
@keyframes flash {
  from { background: color-mix(in oklab, var(--accent) 26%, transparent); }
  to   { background: transparent; }
}

/* -- témoin d'état -------------------------------------------------------- */
.beat { position: relative; transition: background var(--mid) var(--ease), box-shadow var(--mid) var(--ease); }
.beat.on, .beat.busy { box-shadow: 0 0 9px 1px currentColor; }
.beat.on::after, .beat.busy::after {
  content: ""; position: absolute; inset: -3px; border-radius: 50%;
  border: 1px solid currentColor; color: inherit;
  animation: ring 2.4s var(--ease) infinite; opacity: 0;
}
.beat.on { color: var(--good); }
.beat.busy { color: var(--accent); }
.beat.busy::after { animation-duration: 1s; }
@keyframes ring {
  0%   { transform: scale(.7); opacity: .8; }
  100% { transform: scale(1.9); opacity: 0; }
}

/* -- lignes de tableau et listes ------------------------------------------ */
tbody tr { transition: background var(--fast) var(--ease); }
tbody tr:hover { background: var(--surface-2); }
.person, .kv { transition: background var(--fast) var(--ease); }
.person:hover { background: var(--surface-2); }

/* L'apparition en cascade ne joue qu'au (re)dessin réel d'une liste. */
.stagger > * { animation: row-in var(--mid) var(--ease) both; }
@keyframes row-in {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: none; }
}

/* -- arborescence de fichiers --------------------------------------------- */
.tree button { transition: color var(--fast) var(--ease), background var(--fast) var(--ease), padding-left var(--fast) var(--ease); }
.tree button:hover { padding-left: 16px; }

/* -- notifications -------------------------------------------------------- */
.toast {
  animation: toast-in var(--mid) var(--ease) both;
  transition: opacity var(--mid) var(--ease), transform var(--mid) var(--ease);
}
@keyframes toast-in {
  from { opacity: 0; transform: translateX(18px) scale(.97); }
  to   { opacity: 1; transform: none; }
}
.toast[data-leaving] { opacity: 0; transform: translateX(18px) scale(.97); }

/* -- barres de progression ------------------------------------------------ */
#pg-bar, #v-bar { transition: width var(--slow) var(--ease); }

/* -- console -------------------------------------------------------------- */
.console div { animation: line-in var(--fast) var(--ease) both; }
@keyframes line-in { from { opacity: 0; } to { opacity: 1; } }
.console { scroll-behavior: smooth; }

/* ============================================================ aplats de palette

   Les trois teintes brutes de la palette (Teal, Deep Crimson et le crimson
   d'interface) ne conviennent pas au texte sur ce fond, mais ce sont d'excellents
   aplats et bordures, où 3:1 suffit. On les emploie donc là, et seulement là. */

button.act.bad    { border-color: color-mix(in oklab, var(--bad) 55%, var(--rule-strong)); }
button.act.bad:hover:not(:disabled)    { background: color-mix(in oklab, var(--crimson) 34%, var(--surface-2)); box-shadow: 0 0 16px -5px var(--bad-ink); }
button.act.accent:hover:not(:disabled) { background: color-mix(in oklab, var(--accent) 20%, var(--surface-2)); box-shadow: 0 0 16px -5px var(--accent); }
button.act.good:hover:not(:disabled)   { background: color-mix(in oklab, var(--good) 16%, var(--surface-2)); box-shadow: 0 0 16px -5px var(--good); }

/* Les étiquettes gagnent un fond très dilué : le contour seul se perdait sur
   les lignes survolées, qui passent en surface-2. */
.tag { background: color-mix(in oklab, currentColor 14%, transparent); }

/* Onglet actif : liseré plein plutôt qu'une simple couleur de texte, avec une
   lueur interne discrète qui part du liseré — jamais assez forte pour
   distraire d'un onglet qu'on regarde en continu. */
.nav button[aria-current="true"] {
  background: color-mix(in oklab, var(--accent) 12%, var(--surface-2));
  box-shadow: inset 5px 0 14px -9px var(--accent);
}

/* La bande d'en-tête se détache du contenu par un filet teinté. */
.strip { border-bottom-color: color-mix(in oklab, var(--info) 30%, var(--rule)); }

/* Champs de saisie : le focus prend la teinte d'accent, pas un gris de plus. */
input:focus, textarea:focus, select:focus, .cmd input:focus {
  outline: none;
  border-color: var(--accent) !important;
  background: color-mix(in oklab, var(--accent) 7%, var(--well));
}

/* Notifications : une barre de couleur porte le niveau, le texte reste en encre
   normale — une notification entièrement colorée se lit mal. */
.toast { border-left: 3px solid var(--ink-3); }
.toast.error { border-left-color: var(--bad-ink); }
.toast.join  { border-left-color: var(--good); }
.toast.leave { border-left-color: var(--info-ink); }
.toast.tunnel { border-left-color: var(--accent-ink); }

/* ============================================================ bloc d'action

   Un « hero » : l'état du serveur et la seule action pertinente, réunis. Avant,
   la page éparpillait deux tuiles, un bouton, un panneau de réglages et un
   tableau — on ne savait pas par où commencer. */

.hero {
  background: var(--surface); border: 1px solid var(--rule);
  border-left: 3px solid var(--ink-3);
  margin-bottom: 26px;
  transition: border-left-color var(--mid) var(--ease), box-shadow var(--mid) var(--ease);
}
.hero[data-state="ok"]      { border-left-color: var(--good); box-shadow: 0 6px 28px -16px var(--good); }
.hero[data-state="update"]  { border-left-color: var(--accent); box-shadow: 0 6px 28px -16px var(--accent); }
.hero[data-state="working"] { border-left-color: var(--info-ink); box-shadow: 0 6px 28px -16px var(--info-ink); }
.hero[data-state="warn"]    { border-left-color: var(--bad-ink); box-shadow: 0 6px 28px -16px var(--bad-ink); }

.hero-main { display: flex; align-items: center; gap: 16px; padding: 20px 22px; }
.hero-dot {
  width: 10px; height: 10px; border-radius: 50%; flex: none;
  background: var(--ink-3); transition: background var(--mid) var(--ease);
}
.hero[data-state="ok"]      .hero-dot { background: var(--good); }
.hero[data-state="update"]  .hero-dot { background: var(--accent); }
.hero[data-state="working"] .hero-dot { background: var(--info-ink); animation: pulse 1s ease-in-out infinite; }
.hero[data-state="warn"]    .hero-dot { background: var(--bad-ink); }

.hero-text { min-width: 0; flex: 1; }
.hero-text b {
  display: block; font-size: 17px; font-weight: 600; letter-spacing: .01em;
  line-height: 1.3;
}
.hero-text span { display: block; font-size: 13px; color: var(--ink-2); margin-top: 3px; }
.hero-main .act { flex: none; }

.hero-run { padding: 0 22px 16px; }
.hero-run span { display: block; font-size: 13px; color: var(--info-ink); margin-bottom: 8px; }
.hero-run .bar { height: 4px; background: var(--surface-2); overflow: hidden; }
.hero-run .bar i { display: block; height: 100%; width: 0; background: var(--info-ink); }

.hero-foot {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 11px 22px; border-top: 1px solid var(--rule);
  background: color-mix(in oklab, var(--plane) 55%, var(--surface));
  font-size: 12px; color: var(--ink-3);
}
.hero-check { margin-left: auto; display: flex; align-items: center; gap: 8px; }

/* Action secondaire discrète : un lien, pas un bouton qui rivalise avec
   l'action principale du bloc. */
.linky {
  background: none; border: 0; padding: 0; font: inherit; cursor: pointer;
  color: var(--accent-ink); border-bottom: 1px solid transparent;
  transition: border-color var(--fast) var(--ease);
}
.linky:hover { border-bottom-color: var(--accent-ink); }
.linky:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 3px; }
.linky[data-pending] { color: var(--ink-3); pointer-events: none; }

/* ============================================================ repli */

.fold { border: 1px solid var(--rule); background: var(--surface); margin-bottom: 22px; }
.fold summary {
  cursor: pointer; padding: 13px 20px; list-style: none;
  font-size: 12px; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-2);
  display: flex; align-items: center; gap: 12px;
  transition: color var(--fast) var(--ease), background var(--fast) var(--ease);
}
.fold summary::-webkit-details-marker { display: none; }
.fold summary::before {
  content: "▸"; color: var(--ink-3); font-size: 11px;
  transition: transform var(--mid) var(--ease);
}
.fold[open] summary::before { transform: rotate(90deg); }
.fold summary:hover { color: var(--ink); background: var(--surface-2); }
.fold-state {
  margin-left: auto; text-transform: none; letter-spacing: 0;
  font-size: 12px; color: var(--ink-3);
}
.fold-body { padding: 4px 20px 18px; border-top: 1px solid var(--rule); }

/* Texte d'aide : présent pour qui le cherche, effacé pour qui ne le cherche pas. */
.hint {
  font-size: 12px; color: var(--ink-3); line-height: 1.6;
  margin: 10px 0 0; max-width: 78ch;
}
.hint code { font-family: var(--mono); font-size: 11.5px; color: var(--ink-2); }

/* ============================================================ étiquette de colonne
   Même habillage que les deux libellés d'origine de l'Aperçu (encore en style
   inline) — une classe pour ne pas tripler la duplication sur la nouvelle colonne. */
.lbl {
  display: block; text-decoration: none; font-size: 10.5px;
  letter-spacing: .12em; text-transform: uppercase; color: var(--ink-3); margin-bottom: 12px;
}

/* ============================================================ avatars joueurs */

img.avatar {
  width: 20px; height: 20px; border-radius: 3px; flex: none;
  image-rendering: pixelated; vertical-align: middle; background: var(--well);
}

/* ============================================================ flux d'activité */

.feed { max-height: 280px; overflow-y: auto; display: flex; flex-direction: column; }
.feed-row {
  display: flex; align-items: baseline; gap: 8px; padding: 6px 2px;
  font-size: 12.5px; border-bottom: 1px solid var(--rule);
}
.feed-row:last-child { border-bottom: 0; }
.feed-row i { font-style: normal; width: 14px; flex: none; text-align: center; opacity: .9; }
.feed-row .txt { flex: 1; min-width: 0; color: var(--ink-2); overflow-wrap: anywhere; }
.feed-row .when { flex: none; font-family: var(--mono); font-size: 10.5px; color: var(--ink-3); }
.feed-row.join i        { color: var(--good); }
.feed-row.leave i       { color: var(--ink-3); }
.feed-row.death i       { color: var(--bad-ink); }
.feed-row.advancement i { color: var(--accent-ink); }
.feed-row.error i       { color: var(--bad-ink); }
.feed-row.info i        { color: var(--info-ink); }
.feed-row.tunnel i      { color: var(--accent-ink); }

/* ============================================================ disponibilité */

.avail-bars { display: flex; gap: 3px; justify-content: flex-end; padding: 4px 0 2px; }
.avail-bars i { display: block; width: 11px; height: 14px; border-radius: 1px; background: var(--rule); }

/* ============================================================ changelog modpack */

.hero-changelog { padding: 14px 22px; border-top: 1px solid var(--rule); font-size: 12.5px; }
.hero-changelog b { display: block; margin-bottom: 8px; color: var(--ink); font-weight: 600; }
.hero-changelog .cols { display: flex; gap: 28px; flex-wrap: wrap; }
.hero-changelog .cols > div { min-width: 160px; }
.hero-changelog .cols > div > span { color: var(--ink-3); font-size: 11px; letter-spacing: .06em; text-transform: uppercase; }
.hero-changelog ul { margin: 6px 0 0; padding-left: 16px; max-height: 180px; overflow-y: auto; }
.hero-changelog li { font-family: var(--mono); font-size: 11.5px; color: var(--ink-2); line-height: 1.7; }

/* ============================================================ propriétés du serveur */

.prop-group { background: var(--surface); border: 1px solid var(--rule); margin-bottom: 18px; }
.prop-group > u {
  display: block; padding: 12px 18px; border-bottom: 1px solid var(--rule);
  font-size: 11px; font-weight: 600; letter-spacing: .12em; text-transform: uppercase;
  text-decoration: none; color: var(--ink-2);
}
.prop-row {
  display: flex; align-items: center; justify-content: space-between; gap: 18px;
  padding: 11px 18px; border-bottom: 1px solid var(--rule); font-size: 13.5px;
}
.prop-row:last-child { border-bottom: 0; }
.prop-row .prop-label { min-width: 0; }
.prop-row .prop-label b { display: block; font-weight: 500; }
.prop-row .prop-label span { display: block; font-size: 11.5px; color: var(--ink-3); margin-top: 2px; max-width: 52ch; }
.prop-row .prop-ctl { flex: none; display: flex; align-items: center; gap: 8px; }
.prop-row input[type=number] {
  width: 90px; background: var(--well); border: 1px solid var(--rule); color: var(--ink);
  border-radius: 2px; padding: 7px 9px; font: inherit; font-family: var(--mono); font-size: 13px;
}
.prop-row input[type=text] {
  width: 260px; background: var(--well); border: 1px solid var(--rule); color: var(--ink);
  border-radius: 2px; padding: 7px 11px; font: inherit; font-size: 13px;
}
.prop-row select {
  background: var(--well); border: 1px solid var(--rule); color: var(--ink);
  border-radius: 2px; padding: 7px 9px; font: inherit; font-size: 13px;
}
.prop-row input:focus, .prop-row select:focus { outline: none; border-color: var(--accent) !important; }
.prop-row[data-dirty="1"] { background: color-mix(in oklab, var(--accent) 6%, transparent); }

/* ============================================================ carte (BlueMap) */

.map-frame { width: 100%; height: min(78vh, 780px); border: 0; display: block; background: var(--well); }
