/* ============================================================================
   GSV GAMES — NEUES UI (ui-neu-20260910)
   ----------------------------------------------------------------------------
   Owner am 10.09.2026: „die ganze Seite auf games.g-sv.eu sollst du
   aktualisieren — ich will, dass du von null ein neues UI machst. Wie das
   aussieht ist mir egal, interaktiv muss es sein und funktionierend, aber
   nicht wie jetzt. Und: games hat nichts mit dem Hub zu tun."

   DESHALB IST DAS HIER EINE EIGENE HANDSCHRIFT, kein Hub-Abkömmling:
   der Hub ist warmes Papier mit Serifen — dieses Panel ist ein Werkzeug für
   laufende Maschinen und sieht aus wie eines. Graphit statt Pergament,
   Grotesk statt Serife, Zahlen tabellarisch, Zustand über Farbe.

   DIESE DATEI ERSETZT den alten Satz vollständig (gsv-ui.css, styles.css,
   gsv-theme.css, gsv-tokens.css, gsv-redesign.css, gsv-components.css,
   spielerbereich.css, server-leiste.css, deck-neu.css). Sie werden in
   index.html AUSGEHÄNGT, nicht überschrieben — sonst gewinnt die alte Regel
   und das neue Design bleibt unsichtbar.

   WAS SICH NICHT ÄNDERT: keine einzige Klasse, kein data-Hook, keine ID.
   Jede Funktion bleibt angeschlossen; getauscht wird ausschliesslich die
   Optik. Der Abnahme-Zettel ist abzug-games/inventar.json (11 Reiter je
   Server) und tools/games-durchklick.js.
   ============================================================================ */

/* ---------------------------------------------------------------- 1. TOKENS
   Jeder Wert steht hier und nirgends sonst. Wer eine Farbe braucht, die es
   hier nicht gibt, hat einen Zustand gefunden, den das Design noch nicht
   kennt — dann gehört er hier hinein, nicht als #hex in die Regel. */
:root {
  color-scheme: dark;

  /* Grund: drei Tiefen, damit Karten auf Flächen liegen können */
  --g-0: #0b0e13;          /* der Grund, ganz hinten */
  --g-1: #11151d;          /* Flächen: Rail, Topbar */
  --g-2: #161b25;          /* Karten */
  --g-3: #1d2430;          /* Karten, die hervortreten (Hover, aktiv) */
  --g-4: #262f3d;          /* Eingaben, Chips */

  /* Linien */
  --line: #232c39;
  --line-hell: #33405233;
  --line-stark: #3a4759;
  --haar: 1px;

  /* Schrift */
  --ink: #e8edf5;          /* Haupttext */
  --ink-2: #a9b6c9;        /* Nebentext */
  --ink-3: #6f7f96;        /* Beschriftungen, Einheiten */
  --ink-aus: #4a586c;      /* deaktiviert */

  /* Akzent und Zustände */
  --akz: #35d6a4;          /* GSV-Grün: läuft, Erfolg, primär */
  --akz-tief: #1d9c76;
  --akz-schwach: #35d6a41f;
  --auf-akz: #05130e;       /* Schrift auf Akzentflaeche: 10,22:1 auf --akz dunkel, 5,49:1 auf --akz-tief, 5,25:1 auf --akz hell - gilt fuer beide Themen */
  --warn: #f0b429;
  --warn-schwach: #f0b4291f;
  --gefahr: #ef5b6b;
  --gefahr-schwach: #ef5b6b1f;
  --info: #4aa8ff;
  --info-schwach: #4aa8ff1f;
  --ruhe: #6f7f96;         /* gestoppt, unbekannt */

  /* --ok und --danger: bis 12.09.2026 in KEINER :root-Regel definiert.
     Benutzt wurden sie trotzdem - zweimal im JS mit #hex-Rueckfall (der
     damit immer gewann) und an einem Dutzend weiterer Stellen ganz OHNE
     Rueckfall, wo die Farbe schlicht nicht ankam. Die Werte hier sind
     exakt die bisherigen Rueckfaelle, damit sich am Bild nichts aendert -
     nur die Herkunft. Absichtlich nur hier und nicht im Hell-Block: der
     feste Rueckfall galt bisher auch in beiden Themes. */
  --ok: #34e29b;           /* Erfolg, gelungen */
  --danger: #ff5a72;       /* Fehler, fehlgeschlagen */
  /* --ok-schwach: neu am 12.09.2026, KEINE erfundene Farbe - es ist --ok
     mit genau der 1f-Alpha, die --akz-schwach/--warn-schwach/--gefahr-schwach
     schon tragen. Gebraucht wird sie fuer den Zweitnamen --ok-soft (11x im JS,
     bisher ohne jeden Wert). */
  --ok-schwach: #34e29b1f;

  /* Abstände — eine Leiter, keine freien Zahlen */
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 24px;
  --s-6: 32px;
  --s-7: 48px;
  --s-8: 64px;

  /* Radien */
  --r-1: 6px;
  --r-2: 10px;
  --r-3: 14px;
  --r-voll: 999px;

  /* Schriftgrößen */
  --fs-xs: 11px;
  --fs-s: 12.5px;
  --fs-m: 14px;
  --fs-l: 17px;
  --fs-xl: 22px;
  --fs-2xl: 30px;
  --fs-code: 48px;   /* Code-Ziffern (launcher-link.html), Owner 24.09. */

  /* Schriftfamilien */
  --ff: "Inter", "Segoe UI Variable", "Segoe UI", system-ui, -apple-system, sans-serif;
  --ff-mono: "JetBrains Mono", "Cascadia Mono", "Consolas", ui-monospace, monospace;

  /* Bewegung */
  --t: 140ms;
  --t-lang: 260ms;
  --t-dreh: 800ms;   /* eine Umdrehung Ladekreis, Owner 24.09. */
  --ease: cubic-bezier(.2, .7, .3, 1);

  /* Schatten — sparsam: ein Panel ist kein Schaufenster */
  --sch-1: 0 1px 2px #00000040;
  --sch-2: 0 6px 20px #0000005c;

  /* Maße */
  --rail-b: 232px;
  --topbar-h: 54px;
  --inhalt-max: 1560px;
  --karte-schmal: 440px;   /* schmale Einzelkarte (launcher-link.html), Owner 24.09. */
  /* Hoehe der kleinen Knoepfe. KEINE neue Zahl: .btn.xs trug diese 25 px
     bisher fest in ihrer eigenen Regel; nachgemessen im Browser ist
     .btn.xs.ov-klapp (der Klapp-Knopf im Konsolen-Kopf) genau 25 px hoch.
     Sie bekommt hier nur einen Namen, damit .ov-qcon > .card-h seine
     Mindesthoehe daraus rechnen kann statt aus einer freien Zahl. */
  --h-xs: 25px;

  /* Glas und Schleier. Kein neuer Wert, nur ein neuer Ort: blur(10px) stand
     an allen drei Glas-Stellen dieser Datei (Topbar, Fussleiste, Anmeldekarte),
     die vier Schleier-Hexe standen frei in den Regeln. Beides ist byte-gleich
     hierher gewandert. Absichtlich NICHT im Hell-Block: sie galten bisher in
     beiden Themen gleich, und daran soll sich heute nichts aendern. */
  --blur-glas: 10px;
  --scrim: #05070b99;         /* Rail-Schleier (schmale Breite) */
  --scrim-mittel: #05070ba8;  /* Ladeschleier */
  --scrim-stark: #05070bcc;   /* Dialog- und Palettenschleier */

  /* Verlauf - nur aus vorhandenen Tokens komponiert */
  --verlauf-akz: linear-gradient(135deg, var(--akz), var(--info));

  /* ------------------------------------------------------------- ZWEITNAMEN
     Im Panel lebten zwei Token-Sprachen nebeneinander. Die kanonische steht
     oben - sie ist die einzige, die Werte traegt. Die zweite stammt aus dem
     alten Satz (--accent, --surface, --border, --text, --ansi-* ...) und wird
     im JS an 442 Stellen benutzt, war aber NIRGENDS deklariert: die Dateien,
     in denen sie stand (gsv-ui.css, gsv-redesign.css), haengen seit dem
     10.09.2026 aus. Wo im JS ein #hex-Rueckfall danebenstand, gewann der
     Rueckfall und das Thema hatte dort keine Wirkung; wo keiner stand - rund
     die Haelfte, darunter die komplette Konsolen-Palette - kam ueberhaupt
     keine Farbe an.

     Owner-Entscheid 12.09.2026 (O43): die Token-Datei gewinnt. Kein Zweitname
     bekommt einen eigenen Wert. Jeder zeigt hier - und NUR hier - per var()
     auf ein kanonisches Token. Dadurch zieht :root[data-theme="light"]
     automatisch mit, ohne dass dort eine einzige Zeile verdoppelt wird.

     Wer einen Zweitnamen sucht: er steht in diesem Block und sonst nirgends.
     Wer eine neue Farbe braucht: oben ins kanonische Set, nicht hier hinein.
     -------------------------------------------------------------------- */

  /* Flaechen */
  --primary:             var(--akz);
  --accent:              var(--akz);
  --accent-hover:        var(--akz-tief);
  --accent-soft:         var(--akz-schwach);
  --surface:             var(--g-2);
  --surface-2:           var(--g-3);
  --bg-elev:             var(--g-3);
  --bg2:                 var(--g-3);
  --glass-opaque-2:      var(--g-3);

  /* Linien */
  --border:              var(--line);
  --border-2:            var(--line-stark);
  --border2:             var(--line-stark);
  --glass-border-bright: var(--line-stark);

  /* Schrift */
  --text:                var(--ink);
  --fg:                  var(--ink);
  --text-dim:            var(--ink-2);
  --muted:               var(--ink-2);
  --text-faint:          var(--ink-3);
  --font-mono:           var(--ff-mono);
  --mono:                var(--ff-mono);

  /* Zustaende */
  --ok-soft:             var(--ok-schwach);
  --danger-soft:         var(--gefahr-schwach);
  --warn-soft:           var(--warn-schwach);

  /* Masse, Schatten, Bewegung, Glas */
  --radius-sm:           var(--r-1);
  --shadow:              var(--sch-2);
  --tr:                  var(--t) var(--ease);
  --blur-glass:          var(--blur-glas);

  /* Verlauf (die "Aurora" des alten Satzes) */
  --aurora-grad:         var(--verlauf-akz);
  --aurora-soft:         var(--akz-schwach);
  /* gemessen, nicht geraten: --aurora-glow steht in backups.js:821 als
     zweites Glied einer box-shadow-Liste. Es ist also ein Schatten, keine
     Farbe - deshalb --sch-2 und nicht --akz-schwach. */
  --aurora-glow:         var(--sch-2);

  /* ANSI- und Minecraft-Palette der Konsole (console.js:45 bildet die
     16 SGR-Codes und die 16 Paragraf-Farben hierauf ab). Diese Palette stand
     in gsv-redesign.css; seit die Datei am 10.09.2026 ausgehaengt ist, war die
     Konsole komplett farblos - das hat niemand entschieden, das war eine
     Regression. Hier steht kein einziger neuer Hex, nur Verweise.

     OFFEN FUER DEN OWNER: der Satz kennt kein Violett und keine 16 unter-
     scheidbaren Terminalfarben. Sechs Paare fallen deshalb zusammen (unten
     als KOLLISION markiert). Wer die Konsole wirklich 16-farbig will, braucht
     dafuer neue kanonische Token - erfunden wird hier keiner. */
  --ansi-30: var(--ink-aus);   /* schwarz */
  --ansi-31: var(--gefahr);    /* rot */
  --ansi-32: var(--akz-tief);  /* gruen */
  --ansi-33: var(--warn);      /* gelb */
  --ansi-34: var(--info);      /* blau */
  --ansi-35: var(--gefahr);    /* magenta   - KOLLISION mit 31 (kein Violett im Satz) */
  --ansi-36: var(--akz-tief);  /* cyan      - KOLLISION mit 32 */
  --ansi-37: var(--ink-2);     /* weiss */
  --ansi-90: var(--ink-3);     /* grau */
  --ansi-91: var(--danger);    /* hellrot */
  --ansi-92: var(--ok);        /* hellgruen */
  --ansi-93: var(--warn);      /* hellgelb  - KOLLISION mit 33 */
  --ansi-94: var(--info);      /* hellblau  - KOLLISION mit 34 */
  --ansi-95: var(--danger);    /* hellmagenta - KOLLISION mit 91 */
  --ansi-96: var(--ok);        /* hellcyan  - KOLLISION mit 92 */
  --ansi-97: var(--ink);       /* hellweiss */
}

/* Helles Thema. Der Rail-Schalter setzt data-theme="light". */
:root[data-theme="light"] {
  color-scheme: light;
  --g-0: #f4f6f9;
  --g-1: #ffffff;
  --g-2: #ffffff;
  --g-3: #eef2f7;
  --g-4: #e4eaf2;
  --line: #dde4ed;
  --line-hell: #dde4ed80;
  --line-stark: #c3cede;
  --ink: #131922;
  --ink-2: #46536a;
  --ink-3: #6b7a92;
  --ink-aus: #a3aebf;
  --akz: #10996f;
  --akz-tief: #0b7a58;
  --akz-schwach: #10996f1a;
  --warn: #b8790a;
  --warn-schwach: #b8790a1a;
  --gefahr: #d3384a;
  --gefahr-schwach: #d3384a1a;
  --info: #1a6fd4;
  --info-schwach: #1a6fd41a;
  --ruhe: #7b8798;
  /* --ok und --danger fehlten hier. Gemessen am 12.09.2026: --ansi-92/96
     zeigen auf --ok, --ansi-91/95 auf --danger, und die Nachtwerte auf dem
     hellen Grund --g-0 kamen auf 1,55:1 bzw. 2,79:1 -- unter jedem Schwellwert.
     Keine neue Farbe: --ok bekommt das hier schon entschiedene --akz, --danger
     das hier schon entschiedene --gefahr. --ok-schwach folgt --ok. */
  --ok: var(--akz);
  --danger: var(--gefahr);
  --ok-schwach: var(--akz-schwach);
  --sch-1: 0 1px 2px #0f172a14;
  --sch-2: 0 8px 24px #0f172a1f;
}

/* ------------------------------------------------------------- 2. GRUNDLAGE */
*, *::before, *::after { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
}

body {
  background: var(--g-0);
  color: var(--ink);
  font-family: var(--ff);
  font-size: var(--fs-m);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  font-variant-numeric: tabular-nums;
}

h1, h2, h3, h4 { margin: 0; font-weight: 650; letter-spacing: -.01em; line-height: 1.25; }
h1 { font-size: var(--fs-2xl); }
h2 { font-size: var(--fs-xl); }
h3 { font-size: var(--fs-l); }
h4 { font-size: var(--fs-m); }
p { margin: 0; }
a { color: var(--info); text-decoration: none; }
a:hover { text-decoration: underline; }
img { max-width: 100%; }
svg { flex: 0 0 auto; }

button, input, select, textarea { font: inherit; color: inherit; }

/* SVG BRAUCHT EINE GROESSE, SONST NIMMT ES SICH DIE GANZE BREITE.
   Gemessen am 10.09.2026 beim ersten Aufschlag des neuen Satzes: die Symbole
   in den Leerzustands-Karten waren 400 px hoch und schoben den Text aus der
   Karte. Ein <svg> ohne width/height ist ein Blockelement, das so gross wird
   wie sein Platz — die alten Stylesheets hatten die Groesse an ueber hundert
   Stellen einzeln stehen. Hier steht sie EINMAL, und wer wirklich ein grosses
   Bild braucht, sagt es unten ausdruecklich. */
svg { width: 18px; height: 18px; }
/* Was absichtlich gross ist: Diagramme, Verlaufskurven, Leerzustands-Zeichen. */
.s-spark svg, .pv-chart svg, .pv-graph svg, .chart svg, .spark svg,
svg.spark, svg.chart, svg[data-chart] { width: 100%; height: 100%; }
.mv-empty svg, .ff-empty svg, .empty svg, .mv-empty-ic svg { width: 34px; height: 34px; }
.logo svg { width: 20px; height: 20px; }
button { background: none; border: 0; padding: 0; cursor: pointer; }

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--line-stark); border-radius: var(--r-voll); border: 3px solid transparent; background-clip: content-box; }
::-webkit-scrollbar-thumb:hover { background: var(--ink-aus); background-clip: content-box; }

:focus-visible { outline: 2px solid var(--akz); outline-offset: 2px; border-radius: var(--r-1); }

.mono { font-family: var(--ff-mono); font-size: .93em; }
.dim { color: var(--ink-3); }
.ta-r { text-align: right; }
.xs { font-size: var(--fs-xs); }
.sm { font-size: var(--fs-s); }
.tight { gap: var(--s-1) !important; }
.flush { margin: 0 !important; }
.ok { color: var(--akz); }
.warn { color: var(--warn); }
.danger, .gefahr { color: var(--gefahr); }
.idle, .none { color: var(--ruhe); }
.info { color: var(--info); }
.clickable { cursor: pointer; }
[hidden] { display: none !important; }

/* --------------------------------------------------------------- 3. SCHALE
   Rail links, Topbar oben, Inhalt darunter. Die Rail ist breit genug für
   Symbol UND Wort — ein Panel, in dem man raten muss, was ein Symbol
   bedeutet, kostet bei jedem Klick eine Sekunde. */
.app {
  display: grid;
  grid-template-columns: var(--rail-b) minmax(0, 1fr);
  min-height: 100vh;
  background: var(--g-0);
}

.rail {
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
  padding: var(--s-3);
  background: var(--g-1);
  border-right: var(--haar) solid var(--line);
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
}

.rail-brand {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-2);
  margin-bottom: var(--s-3);
  border-radius: var(--r-2);
  width: 100%;
}
.rail-brand:hover { background: var(--g-3); }
.logo, .brand .logo {
  display: grid;
  place-items: center;
  width: 34px; height: 34px;
  border-radius: var(--r-2);
  background: linear-gradient(150deg, var(--akz), var(--akz-tief));
  color: var(--auf-akz);
  font-weight: 800;
  font-size: var(--fs-s);
  letter-spacing: -.03em;
}
.logo svg { width: 20px; height: 20px; }
.rail-btxt { display: flex; flex-direction: column; align-items: flex-start; line-height: 1.15; }
.rail-btxt strong, .rail-btxt b { font-size: var(--fs-m); font-weight: 700; }
.rail-btxt span, .rail-btxt small { font-size: var(--fs-xs); color: var(--ink-3); letter-spacing: .09em; text-transform: uppercase; }

.nav { display: flex; flex-direction: column; gap: 2px; }
.nav-i {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-2);
  color: var(--ink-2);
  font-size: var(--fs-m);
  font-weight: 550;
  text-align: left;
  width: 100%;
  transition: background var(--t) var(--ease), color var(--t) var(--ease);
}
.nav-i svg { width: 18px; height: 18px; opacity: .85; }
.nav-i:hover { background: var(--g-3); color: var(--ink); }
/* Der aktive Punkt trägt links einen Balken — Farbe allein reicht nicht,
   wenn jemand Farben schlecht unterscheidet. */
.nav-i.on {
  background: var(--akz-schwach);
  color: var(--ink);
  box-shadow: inset 3px 0 0 var(--akz);
}
.nav-i.on svg { opacity: 1; color: var(--akz); }

.rail-fuell { flex: 1 1 auto; min-height: var(--s-4); }

.rail-theme, .rail-foot {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-2);
  color: var(--ink-2);
  width: 100%;
  text-align: left;
}
.rail-theme:hover, .rail-foot:hover { background: var(--g-3); color: var(--ink); }
.rt-ic { display: grid; place-items: center; width: 20px; }
.rt-label { font-size: var(--fs-s); }
.rail-foot { margin-top: var(--s-1); border: var(--haar) solid var(--line); background: var(--g-2); }
.fava, .avatar {
  display: grid; place-items: center;
  width: 30px; height: 30px;
  border-radius: var(--r-voll);
  background: var(--g-4);
  color: var(--ink);
  font-size: var(--fs-xs);
  font-weight: 700;
  flex: 0 0 auto;
}
.fmeta { display: flex; flex-direction: column; min-width: 0; line-height: 1.2; }
.fmeta strong, .fmeta b, .uname { font-size: var(--fs-s); font-weight: 650; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fmeta span, .fmeta small { font-size: var(--fs-xs); color: var(--ink-3); }
.ni { margin-left: auto; color: var(--ink-3); display: grid; place-items: center; }
.ni svg { width: 16px; height: 16px; }

.rail-scrim { display: none; }

.main { display: flex; flex-direction: column; min-width: 0; min-height: 100vh; }

.topbar {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  height: var(--topbar-h);
  padding: 0 var(--s-5);
  background: color-mix(in srgb, var(--g-1) 88%, transparent);
  backdrop-filter: blur(var(--blur-glas));
  border-bottom: var(--haar) solid var(--line);
  position: sticky;
  top: 0;
  z-index: 30;
}
.topbar-r { margin-left: auto; display: flex; align-items: center; gap: var(--s-2); }
.menu-btn { display: none; }

.content {
  flex: 1 1 auto;
  padding: var(--s-5);
  max-width: var(--inhalt-max);
  width: 100%;
  margin: 0 auto;
}

.mobile-tabbar { display: none; }

/* --------------------------------------------------------- 4. BAUSTEINE */

/* Knöpfe. Vier Sorten, mehr braucht ein Panel nicht: der eine Weg vorwärts
   (primary), der normale (neutral), der leise (ghost/subtle) und der, den
   man zweimal überlegt (danger). */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  padding: 0 var(--s-4);
  height: 36px;
  border-radius: var(--r-2);
  border: var(--haar) solid var(--line-stark);
  background: var(--g-3);
  color: var(--ink);
  font-size: var(--fs-s);
  font-weight: 600;
  white-space: nowrap;
  transition: background var(--t) var(--ease), border-color var(--t) var(--ease), transform var(--t) var(--ease);
}
.btn svg { width: 16px; height: 16px; }
.btn:hover { background: var(--g-4); border-color: var(--ink-aus); }
.btn:active { transform: translateY(1px); }
.btn[disabled], .btn:disabled, .btn[aria-disabled="true"] {
  opacity: .45;
  cursor: not-allowed;
  transform: none;
}
.btn.primary {
  background: var(--akz);
  border-color: var(--akz);
  color: var(--auf-akz);
}
.btn.primary:hover { background: color-mix(in srgb, var(--akz) 86%, #fff); }
.btn.ghost, .btn.subtle {
  background: transparent;
  border-color: var(--line);
  color: var(--ink-2);
}
.btn.ghost:hover, .btn.subtle:hover { background: var(--g-3); color: var(--ink); border-color: var(--line-stark); }
.btn.danger {
  background: var(--gefahr-schwach);
  border-color: color-mix(in srgb, var(--gefahr) 50%, transparent);
  color: var(--gefahr);
}
.btn.danger:hover { background: var(--gefahr); color: #fff; border-color: var(--gefahr); }
.btn.sm { height: 30px; padding: 0 var(--s-3); font-size: var(--fs-xs); }
.btn.xs { height: var(--h-xs); padding: 0 var(--s-2); font-size: var(--fs-xs); border-radius: var(--r-1); }
.btn-row, .page-actions, .ph-actions, .quickcmd-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  align-items: center;
}

.icon-btn {
  display: grid;
  place-items: center;
  width: 34px; height: 34px;
  border-radius: var(--r-2);
  border: var(--haar) solid transparent;
  color: var(--ink-2);
  transition: background var(--t) var(--ease), color var(--t) var(--ease);
}
.icon-btn svg { width: 17px; height: 17px; }
.icon-btn:hover { background: var(--g-3); color: var(--ink); border-color: var(--line); }

/* Karten. Eine Karte ist eine Fläche mit Rand — kein Schlagschatten-Karussell. */
.card {
  min-width: 0;
  overflow-wrap: anywhere;
  background: var(--g-2);
  border: var(--haar) solid var(--line);
  border-radius: var(--r-3);
  padding: var(--s-4);
}
.card-h {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  margin-bottom: var(--s-3);
  font-size: var(--fs-m);
  font-weight: 650;
}
.card-h .btn { margin-left: auto; }
.card-sub { font-size: var(--fs-s); color: var(--ink-3); margin-top: calc(var(--s-1) * -1); margin-bottom: var(--s-3); }
.section { margin-bottom: var(--s-5); }
.section-title {
  display: flex;
  align-items: baseline;
  gap: var(--s-3);
  margin: var(--s-6) 0 var(--s-3);
}
.section-title h2 { font-size: var(--fs-l); }
.st-sub { font-size: var(--fs-s); color: var(--ink-3); margin-left: auto; }

.page-head {
  display: flex;
  align-items: flex-start;
  gap: var(--s-4);
  margin-bottom: var(--s-5);
}
.page-head h1 { font-size: var(--fs-2xl); }
.page-head p { color: var(--ink-3); font-size: var(--fs-m); margin-top: var(--s-1); }
.ph-actions { margin-left: auto; }

/* Eingaben */
.inp, input[type="text"], input[type="password"], input[type="email"],
input[type="number"], input[type="search"], select, textarea {
  width: 100%;
  padding: 0 var(--s-3);
  height: 36px;
  background: var(--g-1);
  border: var(--haar) solid var(--line-stark);
  border-radius: var(--r-2);
  color: var(--ink);
  font-size: var(--fs-s);
  transition: border-color var(--t) var(--ease), background var(--t) var(--ease);
}
textarea { height: auto; padding: var(--s-3); min-height: 90px; resize: vertical; font-family: var(--ff-mono); }
.inp:focus, input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--akz);
  background: var(--g-2);
}
.inp::placeholder, input::placeholder, textarea::placeholder { color: var(--ink-aus); }
.inp.sm, input.sm, select.sm { height: 30px; font-size: var(--fs-xs); }
select { cursor: pointer; }
input[type="checkbox"], input[type="radio"] { accent-color: var(--akz); width: 15px; height: 15px; cursor: pointer; }

.field { display: flex; flex-direction: column; gap: var(--s-2); margin-bottom: var(--s-3); }
.field > label, .s-label, .gl, .dk-kopf, .dl-k {
  font-size: var(--fs-xs);
  color: var(--ink-3);
  font-weight: 600;
  letter-spacing: .07em;
  text-transform: uppercase;
}
.field-hint, .pv-hint { font-size: var(--fs-xs); color: var(--ink-3); }

/* Chips, Marken, Punkte */
.chip, .cf-chip, .connect-chip, .user-chip, .rankpill, .mv-pill, .mv-tag, .scroll-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: 3px var(--s-3);
  border-radius: var(--r-voll);
  background: var(--g-4);
  border: var(--haar) solid var(--line);
  color: var(--ink-2);
  font-size: var(--fs-xs);
  font-weight: 600;
  white-space: nowrap;
}
.cf-chips, .runtime-chips { display: flex; flex-wrap: wrap; gap: var(--s-2); align-items: center; }

.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: 3px var(--s-3);
  border-radius: var(--r-voll);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .04em;
  background: var(--g-4);
  color: var(--ink-2);
  border: var(--haar) solid var(--line);
}
.badge.ok { background: var(--akz-schwach); color: var(--akz); border-color: color-mix(in srgb, var(--akz) 35%, transparent); }
.badge.warn { background: var(--warn-schwach); color: var(--warn); border-color: color-mix(in srgb, var(--warn) 35%, transparent); }
.badge.danger { background: var(--gefahr-schwach); color: var(--gefahr); border-color: color-mix(in srgb, var(--gefahr) 35%, transparent); }
.badge.idle { background: var(--g-4); color: var(--ruhe); }

.dot, .bdot, .at-dot, .rs-punkt, .gc-punkt {
  display: inline-block;
  width: 8px; height: 8px;
  border-radius: var(--r-voll);
  background: var(--ruhe);
  flex: 0 0 auto;
}
.dot.run, .bdot.run, .at-dot.run, .rs-punkt.run, .gc-punkt.run,
.badge.ok .bdot, .ok > .bdot { background: var(--akz); }
.dot.stop, .rs-punkt.stop, .gc-punkt.stop { background: var(--ruhe); }
.at-dot.run, .rs-punkt.run, .gc-punkt.run { animation: pulsPunkt 2.4s var(--ease) infinite; }
@keyframes pulsPunkt {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--akz) 55%, transparent); }
  60%      { box-shadow: 0 0 0 5px transparent; }
}

.status-ring {
  display: grid; place-items: center;
  width: 22px; height: 22px;
  border-radius: var(--r-voll);
  border: 2px solid var(--akz);
}
.status-ring.stop { border-color: var(--ruhe); }
.status-ring i { width: 9px; height: 9px; border-radius: var(--r-voll); background: var(--akz); }
.status-ring.stop i { background: var(--ruhe); }

.live-badge { display: inline-flex; align-items: center; gap: var(--s-2); font-size: var(--fs-xs); color: var(--akz); }
.live-badge .ld, .ld { width: 7px; height: 7px; border-radius: var(--r-voll); background: var(--akz); animation: pulsPunkt 2s var(--ease) infinite; }

/* Balken */
.bar, .dk-bar, .dl-bar, .s-spark {
  height: 5px;
  border-radius: var(--r-voll);
  background: var(--g-4);
  overflow: hidden;
  width: 100%;
}
.bar > i, .dk-bar > i, .dl-bar > i {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: var(--akz);
  transition: width var(--t-lang) var(--ease);
}
.bar.ok > i, .dk-bar.ok > i, .dl-bar.ok > i { background: var(--akz); }
.bar.warn > i, .dk-bar.warn > i, .dl-bar.warn > i { background: var(--warn); }
.bar.danger > i, .dk-bar.danger > i, .dl-bar.danger > i { background: var(--gefahr); }
.dk-bar.ok, .dl-bar.ok { background: var(--akz-schwach); }
.dk-bar.warn, .dl-bar.warn { background: var(--warn-schwach); }
.dk-bar.danger, .dl-bar.danger { background: var(--gefahr-schwach); }

/* Tabellen */
.tbl-wrap { overflow-x: auto; border: var(--haar) solid var(--line); border-radius: var(--r-2); }
.tbl { width: 100%; border-collapse: collapse; font-size: var(--fs-s); }
.tbl th, .tbl td { padding: var(--s-3); text-align: left; border-bottom: var(--haar) solid var(--line); }
.tbl th {
  font-size: var(--fs-xs);
  color: var(--ink-3);
  font-weight: 650;
  letter-spacing: .06em;
  text-transform: uppercase;
  background: var(--g-1);
  position: sticky;
  top: 0;
}
.tbl tr:last-child td { border-bottom: 0; }
.tbl tbody tr:hover { background: var(--g-3); }

.grid { display: grid; gap: var(--s-4); }
.kvlist { display: flex; flex-direction: column; gap: var(--s-2); }
.kvrow { display: flex; align-items: baseline; gap: var(--s-3); font-size: var(--fs-s); }
.kvrow > :first-child { color: var(--ink-3); min-width: 150px; }

/* Statistik-Kachel (auch .stat, .gm/.gmm, .s-val/.s-sub) */
.stat, .gmm, .gm {
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
}
.s-val, .stat b, .gmm b { font-size: var(--fs-xl); font-weight: 700; letter-spacing: -.02em; }
.s-sub, .stat span { font-size: var(--fs-xs); color: var(--ink-3); }
.statrail { display: flex; flex-wrap: wrap; gap: var(--s-5); }

/* Ladezustand — ein Panel, das nichts sagt, während es lädt, fühlt sich
   kaputt an. */
.sk-row, .view-skeleton, .mv-spin, .bk-spin {
  position: relative;
  border-radius: var(--r-2);
  background: linear-gradient(90deg, var(--g-2) 25%, var(--g-3) 37%, var(--g-2) 63%);
  background-size: 400% 100%;
  animation: schimmer 1.4s linear infinite;
  min-height: 42px;
  margin-bottom: var(--s-2);
}
.view-skeleton { min-height: 220px; }
@keyframes schimmer { 0% { background-position: 100% 0; } 100% { background-position: 0 0; } }

/* Leerzustände: sagen, was fehlt, und bieten den nächsten Schritt an. */
.mv-empty, .ff-empty, .empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-7) var(--s-4);
  color: var(--ink-3);
  text-align: center;
}
.mv-empty-ic { opacity: .5; }
.mv-empty, .ff-empty, .empty { min-width: 0; }
.mv-empty *, .ff-empty *, .empty * { max-width: 100%; overflow-wrap: anywhere; }
.empty h3, .mv-empty strong { color: var(--ink); }

/* Gefahr-Bereiche */
.danger-card {
  border-color: color-mix(in srgb, var(--gefahr) 40%, var(--line));
  background: color-mix(in srgb, var(--gefahr) 6%, var(--g-2));
}
.danger-row {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) 0;
  border-bottom: var(--haar) solid var(--line);
}
.danger-row:last-child { border-bottom: 0; }
.danger-row .btn { margin-left: auto; }

/* --------------------------------------------------------- 5. DASHBOARD */
.dash {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: var(--s-3);
  margin-bottom: var(--s-5);
}
.dash-k {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  padding: var(--s-4);
  background: var(--g-2);
  border: var(--haar) solid var(--line);
  border-radius: var(--r-3);
}
.dk-kopf { display: flex; align-items: center; gap: var(--s-2); }
.dk-kopf svg { width: 15px; height: 15px; }
.dk-wert { font-size: var(--fs-2xl); font-weight: 700; letter-spacing: -.025em; line-height: 1; }
.dk-wert small, .dk-wert .dim { font-size: var(--fs-m); font-weight: 500; color: var(--ink-3); margin-left: var(--s-1); }
.dk-unten { font-size: var(--fs-xs); color: var(--ink-3); }

.gamegrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(272px, 1fr));
  gap: var(--s-4);
}
.gamecard {
  display: flex;
  flex-direction: column;
  background: var(--g-2);
  border: var(--haar) solid var(--line);
  border-radius: var(--r-3);
  overflow: hidden;
  cursor: pointer;
  transition: border-color var(--t) var(--ease), transform var(--t) var(--ease), box-shadow var(--t) var(--ease);
}
.gamecard:hover {
  border-color: var(--akz);
  transform: translateY(-2px);
  box-shadow: var(--sch-2);
}
.gamecover {
  position: relative;
  aspect-ratio: 16 / 7;
  display: grid;
  place-items: center;
  overflow: hidden;
  background: var(--g-3);
}
/* Modpack-Bilder sind 64×64 — hochskaliert bleiben sie hart, statt zu
   verwaschen. Ein größeres Bild (server-kachel.png) sticht, wenn es da ist. */
.gc-bild {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
}
.gamecover:has(.gc-bild) .gameicon { display: none; }
.gamecover:has(.gc-bild) .gamever { display: none; }
.gameicon { font-size: 30px; opacity: .5; }
.gamever {
  position: absolute; top: var(--s-2); left: var(--s-2);
  padding: 2px var(--s-2);
  border-radius: var(--r-1);
  background: #000000a6;
  color: #fff;
  font-size: var(--fs-xs);
  font-family: var(--ff-mono);
  max-width: calc(100% - var(--s-4));
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.gamebody { display: flex; flex-direction: column; gap: var(--s-2); padding: var(--s-4); }
.gamename {
  display: flex; align-items: center; gap: var(--s-2);
  font-size: var(--fs-m); font-weight: 650;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.gamecat { font-size: var(--fs-xs); color: var(--ink-3); }
.gamemini { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-3); margin-top: var(--s-1); }
.gmm { gap: 2px; }
.gmm b { font-size: var(--fs-m); }
.gi { display: inline-flex; }

/* ------------------------------------------------------------- 6. DECK
   Die Server-Ansicht: eine Kopfzeile, die IMMER dasselbe sagt (welcher
   Server, wie geht es ihm, was kann ich tun), links die Reiter, rechts der
   Inhalt. */
.deck-topbar {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  flex-wrap: wrap;
  padding: var(--s-3) var(--s-4);
  margin-bottom: var(--s-4);
  background: var(--g-2);
  border: var(--haar) solid var(--line);
  border-radius: var(--r-3);
  position: sticky;
  top: calc(var(--topbar-h) + var(--s-2));
  z-index: 20;
}
.dt-spacer { flex: 1 1 auto; }

.srv-switch {
  display: inline-flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-2);
  border: var(--haar) solid var(--line);
  background: var(--g-3);
  cursor: pointer;
}
.srv-switch:hover { border-color: var(--line-stark); }
.ss-bild { position: relative; display: grid; place-items: center; width: 30px; height: 30px; border-radius: var(--r-1); overflow: hidden; background: var(--g-4); }
.ss-bild img { width: 100%; height: 100%; object-fit: cover; }
.ss-bild .rs-punkt { position: absolute; right: 1px; bottom: 1px; box-shadow: 0 0 0 2px var(--g-3); }
.ssname { font-weight: 650; font-size: var(--fs-m); }
.sschev { color: var(--ink-3); display: grid; place-items: center; }
.sschev svg { width: 15px; height: 15px; }

.deck-live { display: flex; gap: var(--s-4); align-items: center; flex-wrap: wrap; }
.dl-m { display: flex; flex-direction: column; gap: 2px; min-width: 82px; }
.dl-m b { font-size: var(--fs-m); font-weight: 700; }
.dl-bar { height: 4px; }

.power { display: flex; gap: var(--s-2); }
.power button {
  display: grid; place-items: center;
  width: 36px; height: 36px;
  border-radius: var(--r-2);
  border: var(--haar) solid var(--line-stark);
  background: var(--g-3);
  color: var(--ink-2);
  transition: background var(--t) var(--ease), color var(--t) var(--ease), border-color var(--t) var(--ease);
}
.power button svg { width: 16px; height: 16px; }
.power button:hover { background: var(--g-4); color: var(--ink); }
.power .start.live, .power .start:not([disabled]) { color: var(--akz); border-color: color-mix(in srgb, var(--akz) 45%, transparent); }
.power .start.live:hover { background: var(--akz); color: var(--auf-akz); }
.power .stop:not([disabled]):hover { background: var(--gefahr); color: #fff; border-color: var(--gefahr); }
.power button[disabled] { opacity: .4; cursor: not-allowed; }

.deck-layout {
  display: grid;
  grid-template-columns: 214px minmax(0, 1fr);
  gap: var(--s-4);
  align-items: start;
}
.deck-side {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--s-2);
  background: var(--g-2);
  border: var(--haar) solid var(--line);
  border-radius: var(--r-3);
  position: sticky;
  top: calc(var(--topbar-h) + 84px);
}
/* Die Reiter der Server-Ansicht sind Zeilen, keine Karteikarten: elf Stück
   passen quer nie nebeneinander, ohne zu schrumpfen. */
.deck-side a, .deck-side button, .dk-i, .mtab {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-2);
  color: var(--ink-2);
  font-size: var(--fs-s);
  font-weight: 550;
  text-align: left;
  width: 100%;
  transition: background var(--t) var(--ease), color var(--t) var(--ease);
}
.deck-side a:hover, .deck-side button:hover, .dk-i:hover, .mtab:hover { background: var(--g-3); color: var(--ink); text-decoration: none; }
.deck-side a.on, .deck-side button.on, .dk-i.on, .mtab.on, .mtab.active {
  background: var(--akz-schwach);
  color: var(--ink);
  box-shadow: inset 3px 0 0 var(--akz);
}
.deck-side svg, .dk-i svg, .mtab svg { width: 16px; height: 16px; opacity: .8; }
.dk-strich, .km-strich {
  height: var(--haar);
  background: var(--line);
  margin: var(--s-2) var(--s-3);
}
.dk-kopf-gr, .deck-side .gl {
  padding: var(--s-3) var(--s-3) var(--s-1);
}
.tabpane { min-width: 0; }
.tabbar, .seg {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  background: var(--g-1);
  border: var(--haar) solid var(--line);
  border-radius: var(--r-2);
}
.tabbar button, .seg button, .seg-btn {
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-1);
  font-size: var(--fs-xs);
  font-weight: 650;
  color: var(--ink-2);
}
.tabbar button.on, .seg button.on, .seg-btn.active, .seg-btn.on {
  background: var(--g-3);
  color: var(--ink);
}

/* --------------------------------------------------------- 7. KONSOLE
   Sie ist der Grund, warum jemand dieses Panel öffnet. Also: viel Platz,
   Monospace, ruhiger Grund, und die Zeilen dürfen sich nicht verschieben,
   wenn eine neue kommt. */
.console-wrap, .cc-wrap, .logbox {
  background: var(--g-0);
  border: var(--haar) solid var(--line);
  border-radius: var(--r-3);
  overflow: hidden;
}
.logline, .cc-line {
  display: block;
  padding: 1px var(--s-4);
  font-family: var(--ff-mono);
  font-size: var(--fs-s);
  line-height: 1.55;
  color: var(--ink-2);
  white-space: pre-wrap;
  word-break: break-word;
}
.logline:hover, .cc-line:hover { background: var(--g-1); }
.logline.warn { color: var(--warn); }
.logline.err, .logline.danger { color: var(--gefahr); }
.logline.ok { color: var(--akz); }
.logdup {
  display: inline-block;
  margin-left: var(--s-2);
  padding: 0 var(--s-2);
  border-radius: var(--r-voll);
  background: var(--g-4);
  color: var(--ink-3);
  font-size: var(--fs-xs);
}
.prompt, .cmd-input, .qcmd {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-3);
  border-top: var(--haar) solid var(--line);
  background: var(--g-1);
}
.prompt > span:first-child, .cmd-input > span:first-child { color: var(--akz); font-family: var(--ff-mono); }
.prompt input, .cmd-input input { border: 0; background: transparent; font-family: var(--ff-mono); }
.prompt input:focus, .cmd-input input:focus { background: transparent; }
.scroll-pill {
  position: absolute;
  right: var(--s-4); bottom: var(--s-6);
  background: var(--g-4);
  cursor: pointer;
  box-shadow: var(--sch-2);
}
.kbd {
  display: inline-block;
  padding: 1px var(--s-2);
  border-radius: var(--r-1);
  border: var(--haar) solid var(--line-stark);
  background: var(--g-1);
  font-family: var(--ff-mono);
  font-size: var(--fs-xs);
  color: var(--ink-2);
}

/* ------------------------------------------------------- 8. LISTEN-VIEWS
   mv (Mods) · ff (Dateien) · net (Netzwerk) · bk (Sicherungen) · pl/spb
   (Spieler) · pv (Leistung) · ov (Übersicht) · set (Einstellungen).
   Sie teilen dieselben drei Formen: Raster, Zeile, Kopf. Deshalb stehen sie
   hier zusammen — jede Präfix-Familie noch einmal eigen zu bauen, hiesse
   dieselbe Regel achtmal zu schreiben und siebenmal zu vergessen. */
.modgrid, .mv-grid, .ff-grid, .net-list, .bk-list, .pl-list, .spb-list {
  display: grid;
  gap: var(--s-3);
}
.modgrid, .mv-grid { grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); }

.modcard, .mv-card, .net-card, .bk-card, .set-card, .pl-card, .spb-card, .ov-card, .pv-card, .pf-card {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  padding: var(--s-4);
  background: var(--g-2);
  border: var(--haar) solid var(--line);
  border-radius: var(--r-3);
}
.modcard { padding: 0; overflow: hidden; }
.modbanner, .modicon {
  width: 100%;
  aspect-ratio: 16 / 8;
  object-fit: cover;
  background: var(--g-3);
}
.modbody { display: flex; flex-direction: column; gap: var(--s-2); padding: var(--s-3) var(--s-4) var(--s-4); }
.modname { font-weight: 650; font-size: var(--fs-s); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.moddesc {
  font-size: var(--fs-xs);
  color: var(--ink-3);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.modmeta { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; margin-top: auto; }
.modmeta .btn { margin-left: auto; }

.net-row, .bk-row, .pl-row, .spb-row, .ff-row, .mv-row, .set-row, .ov-row {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3);
  background: var(--g-2);
  border: var(--haar) solid var(--line);
  border-radius: var(--r-2);
  font-size: var(--fs-s);
}
.net-row:hover, .bk-row:hover, .pl-row:hover, .spb-row:hover, .ff-row:hover, .mv-row:hover { border-color: var(--line-stark); background: var(--g-3); }
.net-row .btn, .bk-row .btn, .pl-row .btn, .spb-row .btn, .ff-row .btn { margin-left: auto; }

/* Dateien: eine Liste, in der man sich mit den Augen bewegt. Ordner zuerst,
   Name links, alles Weitere rechts und leiser. */
.ff-row { padding: var(--s-2) var(--s-3); border-radius: var(--r-1); border-color: transparent; background: transparent; }
.ff-row:hover { background: var(--g-2); border-color: var(--line); }
.ff-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ff-size, .ff-date, .ff-meta { color: var(--ink-3); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; }
.ff-ic, .ff-icon { color: var(--ink-3); display: grid; place-items: center; width: 20px; }
.ff-crumbs, .ff-pfad {
  display: flex; align-items: center; gap: var(--s-1); flex-wrap: wrap;
  font-family: var(--ff-mono); font-size: var(--fs-xs); color: var(--ink-3);
  padding: var(--s-2) 0;
}

/* Leistungsanzeige */
.pv-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: var(--s-3); }

/* Übersicht */
.ov-hstat { display: flex; flex-direction: column; gap: var(--s-1); }
.ov-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: var(--s-3); }

/* --------------------------------------------------- 9. MODAL, MENÜ, TOAST */
.modal, .modal-backdrop, .ssm {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  padding: var(--s-4);
  background: var(--scrim-mittel);
  backdrop-filter: blur(3px);
  z-index: 200;
}
.modal > *, .mbody {
  width: min(660px, 100%);
  max-height: 86vh;
  overflow-y: auto;
  background: var(--g-2);
  border: var(--haar) solid var(--line-stark);
  border-radius: var(--r-3);
  box-shadow: var(--sch-2);
}
.mbody { padding: var(--s-5); }
.mfoot {
  display: flex;
  justify-content: flex-end;
  gap: var(--s-2);
  padding: var(--s-4) var(--s-5);
  border-top: var(--haar) solid var(--line);
  position: sticky;
  bottom: 0;
  background: var(--g-2);
}
.menu, .ctxt {
  min-width: 190px;
  padding: var(--s-1);
  background: var(--g-2);
  border: var(--haar) solid var(--line-stark);
  border-radius: var(--r-2);
  box-shadow: var(--sch-2);
  z-index: 220;
}
.menu button, .ctxt button {
  display: flex; align-items: center; gap: var(--s-3);
  width: 100%; padding: var(--s-2) var(--s-3);
  border-radius: var(--r-1);
  font-size: var(--fs-s); color: var(--ink-2); text-align: left;
}
.menu button:hover, .ctxt button:hover { background: var(--g-3); color: var(--ink); }
.menu button.gefahr, .ctxt button.gefahr { color: var(--gefahr); }

/* Befehlspalette */
.cmdk {
  display: flex; align-items: center; gap: var(--s-2);
  flex: 1 1 auto; max-width: 460px;
  padding: 0 var(--s-3);
  height: 34px;
  background: var(--g-2);
  border: var(--haar) solid var(--line);
  border-radius: var(--r-2);
  color: var(--ink-3);
  font-size: var(--fs-s);
  cursor: pointer;
}
.cmdk:hover { border-color: var(--line-stark); }
.cmdk svg { width: 15px; height: 15px; }
.cmd-input { border: 0; }

/* --------------------------------------------------------- 10. ANMELDUNG */
.auth {
  display: grid;
  place-items: center;
  min-height: 100vh;
  padding: var(--s-5);
  background:
    radial-gradient(1100px 520px at 22% -8%, color-mix(in srgb, var(--akz) 13%, transparent), transparent 60%),
    var(--g-0);
}
.auth-card, .auth .card {
  width: min(400px, 100%);
  padding: var(--s-6);
  background: var(--g-2);
  border: var(--haar) solid var(--line);
  border-radius: var(--r-3);
  box-shadow: var(--sch-2);
}
.auth h1 { font-size: var(--fs-xl); margin-bottom: var(--s-2); }
.auth p { color: var(--ink-3); font-size: var(--fs-s); margin-bottom: var(--s-5); }
.auth .btn { width: 100%; height: 40px; }
.af-ic { color: var(--ink-3); }

/* ----------------------------------------------------- 11. SCHMALER SCHIRM
   Unter 1000px wird die Rail zu einer Schublade und die Reiter der
   Server-Ansicht legen sich quer. Nichts verschwindet — es wird nur
   umgeräumt. */
@media (max-width: 1000px) {
  .app { grid-template-columns: 1fr; }
  .rail {
    position: fixed;
    left: 0; top: 0; bottom: 0;
    width: var(--rail-b);
    z-index: 120;
    transform: translateX(-100%);
    transition: transform var(--t-lang) var(--ease);
    box-shadow: var(--sch-2);
  }
  .app.rail-open .rail { transform: none; }
  .rail-scrim {
    display: block;
    position: fixed; inset: 0;
    background: var(--scrim);
    z-index: 110;
    opacity: 0; pointer-events: none;
    transition: opacity var(--t) var(--ease);
  }
  .app.rail-open .rail-scrim { opacity: 1; pointer-events: auto; }
  .menu-btn { display: grid; }
  .content { padding: var(--s-4); padding-bottom: var(--s-8); }
  .deck-layout { grid-template-columns: 1fr; }
  .deck-side {
    position: static;
    flex-direction: row;
    overflow-x: auto;
    gap: var(--s-1);
  }
  .deck-side a, .deck-side button { width: auto; white-space: nowrap; }
  .deck-side a.on, .deck-side button.on { box-shadow: inset 0 -3px 0 var(--akz); }
  .dk-strich { display: none; }
  .deck-topbar { position: static; }
  .mobile-tabbar {
    display: flex;
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 100;
    padding: var(--s-2);
    gap: var(--s-1);
    background: color-mix(in srgb, var(--g-1) 94%, transparent);
    backdrop-filter: blur(var(--blur-glas));
    border-top: var(--haar) solid var(--line);
  }
  .mobile-tabbar > * {
    flex: 1 1 0;
    display: flex; flex-direction: column; align-items: center; gap: 2px;
    padding: var(--s-2) 0;
    border-radius: var(--r-2);
    font-size: var(--fs-xs);
    color: var(--ink-3);
  }
  .mobile-tabbar > *.on { color: var(--akz); background: var(--akz-schwach); }
}

@media (max-width: 620px) {
  .dash { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
  .gamegrid { grid-template-columns: 1fr; }
  .page-head { flex-direction: column; gap: var(--s-3); }
  .ph-actions { margin-left: 0; }
}

/* Wer Bewegung nicht möchte, bekommt keine. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
}

/* ==========================================================================
   12. NACHGEMESSEN AM ECHTEN DOM (ui-neu-20260910b)
   Was der erste Aufschlag nicht traf — gemessen an der laufenden Seite, nicht
   aus dem Kopf. Jede Regel hier hat einen Screenshot als Anlass.
   ========================================================================== */

/* Die Ein/Aus-Knöpfe tragen Symbol UND Wort ("Start", "Neustart", "Stop").
   Als 36-px-Quadrat lagen Wort und Symbol übereinander — dreimal unlesbar an
   der wichtigsten Stelle der Seite. Sie sind Knöpfe, also sehen sie aus wie
   welche; die Breite richtet sich nach dem Wort. */
.power { flex-wrap: wrap; }
.power button {
  width: auto;
  min-width: 36px;
  padding: 0 var(--s-3);
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  font-size: var(--fs-xs);
  font-weight: 650;
  white-space: nowrap;
}

/* Mod-Karten: das Bild bekommt eine feste Höhe, sonst bestimmt jedes
   Mod-Logo die Höhe seiner Karte und das Raster franst aus (gemessen: ein
   quadratisches JEI-Logo war doppelt so hoch wie die Karte daneben). */
.modgrid { align-items: stretch; }
.modcard { height: 100%; }
.modbanner {
  position: relative;
  display: grid;
  place-items: center;
  height: 116px;
  aspect-ratio: auto;
  background: var(--g-3);
  border-bottom: var(--haar) solid var(--line);
  overflow: hidden;
}
.modbanner img, .modicon img {
  width: 100%; height: 100%;
  object-fit: cover;
}
.modicon {
  display: grid; place-items: center;
  width: 100%; height: 100%;
  color: var(--ink-aus);
}
.modicon svg { width: 26px; height: 26px; }
.mv-gsv-flag, .mv-inst-flag {
  position: absolute; top: var(--s-2); right: var(--s-2);
  padding: 2px var(--s-2);
  border-radius: var(--r-1);
  background: var(--akz);
  color: var(--auf-akz);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .04em;
}
.mv-inst-flag { background: var(--info); color: #04101f; }
.modcard.mv-installed { border-color: color-mix(in srgb, var(--info) 45%, var(--line)); }

/* Kopf, Suche, Quellen, Ergebniskopf und Blätterleiste der Mod-Ansicht */
.mv-sec { padding: var(--s-4); }
.mv-h { display: flex; align-items: center; gap: var(--s-3); font-size: var(--fs-m); font-weight: 650; margin-bottom: var(--s-3); }
.mv-h-note { margin-left: auto; font-size: var(--fs-xs); }
.mv-searchbar { display: flex; align-items: center; gap: var(--s-2); margin-bottom: var(--s-3); }
.mv-searchbar .inp, .mv-searchbar input { flex: 1 1 auto; }
.mv-si { color: var(--ink-3); display: grid; place-items: center; }
.mv-sources { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; margin-bottom: var(--s-3); }
.mv-src-lbl { font-size: var(--fs-xs); }
/* Eine Quelle, die man wählen kann, muss aussehen wie ein Knopf. Vorher stand
   dort eine Reihe blanker Wörter, an denen nichts sagte, dass man klicken
   darf — und welches gerade gilt, sagte auch nichts. */
.catchip {
  padding: 3px var(--s-3);
  border-radius: var(--r-voll);
  border: var(--haar) solid var(--line-stark);
  background: var(--g-3);
  color: var(--ink-2);
  font-size: var(--fs-xs);
  font-weight: 650;
  cursor: pointer;
  transition: background var(--t) var(--ease), color var(--t) var(--ease), border-color var(--t) var(--ease);
}
.catchip:hover { background: var(--g-4); color: var(--ink); }
.catchip.on, .catchip.active, .catchip[aria-pressed="true"] {
  background: var(--akz-schwach);
  border-color: var(--akz);
  color: var(--akz);
}
.mv-results-head { display: flex; align-items: baseline; gap: var(--s-2); margin: var(--s-4) 0 var(--s-3); font-weight: 650; }
.mv-src-tag, .mv-dl-zero, .dl {
  display: inline-flex; align-items: center; gap: var(--s-1);
  font-size: var(--fs-xs); color: var(--ink-3);
}
.mv-src-tag {
  padding: 1px var(--s-2);
  border-radius: var(--r-1);
  background: var(--g-4);
  color: var(--ink-2);
  font-weight: 600;
}
.mv-pick { margin-left: auto; }
.mv-pager { display: flex; align-items: center; gap: var(--s-2); justify-content: center; margin-top: var(--s-4); }
.mv-pginfo { font-size: var(--fs-xs); }
.mv-vbtn { padding: var(--s-1) var(--s-2); border-radius: var(--r-1); color: var(--ink-3); }
.mv-vbtn.on { background: var(--g-3); color: var(--ink); }

/* Der Hub-Knopf trug seine eigene Farbe mit. In diesem Panel gibt es genau
   eine Akzentfarbe — ein zweiter, fremder Farbton an einer Stelle heisst
   sonst nur: hier hat jemand vergessen aufzuräumen. */
[class*="hub"] .btn, .btn[class*="hub"], .btn[style*="gradient"] {
  background: var(--g-3) !important;   /* !important: der alte Inline-Stil steht im Markup und lässt sich von aussen nicht entfernen */
  color: var(--ink) !important;
  border: var(--haar) solid var(--line-stark) !important;
}

/* Die Tour-Blase gehört über den Inhalt, nicht in ihn hinein. */
.gsv-tour-pop, .tour-pop, [class*="tour"][class*="pop"] {
  background: var(--g-2);
  border: var(--haar) solid var(--line-stark);
  border-radius: var(--r-3);
  box-shadow: var(--sch-2);
  color: var(--ink);
}

/* Meldungen */
.toast, .gsv-toast, [class*="toast"] {
  background: var(--g-2);
  border: var(--haar) solid var(--line-stark);
  border-radius: var(--r-2);
  box-shadow: var(--sch-2);
  color: var(--ink);
  font-size: var(--fs-s);
}

/* --- Die Konsolen-Leiste (gemessen an .console-bar) -----------------------
   „Live" und die Lupe lagen übereinander, weil die Leiste keine Reihe war
   und das Suchfeld sein Symbol nicht führte. Drei Dinge stehen hier
   nebeneinander: der Live-Punkt, die Suche (breit), die Filter. */
.console-bar {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  flex-wrap: wrap;
  padding: var(--s-3) var(--s-4);
  border-bottom: var(--haar) solid var(--line);
  background: var(--g-1);
}
.console-search {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--s-2);
  flex: 1 1 260px;
  min-width: 180px;
  padding: 0 var(--s-3);
  height: 32px;
  background: var(--g-2);
  border: var(--haar) solid var(--line);
  border-radius: var(--r-2);
}
.console-search svg { color: var(--ink-3); }
.console-search input {
  flex: 1 1 auto;
  height: auto;
  padding: 0;
  border: 0;
  background: transparent;
}
.console-search input:focus { background: transparent; }
.console-search:focus-within { border-color: var(--akz); }

/* Die Filter sind Schalter: an heisst an, aus heisst aus — vorher sahen alle
   sieben gleich aus, egal was gerade galt. */
.cf-chip {
  padding: 3px var(--s-3);
  border-radius: var(--r-voll);
  border: var(--haar) solid var(--line-stark);
  background: transparent;
  color: var(--ink-3);
  font-size: var(--fs-xs);
  font-weight: 650;
  cursor: pointer;
  transition: background var(--t) var(--ease), color var(--t) var(--ease), border-color var(--t) var(--ease);
}
.cf-chip:hover { color: var(--ink); border-color: var(--ink-aus); }
.cf-chip.on {
  background: var(--akz-schwach);
  border-color: color-mix(in srgb, var(--akz) 45%, transparent);
  color: var(--akz);
}

.console-log {
  position: relative;
  max-height: 62vh;
  overflow-y: auto;
  padding: var(--s-2) 0;
  background: var(--g-0);
}
.console-older-hint {
  padding: var(--s-2) var(--s-4);
  text-align: center;
  font-size: var(--fs-xs);
  color: var(--ink-3);
}
.logline.info { color: var(--ink-2); }
.logline.chat { color: var(--info); }
.logline.join { color: var(--akz); }
.logline.leave { color: var(--ink-3); }
.logline.cmd, .logline.befehl { color: var(--warn); }

/* Eine Karte, die nur einen Rahmen liefert (Konsole, Tabellen): kein
   Innenabstand, sonst sitzt das Log in einem Passepartout. */
.card.flush { padding: 0; overflow: hidden; }
.card.flush > .page-head, .card.flush > .card-h { padding: var(--s-4) var(--s-4) 0; }

/* Suchfelder im Panel-Stil (dieselbe Form wie die Konsolensuche) */
.search-box, .mv-searchbar label, .ff-suche {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: 0 var(--s-3);
  height: 34px;
  background: var(--g-1);
  border: var(--haar) solid var(--line-stark);
  border-radius: var(--r-2);
  flex: 1 1 auto;
}
.search-box input, .ff-suche input { border: 0; background: transparent; height: auto; padding: 0; }
.search-box input:focus, .ff-suche input:focus { background: transparent; }
.search-box svg, .ff-suche svg, .gi { color: var(--ink-3); }

/* ==========================================================================
   13. DIE ANMELDESEITE (gemessen an .auth-hero / .auth-pane)
   Sie ist die erste Seite, die jemand von GSV sieht. Zwei Spalten: links,
   wofür das Panel da ist, rechts der Weg hinein. Beim ersten Aufschlag lag
   beides untereinander in der linken oberen Ecke, die Aufzählung hatte
   Punkte statt Zeichen und „PasswortPasswort vergessen?" klebte zusammen.
   ========================================================================== */
.auth {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 460px);
  align-items: center;
  gap: var(--s-8);
  padding: var(--s-7) var(--s-6);
  min-height: 100vh;
  position: relative;
  overflow: hidden;
}
/* Der Schimmer im Hintergrund liegt HINTER allem und fängt keinen Klick ab. */
.auth-aurora, .auth-grid {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
}
.auth-aurora {
  background:
    radial-gradient(760px 460px at 12% 6%, color-mix(in srgb, var(--akz) 20%, transparent), transparent 62%),
    radial-gradient(620px 420px at 88% 92%, color-mix(in srgb, var(--info) 14%, transparent), transparent 60%);
}
.auth-grid {
  background-image:
    linear-gradient(var(--line-hell) var(--haar), transparent var(--haar)),
    linear-gradient(90deg, var(--line-hell) var(--haar), transparent var(--haar));
  background-size: 46px 46px;
  mask-image: radial-gradient(900px 620px at 30% 30%, #000 20%, transparent 78%);
  opacity: .5;
}
.auth-hero, .auth-pane { position: relative; z-index: 1; }
.auth-hero { max-width: 620px; margin-left: auto; }

.auth-brand { display: flex; align-items: center; gap: var(--s-3); margin-bottom: var(--s-5); }
.auth-wordmark { display: flex; align-items: baseline; gap: var(--s-2); line-height: 1; }
.auth-wordmark b { font-size: var(--fs-l); font-weight: 750; letter-spacing: -.02em; }
.auth-wordmark .gm, .auth-wordmark span {
  font-size: var(--fs-xs);
  color: var(--ink-3);
  letter-spacing: .16em;
  text-transform: uppercase;
}
.auth-h1 { font-size: clamp(30px, 4.2vw, 46px); line-height: 1.08; letter-spacing: -.03em; }
.auth-lede { margin-top: var(--s-4); color: var(--ink-2); font-size: var(--fs-l); line-height: 1.55; max-width: 54ch; }

/* Eine Aufzählung mit Zeichen davor braucht keine Aufzählungspunkte. */
.auth-feats { list-style: none; margin: var(--s-6) 0 0; padding: 0; display: grid; gap: var(--s-4); }
.auth-feats li { display: flex; align-items: flex-start; gap: var(--s-3); }
.af-ic {
  display: grid; place-items: center;
  width: 34px; height: 34px;
  flex: 0 0 auto;
  border-radius: var(--r-2);
  background: var(--akz-schwach);
  color: var(--akz);
  border: var(--haar) solid color-mix(in srgb, var(--akz) 30%, transparent);
}
/* Titel und Erklärung standen ohne Trennung hintereinander — b und span sind
   zwei Sätze, also stehen sie untereinander. */
.auth-feats li > div { display: flex; flex-direction: column; gap: 2px; }
.auth-feats li b, .auth-feats li strong { font-size: var(--fs-m); font-weight: 650; }
.auth-feats li span, .auth-feats li small { font-size: var(--fs-s); color: var(--ink-3); }

.auth-trust {
  display: flex; align-items: center; gap: var(--s-2);
  margin-top: var(--s-6);
  font-size: var(--fs-s);
  color: var(--ink-3);
}

.auth-pane { display: flex; justify-content: flex-start; }
.auth-card {
  width: 100%;
  max-width: 430px;
  padding: var(--s-6);
  background: color-mix(in srgb, var(--g-2) 92%, transparent);
  backdrop-filter: blur(var(--blur-glas));
  border: var(--haar) solid var(--line-stark);
  border-radius: var(--r-3);
  box-shadow: var(--sch-2);
}
.auth-brand--card { margin-bottom: var(--s-4); }
.auth-title { font-size: var(--fs-xl); }
.auth-sub { margin: var(--s-2) 0 var(--s-5); color: var(--ink-3); font-size: var(--fs-s); }
.auth-error {
  display: none;
  padding: var(--s-3);
  margin-bottom: var(--s-4);
  border-radius: var(--r-2);
  background: var(--gefahr-schwach);
  border: var(--haar) solid color-mix(in srgb, var(--gefahr) 40%, transparent);
  color: var(--gefahr);
  font-size: var(--fs-s);
}
.auth-error:not(:empty) { display: block; }
.auth-form { display: flex; flex-direction: column; gap: var(--s-4); }
/* Das Feld-Etikett und „Passwort vergessen?" gehören auf EINE Zeile, links
   und rechts — hintereinander gesetzt lasen sie sich als ein Wort. */
.auth-form .field > label { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-3); }
.auth-form .field > label a { font-size: var(--fs-xs); text-transform: none; letter-spacing: 0; font-weight: 500; }
.auth-form input { height: 42px; }
.auth-remember { display: flex; align-items: center; gap: var(--s-2); font-size: var(--fs-s); color: var(--ink-2); cursor: pointer; }
.auth-submit { width: 100%; height: 44px; font-size: var(--fs-m); }
.auth-foot { margin-top: var(--s-5); text-align: center; font-size: var(--fs-s); color: var(--ink-3); }
.auth-link { margin-left: var(--s-1); font-weight: 600; }

@media (max-width: 980px) {
  .auth { grid-template-columns: 1fr; gap: var(--s-6); padding: var(--s-5) var(--s-4); align-content: center; }
  .auth-hero { margin: 0 auto; text-align: center; }
  .auth-brand { justify-content: center; }
  .auth-feats { text-align: left; max-width: 420px; margin-inline: auto; }
  .auth-trust { justify-content: center; }
  .auth-pane { justify-content: center; }
}
/* Auf kleinen Schirmen zählt nur der Weg hinein. */
@media (max-width: 620px) {
  .auth-hero { display: none; }
}

/* ==========================================================================
   14. DIE NEUE SCHALE (ui-neu-20260910c)
   Owner: „das ist nix Neues, ich habe gesagt alles neu."
   Er hatte recht. Bis hierher waren nur Farben und Bausteine neu; der AUFBAU
   war der alte: Rail, Topbar, Deck-Kopf, Deck-Seitenleiste — vier Ebenen, die
   alle navigieren. Jetzt navigiert EINE Leiste, und rechts steht Inhalt.
   Was das bringt, ist keine Geschmacksfrage: zwischen Fensterrand und dem
   ersten Zeichen des Logs lagen zwei Leisten. Jetzt eine.
   ========================================================================== */

/* Die Topbar ist GELÖSCHT, nicht versteckt — sie wird nicht mehr gebaut. */
.topbar, .topbar-r { display: none; }

.main-griff { display: none; }

.rail { gap: var(--s-2); }

/* Suche in der Leiste, nicht mehr oben quer. */
.rail-suche {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  width: 100%;
  padding: 0 var(--s-3);
  height: 34px;
  margin-bottom: var(--s-2);
  background: var(--g-2);
  border: var(--haar) solid var(--line);
  border-radius: var(--r-2);
  color: var(--ink-3);
  font-size: var(--fs-s);
  text-align: left;
}
.rail-suche:hover { border-color: var(--line-stark); color: var(--ink-2); }
.rail-suche > span:first-of-type { flex: 1 1 auto; }
.rail-suche .kbd { font-size: 10px; }

/* Der Kasten, in dem die Reiter des offenen Servers stehen. */
.rail-kontext { display: none; }
.rail-kontext.an { display: block; }
.rail-kontext .deck-nav { display: flex; flex-direction: column; gap: 2px; }

/* Solange ein Server offen ist, tritt die Panel-Navigation zurück: sie ist
   dann nicht der Weg, den man sucht — aber sie verschwindet nicht, sonst
   fände niemand mehr aus dem Server heraus. */
#railnav.leise {
  margin-top: var(--s-3);
  padding-top: var(--s-3);
  border-top: var(--haar) solid var(--line);
  opacity: .62;
}
#railnav.leise:hover { opacity: 1; }

/* Die Reiter des Servers — dieselbe Form wie die Panel-Navigation, damit die
   Leiste EINE Leiste bleibt und nicht zwei übereinander. */
.deck-nav-grp {
  padding: var(--s-3) var(--s-3) var(--s-1);
  font-size: var(--fs-xs);
  color: var(--ink-3);
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
}
.deck-nav [data-tab] {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  width: 100%;
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-2);
  color: var(--ink-2);
  font-size: var(--fs-s);
  font-weight: 550;
  text-align: left;
  transition: background var(--t) var(--ease), color var(--t) var(--ease);
}
.deck-nav [data-tab]:hover { background: var(--g-3); color: var(--ink); }
.deck-nav [data-tab].on {
  background: var(--akz-schwach);
  color: var(--ink);
  box-shadow: inset 3px 0 0 var(--akz);
}
.deck-nav [data-tab] svg { width: 16px; height: 16px; opacity: .8; }
.deck-nav [data-tab].on svg { opacity: 1; color: var(--akz); }
/* Die Zähler (Spieler 0) rutschen nach rechts, statt am Wort zu kleben. */
.deck-nav [data-tab] .tcount, .deck-nav [data-tab] .nt + span { margin-left: auto; color: var(--ink-3); font-size: var(--fs-xs); }

/* Unterpunkte (Mods/Modpacks, Allgemein/Start/Ressourcen …) klappen unter
   ihrem Reiter auf und sind eingerückt — sonst liest man sie als weitere
   Hauptpunkte. */
.deck-nav-subs { display: none; flex-direction: column; gap: 1px; margin: 2px 0 var(--s-2) var(--s-6); }
.deck-nav-subs.open { display: flex; }
.deck-nav-sub {
  padding: var(--s-1) var(--s-3);
  border-radius: var(--r-1);
  border-left: 2px solid var(--line);
  color: var(--ink-3);
  font-size: var(--fs-xs);
  text-align: left;
}
.deck-nav-sub:hover { color: var(--ink); border-left-color: var(--line-stark); }
.deck-nav-sub.on { color: var(--akz); border-left-color: var(--akz); }
.deck-nav-sub.is-danger { color: var(--gefahr); }

/* KOMPAKT2-20260911 — JEDER PUNKT DER SERVER-LEISTE IST EIN BLOCK.
   Owner: „richtige Umrandung vielleicht — und fuer Mods und Welt und co
   auch extra?" Vorher hing an den Unterpunkten nur ein Haarstrich links;
   aufgeklappt sah das aus wie eine Liste, die unten ausfranst. Jetzt
   umschliesst EIN Rahmen den Kopf SAMT seinen Unterpunkten — wie eine Karte.
   Auch Punkte ohne Unterpunkte bekommen den Block: zwei eingefasste Gruppen
   neben vier losen Zeilen liest man sonst als zwei verschiedene Arten Weg.
   Alles aus den Tokens dieser Datei, kein eigenes Hex, keine freien px. */
.deck-nav-box {
  border: var(--haar) solid var(--line);
  border-radius: var(--r-2);
  margin-bottom: var(--s-1);
  overflow: hidden;              /* der Radius gilt auch fuer den Kopf darin */
  transition: border-color var(--t) var(--ease), background var(--t) var(--ease);
}
/* Aufgeklappt hebt sich der Block ab — Flaeche und kraeftigerer Rand. */
.deck-nav-box.auf {
  border-color: var(--line-stark);
  background: var(--g-2);
}
/* Die Form macht der Block. Der Knopf darin fuellt ihn aus, statt sich einen
   zweiten Radius innerhalb des Rahmens zu geben. */
.deck-nav .deck-nav-box > [data-tab] { border-radius: 0; }
/* Die Unterpunkte sitzen IM Block: durch eine Linie abgesetzt, eingerueckt,
   ohne den frueheren Aussenabstand — der schob sie aus dem Rahmen heraus. */
.deck-nav-box .deck-nav-subs {
  margin: 0;
  padding: var(--s-1) var(--s-2) var(--s-2) var(--s-4);
  border-top: var(--haar) solid var(--line);
}
.deck-nav-box .deck-nav-sub { padding: var(--s-1) var(--s-2); }

/* Werkzeuge und Staff-Schalter unten in der Leiste. */
.rail-werkzeug {
  display: flex;
  gap: var(--s-1);
  padding: var(--s-1) 0;
}
.rail-werkzeug .icon-btn { width: 32px; height: 32px; font-size: 14px; font-weight: 700; }
.rail [data-actas] {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  width: 100%;
  padding: var(--s-2) var(--s-3) !important;
  margin-bottom: var(--s-1);
  border: var(--haar) solid var(--line) !important;
  border-radius: var(--r-2);
  background: var(--g-2) !important;
  color: var(--ink-2) !important;
  font-size: var(--fs-xs) !important;
}
.rail [data-actas]:hover { background: var(--g-3) !important; color: var(--ink) !important; }

/* Der Inhalt hat jetzt die ganze Breite — es gibt keine zweite Spalte mehr. */
.content { padding: var(--s-5) var(--s-6); }
.deck-body { min-width: 0; }
.deck-layout { display: block; }
.deck-side { display: none; }

/* Der Server-Kopf ist ein schmaler Streifen, kein zweites Menü: er sagt, in
   welchem Server man ist und wie es ihm geht — mehr nicht. */
.deck-topbar {
  top: var(--s-2);
  padding: var(--s-2) var(--s-3);
  margin-bottom: var(--s-5);
}

@media (max-width: 1000px) {
  .main-griff {
    display: flex;
    align-items: center;
    height: 46px;
    padding: 0 var(--s-3);
    border-bottom: var(--haar) solid var(--line);
    background: var(--g-1);
    position: sticky; top: 0; z-index: 30;
  }
  .content { padding: var(--s-4); padding-bottom: var(--s-8); }
}

/* --- Die Konsole hat wieder Farbe (konsole-farbe-20260910) ----------------
   Nicht bunt: lesbar. Der Level trägt die Farbe, Thread und Logger treten
   zurück, Zahlen und Werte heben sich leicht ab, die Nachricht bleibt normal.
   Ein Log, in dem alles leuchtet, ist so unlesbar wie eines, in dem nichts
   leuchtet. */
.lg-thread { color: var(--ink-aus); }
.lg-logger { color: var(--ink-3); opacity: .85; }
.lg-lvl { font-weight: 700; }
.lvl-info  { color: var(--info); }
.lvl-warn  { color: var(--warn); }
.lvl-error, .lvl-fatal { color: var(--gefahr); }
.lvl-debug, .lvl-trace { color: var(--ruhe); }
.lg-key { color: var(--ink-3); }
.lg-val { color: var(--akz); }
.lg-num { color: color-mix(in srgb, var(--akz) 72%, var(--ink)); }

/* Der Zeitstempel bekommt eine eigene Spalte, damit die Nachrichten
   untereinander an derselben Stelle beginnen. */
.logline { display: flex; gap: var(--s-3); align-items: baseline; }
.lt { flex: 0 0 auto; color: var(--ink-aus); font-variant-numeric: tabular-nums; }
.lm { flex: 1 1 auto; min-width: 0; }

/* Die Kategorien färben die ganze Zeile — aber nur die, bei denen es zählt. */
.logline.error .lm, .logline.error { color: var(--gefahr); }
.logline.warn .lm { color: color-mix(in srgb, var(--warn) 82%, var(--ink)); }
.logline.chat .lm { color: var(--info); }
/* JOIN GRUEN, LEAVE ROT — und zwar SICHTBAR (Owner 10.09.2026: bei den
   Player-Joins in der Konsole gruen, beim Leave rot/orange, „damit man
   sieht, dass das anders ist — ist alles doch schon grau").
   Zwei Dinge waren schuld:
     · Leave stand auf --ink-3, dem Grau der Beschriftungen. Neben dem
       Grau der Info-Zeilen war das kein Unterschied, sondern eine Nuance.
     · Die Farbe traf nur .lm — die Spielernamen und Zahlen darin tragen
       aber ihre EIGENEN Regeln (.lg-num, .lg-key, .lg-logger, .lg-thread).
       Die gewannen, und uebrig blieb eine graue Zeile mit gruenem Rand.
   Deshalb faerbt die Kategorie jetzt auch die Bausteine IN der Zeile. */
.logline.join .lm { color: var(--akz); font-weight: 600; }
.logline.leave .lm { color: var(--gefahr); font-weight: 600; }
.logline.join .lm .lg-num, .logline.join .lm .lg-key, .logline.join .lm .lg-val,
.logline.join .lm .lg-logger, .logline.join .lm .lg-thread { color: inherit; }
.logline.leave .lm .lg-num, .logline.leave .lm .lg-key, .logline.leave .lm .lg-val,
.logline.leave .lm .lg-logger, .logline.leave .lm .lg-thread { color: inherit; }
/* Der NAME ist das, was man sucht — er steht fett, die Zeile drumherum
   traegt die Farbe. */
.lg-spieler { font-weight: 800; }
/* Die Absender-Marke an einer Panel-Zeile: blass und hinten. */
.lg-absender { color: var(--ink-3); font-style: italic; }
.logline.cmd .lm { color: var(--warn); font-weight: 600; }
.logline mark { background: color-mix(in srgb, var(--warn) 40%, transparent); color: var(--ink); border-radius: 2px; }

/* --- Linksbündig (art-20260910) ------------------------------------------
   Owner: „links wo Server ist mit dem Icon, mach das nach linksbündig."
   Die Ursache stand in einer einzigen Zeile weiter oben: `.ni` hatte
   `margin-left:auto` — gedacht für das Konto-Symbol unten, das wirklich nach
   rechts gehört. Nur trägt der Navigationspunkt dieselbe Klasse, und so
   schob dieselbe Regel dort das Symbol ans andere Ende der Zeile.
   Jetzt gilt sie nur noch dort, wo sie gemeint war. */
.ni { margin-left: 0; }
.rail-foot .ni { margin-left: auto; }
.nav-i { justify-content: flex-start; }
.nav-i .ni { display: grid; place-items: center; width: 18px; flex: 0 0 auto; }
.nav-i .nt { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Die Server-Art auf der Kachel: sie steht vor dem Zustand, weil sie sagt,
   WAS das ist — der Zustand sagt nur, ob es gerade läuft. */
.gamecat { display: flex; align-items: center; gap: var(--s-2); }
.gc-art { color: var(--ink-2); font-weight: 600; }
.gc-trenn { color: var(--ink-aus); }
.gc-zustand { color: var(--ink-3); }

/* Der Zusatz hinter der grossen Zahl („/ 2", „Ø je Kern", „GB") stand ohne
   Abstand direkt daran und las sich als ein Wort: „18%Ø je Kern". */
.dk-von {
  margin-left: var(--s-2);
  font-size: var(--fs-m);
  font-weight: 500;
  color: var(--ink-3);
  white-space: nowrap;
}
.dk-wert { display: flex; align-items: baseline; flex-wrap: wrap; gap: 0; }
.dk-unten { line-height: 1.4; }

/* --- Der Server-Wechsler (20260910d) --------------------------------------
   Owner: „bekomme ich hier ein richtiges Menü zum Wechseln oder was ist das
   hier?" — Er klappte als ungestylte Liste auf, in der Name und Zustand
   aneinanderklebten: „SoulrendOnline". Ein Menü muss aussehen wie eines. */
.srv-switch { position: relative; }
.srv-switch-menu {
  position: absolute;
  top: calc(100% + var(--s-2));
  left: 0;
  z-index: 60;
  min-width: 250px;
  max-height: 60vh;
  overflow-y: auto;
  padding: var(--s-1);
  background: var(--g-2);
  border: var(--haar) solid var(--line-stark);
  border-radius: var(--r-2);
  box-shadow: var(--sch-2);
}
.ssm-grp {
  padding: var(--s-2) var(--s-3) var(--s-1);
  font-size: var(--fs-xs);
  color: var(--ink-3);
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
}
.ssm-item {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-1);
  cursor: pointer;
  font-size: var(--fs-s);
}
.ssm-item:hover { background: var(--g-3); }
.ssm-item.on { background: var(--akz-schwach); box-shadow: inset 3px 0 0 var(--akz); }
.ssm-n { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
/* Der Zustand steht rechts und leise — er klebte vorher am Namen. */
.ssm-m { flex: 0 0 auto; color: var(--ink-3); font-size: var(--fs-xs); }

/* Auf dem Desktop hat die Handy-Leiste nichts zu suchen. Die Regel steht
   zusätzlich zum Inline-Stil, den app.js jetzt richtig setzt — doppelt hält,
   und eine Leiste, die über der Konto-Zeile liegt, ist kein Schönheitsfehler,
   sondern verdeckt einen Knopf. */
@media (min-width: 1001px) {
  .mobile-tabbar { display: none !important; }   /* !important: app.js setzt style.display direkt */
}

/* --- Fremdfarben abfangen (20260910d) --------------------------------------
   Einzelne Karten trugen ihren Farbverlauf im Markup mit — Violett, die
   Handschrift des Hubs. In diesem Panel gibt es EINEN Akzent; ein zweiter
   Farbton heisst sonst nur, dass jemand vergessen hat aufzuräumen.
   `!important`, weil der alte Wert als Inline-Stil im Markup steht und sich
   von aussen nicht entfernen lässt. */
.net-card[style*="gradient"], [class*="conn"][style*="gradient"],
.card[style*="linear-gradient"] {
  background: var(--g-2) !important;
  border-color: var(--line) !important;
}
/* Die Verbindungsadresse ist die wichtigste Zeile der Seite — sie bekommt
   einen ruhigen grünen Grund statt eines bunten Verlaufs. */
.net-conn, .conn-card, .net-card:first-of-type {
  background: linear-gradient(120deg, var(--akz-schwach), transparent 62%), var(--g-2) !important;
  border-color: color-mix(in srgb, var(--akz) 30%, var(--line)) !important;
}

/* ==========================================================================
   15. WAS DER ERSTE SATZ NICHT TRAF (20260910e)
   Drei Befunde des Owners, alle mit derselben Ursache: eine Klasse, die es
   gibt, die aber niemand gestylt hat. Ungestyltes CSS faellt nicht als Fehler
   auf — es liegt einfach falsch oder gar nicht da.
   ========================================================================== */

/* DER DIALOG HING OBEN LINKS. Ich hatte `.modal` als Overlay gestylt — es ist
   aber der KASTEN. Das Overlay heisst `.modal-ov`. Wer die beiden vertauscht,
   macht aus dem Fenster den Vorhang. */
.modal, .modal-backdrop, .ssm { position: static; inset: auto; display: block; padding: 0; background: none; backdrop-filter: none; }
.modal-ov {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  place-items: center;
  padding: var(--s-5);
  background: var(--scrim-stark);
  backdrop-filter: blur(4px);
}
.modal-ov > .modal, .modal {
  width: min(620px, 100%);
  max-height: 86vh;
  overflow-y: auto;
  background: var(--g-2);
  border: var(--haar) solid var(--line-stark);
  border-radius: var(--r-3);
  box-shadow: var(--sch-2);
}
.mv-wide { width: min(760px, 100%); }
.mv-wide-body { padding: var(--s-5); }
.mv-vmodal-head { display: flex; align-items: flex-start; gap: var(--s-4); margin-bottom: var(--s-4); }
.mv-vmodal-icon { width: 64px; height: 64px; flex: 0 0 auto; border-radius: var(--r-2); border: 0; }
.mv-vmodal-meta { font-size: var(--fs-xs); margin-top: var(--s-1); }
.mv-vmodal-desc { color: var(--ink-2); font-size: var(--fs-s); margin-bottom: var(--s-4); }
.mv-vmodal-subh { font-weight: 650; margin: var(--s-5) 0 var(--s-3); }

/* DIE SUCHE WAR VERDRAHTET UND UNSICHTBAR. Der Klick oeffnete das Overlay —
   nur hatte `.cmdk-ov` keine einzige Regel, also lag es ohne Groesse und ohne
   Grund irgendwo im Fluss. Ein Fenster, das niemand sieht, ist ein Knopf ohne
   Wirkung, und genau so kam es beim Owner an. */
.cmdk-ov {
  position: fixed;
  inset: 0;
  z-index: 220;
  display: flex;
  justify-content: center;
  align-items: flex-start;
  padding: 12vh var(--s-5) var(--s-5);
  background: var(--scrim-stark);
  backdrop-filter: blur(4px);
}
.cmdk-box {
  width: min(620px, 100%);
  background: var(--g-2);
  border: var(--haar) solid var(--line-stark);
  border-radius: var(--r-3);
  box-shadow: var(--sch-2);
  overflow: hidden;
}
.cmdk-in {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-4);
  border-bottom: var(--haar) solid var(--line);
}
.cmdk-in svg { color: var(--ink-3); }
.cmdk-in input {
  flex: 1 1 auto;
  height: auto;
  padding: 0;
  border: 0;
  background: transparent;
  font-size: var(--fs-l);
}
.cmdk-in input:focus { background: transparent; }
.cmdk-results { max-height: 52vh; overflow-y: auto; padding: var(--s-1); }
.cmdk-grp {
  padding: var(--s-3) var(--s-3) var(--s-1);
  font-size: var(--fs-xs);
  color: var(--ink-3);
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
}
.cmdk-item {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-2);
  cursor: pointer;
}
.cmdk-item:hover, .cmdk-item.sel { background: var(--g-3); }
.cmdk-item.sel { box-shadow: inset 3px 0 0 var(--akz); }
.ci { display: grid; place-items: center; width: 22px; color: var(--ink-3); }
.ctxt { display: flex; flex-direction: column; min-width: 0; }
.ctxt b { font-size: var(--fs-s); font-weight: 650; }
.ctxt small { font-size: var(--fs-xs); color: var(--ink-3); }
.cmdk-foot {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-3) var(--s-4);
  border-top: var(--haar) solid var(--line);
  font-size: var(--fs-xs);
  color: var(--ink-3);
}

/* --- Der Ansicht-Schalter (20260910e) --------------------------------------
   Zwei Hälften, eine aktiv. Vorher stand dort EIN Knopf mit dem Namen des
   aktuellen Zustands — was zugleich wie die Beschriftung der Aktion aussah.
   Bei einem Schalter, der die halbe Sicht auf das Panel umdreht, darf man
   nicht raten müssen, ob man liest, wo man ist, oder wohin man kommt. */
.ansicht-schalter {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 2px;
  padding: 3px;
  margin-bottom: var(--s-2);
  background: var(--g-1);
  border: var(--haar) solid var(--line);
  border-radius: var(--r-2);
}
.as-h {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-1);
  padding: var(--s-2) var(--s-1);
  border-radius: var(--r-1);
  color: var(--ink-3);
  font-size: var(--fs-xs);
  font-weight: 650;
  white-space: nowrap;
  transition: background var(--t) var(--ease), color var(--t) var(--ease);
}
.as-h:hover { color: var(--ink); }
.as-h.on { background: var(--g-3); color: var(--ink); }
/* Die Kundenansicht ist ein Ausnahmezustand — sie trägt Warnfarbe, damit
   niemand vergisst, dass er gerade nicht alles sieht. */
.as-h[data-ansicht="kunde"].on {
  background: var(--warn-schwach);
  color: var(--warn);
  box-shadow: inset 0 0 0 var(--haar) color-mix(in srgb, var(--warn) 40%, transparent);
}

/* --- Die Adresse im Server-Kopf (20260910f) --------------------------------
   Sie steht jetzt dort, wo man sie braucht: neben dem Namen, ein Klick
   kopiert. Vorher lag sie im Reiter „Netzwerk" — den ein Kunde ohne
   Domain-Recht gar nicht sinnvoll öffnen kann. */
.deck-adr {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  max-width: 320px;
  padding: var(--s-1) var(--s-3);
  border-radius: var(--r-2);
  border: var(--haar) solid var(--line);
  background: var(--g-1);
  color: var(--ink-2);
  font-family: var(--ff-mono);
  font-size: var(--fs-s);
}
.deck-adr:hover { border-color: var(--akz); color: var(--ink); }
.deck-adr svg { width: 14px; height: 14px; color: var(--ink-3); flex: 0 0 auto; }
.deck-adr:hover svg { color: var(--akz); }
.deck-adr-t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* --- Zwei Befunde des Optik-Laufs (20260910g) ------------------------------
   Beide gefunden von tools/games-optik.js, nicht von Hand — genau dafuer
   gibt es ihn. */

/* netzwerk-20260911: der Schimmer (.net-hero-glow) ist aus views/network.js
   verschwunden — die Regel dazu ist geloescht, nicht ueberschrieben. */
.net-hero, .net-card:first-of-type { position: relative; overflow: hidden; }

/* Im Mod-Dialog lag die Quellen-Marke („Gsv") zu 62 % auf dem Link „Mehr
   Infos ansehen". Beide sind anklickbar — wer den Link treffen wollte, traf
   die Marke. */
.mv-vmodal-meta { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; }
.mv-extlink {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  margin-top: var(--s-2);
  font-size: var(--fs-s);
  position: relative;
  z-index: 1;
}
.mv-vmodal-head .catchip, .mv-vmodal-meta .catchip { position: static; }

/* --- Titel und Erklärung stehen untereinander (20260910g) ------------------
   `<div class="tl"><b>Nächtlicher Auto-Neustart</b><small>Jede Nacht …</small></div>`
   ergab „Nächtlicher Auto-NeustartJede Nacht 00:00" — zwei Sätze, die als
   einer gelesen werden. Dasselbe Muster wie auf der Anmeldeseite: wo ein
   Fettdruck und eine Kleinschrift in einem Kasten stehen, sind es Titel und
   Erklärung, und die gehören übereinander. */
.tl, .set-tl, .kv-tl {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.tl > b, .set-tl > b, .kv-tl > b { font-size: var(--fs-m); font-weight: 650; }
.tl > small, .set-tl > small, .kv-tl > small { font-size: var(--fs-xs); color: var(--ink-3); }

/* Die Tour-Blase legte sich über Start/Neustart/Stop. Ein Hinweis, der die
   drei wichtigsten Knöpfe verdeckt, ist im Weg. */
.gsv-tour-pop, [class*="tour"][class*="pop"] { z-index: 240; max-width: min(340px, 92vw); }

/* --- Die Konsole IN der Übersicht (uebersicht-konsole-20260910) ------------
   Owner: „bei der Übersicht sollte direkt die Konsole sein."
   Sie ist keine Beilage mehr, sondern der grösste Kasten der Seite — dafür
   öffnet man ein Server-Panel. Die Schnellaktionen stehen daneben und
   schmal; auf schmalem Schirm rutschen sie darunter. */
/* 10.09.2026 — Owner: „warum ist die Konsole nach unten so extra lang, und
   warum ist rechts so viel Platz fuer Schnellaktionen, obwohl dort nix ist?"
   Zwei Antworten, zwei Zeilen:
     · Die Spalte war 300 px BREIT und die Karte darin nur ein paar Zeilen
       hoch — der Rest war Luft. Jetzt 240 px und Kacheln zu zweit.
     · Die Konsole hatte min-height 420 px UND max-height 46vh am Log; auf
       einem hohen Schirm zog die Karte darunter weiter, ohne Inhalt. */
/* EINE SPALTE. Die Zeile stand hier zweimal vergeblich (200-px-Spalte,
   dann 1fr): overview.js haengt sein eigenes Blatt zur LAUFZEIT ein, und das
   gewinnt bei gleicher Spezifitaet. Am 11.09.2026 (verschachteln) sind
   .ov-wrap und .ov-cols DORT geloescht — ab jetzt gilt nur, was hier steht.
   Nachgemessen: gridTemplateColumns = eine Spalte, gap 16 px. */
.ov-wrap { display: flex; flex-direction: column; gap: var(--s-4); }
.ov-cols {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s-4);
  align-items: start;
}
.ov-qcon { padding: 0; overflow: hidden; display: flex; flex-direction: column; min-width: 0; }
/* DIE ZWEI KARTENKOEPFE WAREN VERSCHIEDEN HOCH (Owner-Punkt, 12.09.2026).
   Gemessen im Browser: Konsolen-Kopf 50 px, Spieler-Kopf 46 px. Ursache ist
   der Inhalt, nicht das Polster: rechts im Konsolen-Kopf sitzt .btn.xs.ov-klapp
   mit 25 px, im Spieler-Kopf steht nur eine Textzeile (--fs-m x 1.5 = 21 px).
   12 + 25 + 12 + 1 = 50 gegen 12 + 21 + 12 + 1 = 46. Die Mindesthoehe rechnet
   deshalb aus denselben Tokens, aus denen die 50 px entstehen -- keine freie
   Zahl, und sie wandert mit, wenn --h-xs oder --s-3 sich aendern. */
.ov-qcon > .card-h {
  padding: var(--s-3) var(--s-4);
  min-height: calc(var(--h-xs) + 2 * var(--s-3) + var(--haar));
  border-bottom: var(--haar) solid var(--line);
  margin: 0;
}
/* Die Konsole ist so hoch wie ihr Inhalt — nicht 420 px, komme was wolle.
   Owner 10.09.2026: „warum ist die Konsole nach unten so extra lang?"
   Gemessen war es die Kombination aus min-height:420px hier und
   max-height:46vh am Log: auf einem hohen Schirm blieb unter der Eingabe
   ein leerer Streifen stehen. Jetzt gibt das Log die Hoehe vor. */
.ov-konsole-halter { display: flex; flex-direction: column; min-width: 0; }
/* Die eingebettete Konsole hat keinen eigenen Seitenkopf — der Kasten IST
   ihre Überschrift. */
.ov-konsole-halter > .page-head { display: none; }
/* DIE KONSOLE IST DER GROESSTE KASTEN DER SEITE (Owner 10.09.2026).
   Mit der Hero-Karte darueber sind rund 110 px frei geworden — die bekommt
   sie. 64vh statt 46vh, und der Kasten darunter darf nicht laenger sein als
   sein Inhalt (siehe .card.flush unten). */
.ov-konsole-halter .console-log { max-height: 64vh; min-height: 320px; }
/* DER LEERE STREIFEN UNTER DER EINGABE — gemessen am 10.09.2026:
   .card.flush im Konsolen-Halter trug min-height:420px und war 710 px hoch,
   waehrend das Log bei 460 px endete. Die Karte zog sich also auf eine
   Hoehe, fuer die es keinen Inhalt gab. Hier gibt der Inhalt die Hoehe vor. */
.ov-konsole-halter .card.flush { min-height: 0; }
.ov-konsole-halter .card { border: 0; border-radius: 0; background: transparent; }

/* --- Konsole auf-/zuklappbar, dahinter Spieler & Raenge (verschachteln-20260911)
   Owner: „mach oben ne Minimieren-Button, ne − oder sowas, dass er dann die
   aktiven Player zeigt, wann wer online war … Dahinter die Spielerverwaltung,
   mit Ranks und co."
   Rechts im Kopf sitzen Kurzzeile und Knopf; der Kopf selbst bleibt keine
   Tuer. Die Spielerkarte traegt dieselbe Klasse .ov-qcon wie die Konsole —
   ein Rahmen, ein Kopf, ein Regelsatz. */
.ov-kopf-rechts { margin-left: auto; display: inline-flex; align-items: center; gap: var(--s-3); min-width: 0; flex: 0 1 auto; }
.ov-qcon > .card-h .ov-kopf-rechts .btn { margin-left: 0; flex: 0 0 auto; }
/* − / + : so hoch wie breit, sonst ein schmaler Strich zum Zielen */
/* .btn.xs (Z.389) hat (0,2,0) — der Selektor muss darueber liegen, sonst gewinnt die alte Regel und padding/font-size sind unsichtbar */
.btn.xs.ov-klapp { aspect-ratio: 1; padding: 0; justify-content: center; font-family: var(--ff-mono); font-size: var(--fs-m); line-height: 1; }
/* Kurzzeile: „● Live · x/y online · zuletzt: Name vor Zeit" — eine Zeile, mit … statt Abschneiden */
.ov-kurz { display: block; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--fs-xs); font-weight: 600; color: var(--ink-2); }
.ov-kurz .dot { vertical-align: middle; margin-right: var(--s-1); }
.ov-kurz [data-ov-kurz-live] { color: var(--ink); }

.ov-sp-summe { font-size: var(--fs-xs); font-weight: 600; color: var(--ink-3); white-space: nowrap; }
.ov-sp-fehler { padding: var(--s-2) var(--s-4); font-size: var(--fs-s); color: var(--gefahr); background: var(--gefahr-schwach); border-bottom: var(--haar) solid var(--line); overflow-wrap: anywhere; }
.ov-sp-liste { display: flex; flex-direction: column; max-height: 64vh; overflow-y: auto; }
.ov-sp-leer { padding: var(--s-4); font-size: var(--fs-s); color: var(--ink-3); }
.ov-sp-zeile { display: flex; align-items: center; gap: var(--s-3); padding: var(--s-2) var(--s-4); border-bottom: var(--haar) solid var(--line); min-width: 0; }
.ov-sp-zeile:last-child { border-bottom: 0; }
.ov-sp-name { font-weight: 650; font-size: var(--fs-m); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ov-sp-zeile.ist-offline .ov-sp-name { color: var(--ink-2); }
.ov-sp-zeit { margin-left: auto; font-size: var(--fs-xs); color: var(--ink-3); white-space: nowrap; }
.ov-sp-zeit.laedt { color: var(--warn); }
.btn.xs.ov-sp-mehr { flex: 0 0 auto; aspect-ratio: 1; padding: 0; justify-content: center; font-weight: 700; }
/* Rang-Marken. Es gibt KEIN Rang-Token — die Zustandsfarben der Palette
   stehen fuer Owner (gefahr), Admin (warn), Mod/Staff (info), VIP (akz);
   jeder andere Rang bleibt die Grundpille (.rankpill oben). Bekommt das
   Design einmal eigene Rang-Farben, gehoeren sie in :root, nicht hierher. */
.rankpill.ov-rang-owner { color: var(--gefahr); background: var(--gefahr-schwach); border-color: color-mix(in srgb, var(--gefahr) 35%, transparent); }
.rankpill.ov-rang-admin { color: var(--warn); background: var(--warn-schwach); border-color: color-mix(in srgb, var(--warn) 35%, transparent); }
.rankpill.ov-rang-mod   { color: var(--info); background: var(--info-schwach); border-color: color-mix(in srgb, var(--info) 35%, transparent); }
.rankpill.ov-rang-vip   { color: var(--akz); background: var(--akz-schwach); border-color: color-mix(in srgb, var(--akz) 35%, transparent); }
/* Schwebende Menues: das …-Menue der Spielerzeile (.ov-sp-*) und das
   Rechtsklick-Menue der Backup-Zeile (.bk-m*, backupmenue-20260911). Beide
   liegen im DOKUMENT (position:fixed), weil ihre Karte abschneidet
   (overflow:hidden) — sonst waere die letzte Zeile ohne Menue. Ein Satz fuer
   beide: das Backup-Menue ist abgeschaut, nicht neu erfunden. */
.ov-sp-menu, .bk-menu {
  position: fixed;
  z-index: 60;
  padding: var(--s-1);
  background: var(--g-2);
  border: var(--haar) solid var(--line-stark);
  border-radius: var(--r-2);
  box-shadow: var(--sch-2);
}
/* Nur das Spieler-Menue haengt unter einem Knopf und braucht den Abstand;
   das Backup-Menue steht am Zeiger und wird in JS auf die Kante korrigiert —
   ein Aussenabstand wuerde diese Rechnung um genau --s-1 verfehlen. */
.ov-sp-menu { margin-top: var(--s-1); }
.ov-sp-mk, .bk-mk { padding: var(--s-2) var(--s-3) var(--s-1); font-size: var(--fs-xs); color: var(--ink-3); font-weight: 700; letter-spacing: .09em; text-transform: uppercase; white-space: nowrap; }
.ov-sp-mi, .bk-mi { display: flex; width: 100%; align-items: center; gap: var(--s-3); padding: var(--s-2) var(--s-3); border-radius: var(--r-1); color: var(--ink); font-size: var(--fs-s); text-align: left; white-space: nowrap; }
.ov-sp-mi:hover, .bk-mi:hover { background: var(--g-3); }
.ov-sp-mi.gefahr, .bk-mi.gefahr { color: var(--gefahr); }
.bk-mi.gefahr:hover { background: var(--gefahr-schwach); }
.ov-sp-mi.aktiv { background: var(--akz-schwach); box-shadow: inset var(--s-1) 0 0 var(--akz); }
.ov-sp-mi:disabled, .bk-mi:disabled { color: var(--ink-aus); cursor: default; }
/* Die Trennlinie ueber „Löschen" — der letzte Punkt steht abgesetzt, damit
   ihn niemand im Vorbeiziehen trifft. */
.bk-msep { height: var(--haar); margin: var(--s-1) 0; background: var(--line); }

/* HIER STAND DER REGELSATZ DER KARTE „Schnellaktionen" (.ov-actions,
   .qa-grid, .qa, .qa-karte). Die Karte ist am 10.09.2026 geloescht worden —
   alles darin stand ohnehin als direkter Eintrag in der linken Leiste. Ihre
   Regeln gehen deshalb mit. */

/* Der @media-Zweig fuer die zweite Spalte ist mit ihr gegangen. */

/* Lange Log-Zeilen brachen 6 px über den Rand — in einem Kasten ohne
   waagerechten Rollbalken muss jedes Wort umbrechen dürfen, auch eines ohne
   Leerzeichen (Java-Klassennamen sind so). */
.ov-konsole-halter .logline, .ov-konsole-halter .lm,
.console-log .logline, .console-log .lm {
  overflow-wrap: anywhere;
  word-break: break-word;
  min-width: 0;
}


/* ===========================================================================
   10.09.2026 — ALLES ÜBER DEN SERVER STEHT IN DER KOPFZEILE
   ---------------------------------------------------------------------------
   Owner: „dann die CPU und Co, also das der Server mit Name, wie lange —
   alles oben in die Leiste da. Und die Leiste darunter auch weg, damit die
   Konsole größer ist."
   Die Hero-Karte der Übersicht ist gelöscht (overview.js). Was sie zeigte,
   steht jetzt hier: Laufzeit, Spielerzahl, CPU, RAM, Speicher — in EINER
   Zeile mit Namen, Zustand, Adresse und den Ein/Aus-Knöpfen.
   =========================================================================== */
.dt-live {
  display: inline-flex;
  align-items: center;
  gap: var(--s-3);
  flex-wrap: wrap;
  row-gap: var(--s-1);
  min-width: 0;
}
.dt-uptime, .dt-players {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  color: var(--ink-2);
  font-size: var(--fs-xs);
  white-space: nowrap;
}
.dt-uptime svg, .dt-players svg { width: 14px; height: 14px; }
.dt-rings {
  display: inline-flex;
  align-items: center;
  gap: var(--s-3);
  flex-wrap: wrap;
  row-gap: var(--s-1);
  min-width: 0;
}
/* Die Ringe sind in der Kopfzeile eine Spur kleiner als in der alten
   Hero-Karte — dort hatten sie eine eigene Zeile, hier teilen sie sich eine
   mit sechs anderen Dingen. */
/* --- Kennzahl im Kopf: Ring + Text (token-20260912) ---------------------
   HIER STAND `.dt-rings .mini-ring, .dt-rings .hero-chip { font-size: ... }`.
   Der Regelsatz ist GELÖSCHT, nicht überschrieben — er traf nichts: die
   Klassen `mini-ring` und `hero-chip` kamen in keiner JS-Datei des Panels
   vor (gemessen 12.09.2026: nur in DIESER Datei). Das Markup trug seine
   Größen als Inline-Stil (9 / 9,6 / 10,5 px) und seine Farben als festes
   Hex — beides gewann deshalb immer, und beides steht nicht in den Token.
   Jetzt trägt das Markup Klassen und eine Stufe; jeder Wert kommt von hier. */
.dt-ring {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  cursor: pointer;
  border-radius: var(--r-1);
}
.dt-ring:focus-visible { outline: var(--haar) solid var(--akz); outline-offset: var(--s-1); }
.dt-ring-uhr { display: block; width: 34px; height: 34px; flex: 0 0 auto; }
.dt-ring-bahn { stroke: var(--line-stark); }
/* Die Stufe färbt Ring UND Zahl — eine Quelle, nicht zwei. */
.dt-ring[data-stufe="gut"] .dt-ring-fuell { stroke: var(--akz); }
.dt-ring[data-stufe="gut"] .dt-ring-pz { color: var(--akz); }
.dt-ring[data-stufe="warn"] .dt-ring-fuell { stroke: var(--warn); }
.dt-ring[data-stufe="warn"] .dt-ring-pz { color: var(--warn); }
.dt-ring[data-stufe="gefahr"] .dt-ring-fuell { stroke: var(--gefahr); }
.dt-ring[data-stufe="gefahr"] .dt-ring-pz { color: var(--gefahr); }
.dt-ring-txt { display: inline-flex; flex-direction: column; line-height: 1.35; min-width: 0; }
.dt-ring-k { display: inline-flex; align-items: baseline; gap: var(--s-1); }
.dt-ring-l { font-size: var(--fs-xs); font-weight: 700; color: var(--ink); }
.dt-ring-pz { font-size: var(--fs-xs); font-weight: 800; font-variant-numeric: tabular-nums; }
.dt-ring-v { font-size: var(--fs-xs); color: var(--ink-2); white-space: nowrap; font-variant-numeric: tabular-nums; }
.dt-ring-v2 { font-size: var(--fs-xs); color: var(--ink-3); white-space: nowrap; }
.dt-chip-sym {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px; height: 34px;
  flex: 0 0 auto;
  border-radius: var(--r-voll);
  border: var(--haar) solid var(--line-stark);
  color: var(--ink-3);
}
.dt-chip-sym svg { width: 16px; height: 16px; }

/* HIER STAND `@media (max-width:1100px){ .dt-live { display: none } }`.
   Damit war die EINE Stelle, an der CPU, RAM und Speicher überhaupt stehen,
   auf einem schmalen Fenster leer — gemessen am 12.09.2026 bei 1100 px:
   kein Ring mehr im Dokument, kein Ersatz irgendwo. Der Owner hat diese
   Werte ausdrücklich nach oben verlangt („die CPU und Co … alles oben in
   die Leiste"); sie dürfen umziehen, aber nicht verschwinden.
   Jetzt: eigene Zeile unter Name, Zustand und Adresse. Nur die Zweitzeile
   (Container-Speicher, ohnehin nur für Staff) fällt weg. */
@media (max-width: 1100px) {
  .dt-live { order: 9; flex-basis: 100%; margin-top: var(--s-1); }
  .dt-ring-v2 { display: none; }
}

/* --- Der Wiederherstellungs-Hinweis mit Kreuz --------------------------- */
.ov-restore {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  margin: 0 0 var(--s-3);
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-3);
  /* Farbe NUR aus den Token dieser Datei — --warn-line/--warn-flaeche gibt
     es nicht, und ein eigenes rgba() waere genau die freie Farbe, die hier
     nicht sein darf. Die Warnfarbe traegt der Rand, die Flaeche bleibt eine
     Karte. */
  border: var(--haar) solid var(--warn);
  background: var(--g-2);
  font-weight: 600;
}
.ov-restore-t { flex: 1 1 auto; min-width: 0; }
.ov-restore-x {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: var(--r-2);
  background: transparent;
  color: var(--ink-2);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}
.ov-restore-x:hover { background: var(--g-3); color: var(--ink); }

/* --- Die Ein/Aus-Knöpfe sagen ihre Farbe ------------------------------- */
/* Owner 10.09.2026: „wenn Server an, mach Start zu Stop und Neustart zu gelb
   oder sowas". Also: läuft er, ist Stop rot und Neustart gelb. Steht er,
   sind beide aus und nur Start trägt Farbe — dann kann man nichts kaputt
   machen, und die Farbe soll auch nicht so tun, als könnte man. */
.power .restart.warn:not([disabled]) {
  border-color: var(--warn);
  color: var(--warn);
}
.power .restart.warn:not([disabled]):hover { background: var(--g-3); }
.power .stop.scharf:not([disabled]) {
  border-color: var(--gefahr);
  color: var(--gefahr);
}
.power .stop.scharf:not([disabled]):hover { background: var(--g-3); }

/* --- Name, Zustand und Adresse im Kopf etwas groesser ---------------------
   Owner 10.09.2026: „dann also oben die Apocalypse, Läuft, IP etwas größer
   bitte."
   Die drei sagen, WO man ist — sie standen in derselben Groesse wie die
   Nebenwerte daneben. Alle drei eine Stufe hoch (fs-m -> fs-l, fs-xs ->
   fs-s), nicht mehr: die Kopfzeile muss in einer Zeile bleiben, sonst
   springt sie um und nimmt der Konsole wieder Hoehe. Werte NUR aus den
   Token dieser Datei. */
.deck-topbar .ssname { font-size: var(--fs-l); }
.deck-topbar .runtime-chips .badge { font-size: var(--fs-s); padding: 4px var(--s-3); }
.deck-topbar .deck-adr { font-size: var(--fs-m); max-width: 380px; }

/* „In G-SV Hub spielen" in der Kopfzeile (10.09.2026). Er stand bis heute
   unten in der Karte „Schnellaktionen"; die ist geloescht, weil alles andere
   darin auch links in der Leiste steht. Dieser eine Punkt gab es sonst
   nirgends — also steht er dort, wo die anderen Server-Handlungen stehen.
   Er traegt Akzentfarbe, weil er als einziger Knopf dort etwas OEFFNET statt
   den Server zu schalten. */
.dt-hub { flex: 0 0 auto; white-space: nowrap; }
/* Hub-Knopf und Ein/Aus-Knoepfe als EINE Gruppe: sonst brach die Kopfzeile
   zwischen ihnen um und Start/Neustart/Stop standen allein in Zeile zwei. */
.dt-tat {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  flex: 0 0 auto;
}
/* Die Live-Werte duerfen schrumpfen, bevor die Knoepfe umbrechen — sie sind
   Auskunft, die Knoepfe sind Handlung. */
.dt-live { flex: 0 1 auto; overflow: hidden; }

/* --- Die Laufzeit im Platz des Start-Knopfes (11.09.2026) ----------------
   Owner: „dort wo Start ist ausgegraut, soll der Timer drinne stehen — dort
   sollte stehen 3h und 33m oder sowas" · „in Start, nirgends anders."
   Sie sieht aus wie ein Knopf, weil sie an dessen Platz steht und die Zeile
   sonst einen Sprung bekäme — aber sie ist keiner: kein Zeiger, kein Hover,
   nichts zum Drücken. Eine Anzeige, die sich als Knopf ausgibt, ist eine
   Einladung zum Klicken ins Leere. */
.power .pw-laufzeit {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  height: 36px;
  min-width: 36px;
  padding: 0 var(--s-3);
  border-radius: var(--r-2);
  border: var(--haar) solid var(--line);
  background: var(--g-2);
  color: var(--ink-2);
  font-size: var(--fs-xs);
  font-weight: 650;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  cursor: default;
}
.power .pw-laufzeit svg { width: 15px; height: 15px; color: var(--ink-3); }

/* === Konto-Popup (popup-20260911) =========================================
   Owner 11.09.: „ich will bei Account unten nen richtiges Popup-Menue, nicht
   nur unten das Erweitern!" — gemessen: es gab fuer .konto-menue in KEINEM
   Stylesheet eine Regel, deshalb liefen die Eintraege ineinander. Das Markup
   (app.js kontoMenueAuf: .km-i, .km-strich) stand die ganze Zeit da.
   Nur Tokens, keine freien Werte. Position setzt app.js zur Laufzeit. */
.konto-menue {
  position: fixed;
  z-index: 400;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--s-2);
  background: var(--g-2);
  border: 1px solid var(--line-stark);
  border-radius: var(--r-3);
  box-shadow: var(--sch-2);
}
.konto-menue .km-i {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  width: 100%;
  padding: var(--s-2) var(--s-3);
  background: transparent;
  border: 0;
  border-radius: var(--r-1);
  color: var(--ink);
  font: inherit;
  font-size: var(--fs-m);
  text-align: left;
  white-space: nowrap;
  cursor: pointer;
}
.konto-menue .km-i:hover,
.konto-menue .km-i:focus-visible { background: var(--g-3); outline: none; }
.konto-menue .km-i .ni { display: flex; flex: 0 0 auto; color: var(--ink-3); }
.konto-menue .km-i .ni svg { width: 16px; height: 16px; }
.konto-menue .km-i:hover .ni { color: var(--akz); }
.konto-menue .km-strich { height: 1px; margin: var(--s-1) 0; background: var(--line); }
.konto-menue .km-i.gefahr { color: var(--gefahr); }
.konto-menue .km-i.gefahr:hover { background: var(--gefahr-schwach); }
.konto-menue .km-i.gefahr .ni { color: var(--gefahr); }

/* PN-VOICE-006/007 (24.09.2026): Umschalter .tog + Aufklapp-Zeichen Admin-Infra. Stil von Qwen (qwen3.8-27b), nur Tokens. */
.tog-row {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) 0;
}

.tog-row .tl {
  flex: 1;
  min-width: 0;
}

.tog-row .tl b {
  display: block;
  font-size: var(--fs-m);
  font-weight: 700;
  color: var(--ink);
}

.tog-row .tl small {
  display: block;
  font-size: var(--fs-s);
  color: var(--ink-3);
  margin-top: calc(var(--s-1) / 2);
}

.tog {
  flex: 0 0 auto;
  position: relative;
  width: calc(var(--s-6) + var(--s-3));
  height: var(--s-5);
  border-radius: var(--r-voll);
  background-color: var(--g-4);
  border: var(--haar) solid var(--line);
  cursor: pointer;
  transition: all var(--tr);
}

.tog::after {
  content: "";
  position: absolute;
  top: calc(var(--s-1) / 2);
  left: calc(var(--s-1) / 2);
  width: calc(var(--s-5) - var(--s-1) - 2 * var(--haar));
  height: calc(var(--s-5) - var(--s-1) - 2 * var(--haar));
  border-radius: var(--r-voll);
  background-color: var(--ink-3);
  transition: transform var(--tr), background-color var(--tr);
}

.tog.on {
  background-color: var(--akz-schwach);
  border-color: var(--akz);
}

.tog.on::after {
  transform: translateX(calc(var(--s-5) - var(--s-1)));
  background-color: var(--akz);
}

.tog:focus-visible {
  outline: calc(var(--haar) * 2) solid var(--akz);
  outline-offset: calc(var(--haar) * 2);
}

/* PN-VOICE-007 Nachzug (24.09.2026): Meldung .toastbox (vorher nur in gsv-ui.css, dort ausgehaengt -> static, ausserhalb Bild) + Schalter-Zustaende. Stil von Qwen (qwen3.8-27b), Selektor-Spezifitaet .tog-err von Opus. Nur Tokens. */
.toastbox {
  position: fixed;
  bottom: var(--s-5);
  left: 50%;
  transform: translateX(-50%);
  z-index: 9999;
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-3) var(--s-5);
  max-width: 92vw;
  font-weight: 600;
  border-left: calc(var(--haar) * 3) solid var(--akz);
  animation: toast-ein calc(var(--t) * 2) var(--ease);
}

.toastbox.ok {
  border-left-color: var(--ok);
}

.toastbox.err {
  border-left-color: var(--danger);
}

@keyframes toast-ein {
  from {
    opacity: 0;
    transform: translate(-50%, var(--s-3));
  }
  to {
    opacity: 1;
    transform: translate(-50%, 0);
  }
}

.tog[aria-busy="true"] {
  cursor: progress;
  opacity: 0.6;
}

.tog[aria-disabled="true"] {
  cursor: not-allowed;
  opacity: 0.5;
}

.tog-row .tl .tog-err {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  flex-wrap: wrap;
  margin-top: var(--s-1);
  color: var(--danger);
  font-size: var(--fs-s);
}

.tog-row .tl .tog-err[hidden] {
  display: none;
}
/* ================================================================ PN-GUI-012 (gui012-20260924)
   Klassen, die games/voice-JS noch setzt, deren Stil aber nur in der ausgehaengten gsv-ui.css stand.
   Nachbau aus Tokens (Qwen war nicht erreichbar, von Opus nach auftrag.md gebaut). Liste: panel-gui012/liste.md */

/* PN-GUI-012 · Wartungsseite */
.gsv-maint {
  position: relative;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: var(--s-5);
  overflow: hidden;
  background: var(--g-0);
}
.gsv-maint-card {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: calc(var(--s-8) * 8);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-6);
  text-align: center;
  background: var(--g-2);
  border: var(--haar) solid var(--line);
  border-radius: var(--r-3);
  box-shadow: var(--sch-2);
}
.gsv-maint-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-1) var(--s-3);
  border-radius: var(--r-voll);
  background: var(--warn-schwach);
  color: var(--warn);
  font-size: var(--fs-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
}
.gsv-maint-spin {
  width: var(--s-4);
  height: var(--s-4);
  border-radius: 50%;
  border: calc(var(--haar) * 2) solid var(--line);
  border-top-color: var(--akz);
  animation: gui012-dreh 1s linear infinite;
}
.gsv-maint-brand { font-size: var(--fs-s); font-weight: 700; color: var(--ink-3); letter-spacing: .04em; }
.gsv-maint-title { margin: 0; font-size: var(--fs-2xl); font-weight: 700; color: var(--ink); }
.gsv-maint-msg { margin: 0; max-width: 42ch; font-size: var(--fs-m); color: var(--ink-2); }
.gsv-maint-foot {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-2);
  margin-top: var(--s-2);
}
.gsv-maint-retry {
  padding: var(--s-3) var(--s-5);
  border: var(--haar) solid var(--akz);
  border-radius: var(--r-2);
  background: var(--akz);
  color: var(--auf-akz);
  font: inherit;
  font-size: var(--fs-m);
  font-weight: 700;
  cursor: pointer;
  transition: background var(--t) var(--ease);
}
.gsv-maint-retry:hover { background: color-mix(in srgb, var(--akz) 86%, var(--ink)); }
.gsv-maint-retry:focus-visible { outline: calc(var(--haar) * 2) solid var(--akz); outline-offset: var(--s-1); }
.gsv-maint-hint { font-size: var(--fs-s); color: var(--ink-3); }

/* PN-GUI-012 · Handy-Blatt "Mehr" */
.deck-more-ov {
  position: fixed;
  inset: 0;
  z-index: 120;
  display: flex;
  align-items: flex-end;
  background: var(--scrim);
  animation: gui012-ein var(--t-lang) var(--ease);
}
.deck-more-sheet {
  width: 100%;
  max-height: 78vh;
  overflow-y: auto;
  background: var(--g-2);
  border-top: var(--haar) solid var(--line);
  border-radius: var(--r-3) var(--r-3) 0 0;
  padding: var(--s-3) var(--s-3) max(var(--s-3), env(safe-area-inset-bottom));
}
.deck-more-h { padding: var(--s-1) var(--s-2) var(--s-2); text-align: center; font-size: var(--fs-m); font-weight: 700; color: var(--ink); }
.deck-more-grp {
  padding: var(--s-3) var(--s-2) var(--s-1);
  font-size: var(--fs-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--ink-3);
}
.deck-more-i {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  width: 100%;
  padding: var(--s-3);
  border: 0;
  border-radius: var(--r-2);
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: var(--fs-m);
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}
.deck-more-i:hover, .deck-more-i.on { background: var(--akz-schwach); }
.deck-more-i.on { color: var(--akz); }
.deck-more-i .dni {
  display: grid;
  place-items: center;
  width: calc(var(--s-5) - var(--s-1));
  height: calc(var(--s-5) - var(--s-1));
  color: var(--akz);
}
.deck-more-i .dni svg { width: calc(var(--s-4) + var(--haar) * 2); height: calc(var(--s-4) + var(--haar) * 2); }

/* PN-GUI-012 · Reiter "Bridge-Rechte" */
.tabbar .tab {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-1);
  font-size: var(--fs-xs);
  font-weight: 650;
  color: var(--ink-2);
  cursor: pointer;
  transition: color var(--t) var(--ease), background var(--t) var(--ease);
}
.tabbar .tab:hover { color: var(--ink); }
.tabbar .tab.on { background: var(--g-3); color: var(--ink); }
.tabbar .tab svg { width: var(--s-4); height: var(--s-4); flex: none; }
.tabbar .tab:focus-visible { outline: calc(var(--haar) * 2) solid var(--akz); outline-offset: var(--s-1); }

/* PN-GUI-012 · Backup-Zeitplan-Hinweis */
.bk-sched-hint {
  display: flex;
  align-items: flex-start;
  gap: var(--s-2);
  margin-top: var(--s-3);
  font-size: var(--fs-s);
  line-height: 1.55;
  color: var(--ink-2);
}
.bk-sched-hint svg { flex: none; width: var(--fs-l); height: var(--fs-l); margin-top: var(--haar); color: var(--akz); }
.bk-sched-flow { flex: 1 1 auto; min-width: 0; }
.bk-sched-flow b { font-weight: 700; color: var(--ink); }

/* PN-GUI-012 · Anmeldung */
.auth-pwrow { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-2); }

/* PN-GUI-012 · Verbindungs-Chip */
.connect-chip .cc-label { font-size: var(--fs-xs); font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-3); }
.connect-chip .cc-addr { font-family: var(--ff-mono); font-size: var(--fs-m); font-weight: 700; color: var(--ink); }
.connect-chip .cc-ico { color: var(--ink-2); }
.connect-chip.copied { border-color: var(--ok); }
.connect-chip.copied .cc-ico { color: var(--ok); }

/* PN-GUI-012 · Kennzahl */
.stat .s-val.grad { color: var(--akz); }

/* PN-GUI-012 · Lade-Skelett */
.deck-loading { padding-top: var(--s-1); }
.sk-head { width: 36%; height: calc(var(--s-5) + var(--s-1)); min-height: 0; margin-bottom: var(--s-4); }
.sk-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-3); margin-bottom: var(--s-4); }
.sk-card {
  height: calc(var(--s-8) + var(--s-6) - var(--s-1));
  border-radius: var(--r-2);
  background: linear-gradient(90deg, var(--g-2) 25%, var(--g-3) 37%, var(--g-2) 63%);
  background-size: 400% 100%;
  animation: schimmer 1.4s linear infinite;
}
.sk-card.sk-tall { height: calc(var(--s-8) * 3 + var(--s-5) + var(--s-1)); }
@media (max-width: 720px) { .sk-grid { grid-template-columns: 1fr; } }

/* PN-GUI-012 · Spieler */
.pl-uuid {
  display: inline-block;
  max-width: 100%;
  font-family: var(--ff-mono);
  font-size: var(--fs-xs);
  font-weight: 500;
  color: var(--ink-2);
  word-break: break-all;
  cursor: pointer;
}
.pl-uuid:hover { color: var(--akz); }
.pl-head-fb {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border-radius: var(--r-2);
  background: var(--akz-schwach);
  border: var(--haar) solid var(--akz);
  color: var(--akz);
  font-weight: 800;
  text-transform: uppercase;
  line-height: 1;
}

@keyframes gui012-dreh { to { transform: rotate(360deg); } }
@keyframes gui012-ein { from { opacity: 0; } to { opacity: 1; } }

/* PN-GUI-014 · Spielerverwaltung (Whitelist · Banns · Verlauf), Vorlage spielerbereich.css, nur Tokens */
.spb-head { flex: 0 0 auto; border-radius: var(--r-1); background: var(--g-4); image-rendering: pixelated; }

.spb-empty { display: flex; flex-direction: column; align-items: center; gap: var(--s-2); padding: var(--s-7) var(--s-4); text-align: center; color: var(--ink-3); }
.spb-empty-ic {
  display: inline-flex; align-items: center; justify-content: center;
  width: calc(var(--s-6) + var(--s-2)); height: calc(var(--s-6) + var(--s-2));
  margin-bottom: var(--s-1);
  border-radius: var(--r-voll); background: var(--g-3); border: var(--haar) solid var(--line); color: var(--ink-3);
}
.spb-empty-ic svg { width: calc(var(--s-4) + var(--s-1)); height: calc(var(--s-4) + var(--s-1)); }
.spb-empty-t { font-size: var(--fs-l); font-weight: 700; color: var(--ink); }
.spb-empty-s { max-width: 46ch; font-size: var(--fs-s); line-height: 1.55; color: var(--ink-2); }

.spb-sk { display: grid; gap: var(--s-2); padding: var(--s-3); }
.spb-sk-row { display: flex; align-items: center; gap: var(--s-3); padding: var(--s-3); border: var(--haar) solid var(--line); border-radius: var(--r-2); }
.spb-sk-av, .spb-sk-line {
  background: linear-gradient(90deg, var(--g-3) 25%, var(--g-4) 37%, var(--g-3) 63%);
  background-size: 400% 100%;
  animation: schimmer 1.4s linear infinite;
}
.spb-sk-av { flex: 0 0 auto; width: calc(var(--s-5) + var(--s-1)); height: calc(var(--s-5) + var(--s-1)); border-radius: var(--r-1); }
.spb-sk-line { flex: 1; height: var(--s-3); border-radius: var(--r-voll); }
.spb-sk-row:nth-child(2) .spb-sk-line { flex: 0 1 70%; }
.spb-sk-row:nth-child(3) .spb-sk-line { flex: 0 1 45%; }

.spb-err {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--s-3);
  margin: var(--s-3); padding: var(--s-3) var(--s-4);
  border: var(--haar) solid color-mix(in srgb, var(--gefahr) 40%, var(--line)); border-radius: var(--r-2);
  background: var(--gefahr-schwach); color: var(--gefahr); font-size: var(--fs-s);
}
.card > .spb-err { margin: 0; }
.spb-err-btn { margin-left: auto; }
.spb-hint { margin-top: var(--s-3); font-size: var(--fs-xs); line-height: 1.5; color: var(--ink-3); }
.spb-mini { margin-left: var(--s-2); flex: 0 0 auto; }

/* Scharf-Karte (Whitelist) */
.spb-scharf { display: flex; flex-direction: row; align-items: center; flex-wrap: wrap; gap: var(--s-3); margin-bottom: var(--s-4); }
.spb-scharf-txt { flex: 1; min-width: calc(var(--s-8) * 3); }
.spb-scharf-t { font-size: var(--fs-m); font-weight: 700; color: var(--ink); }
.spb-scharf-s { margin-top: var(--s-1); font-size: var(--fs-s); color: var(--ink-3); }

/* Hinzufuegen-Karte */
.spb-add { margin-bottom: var(--s-4); }
.spb-add-row { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-top: var(--s-3); }
.spb-add-row .inp { flex: 1; min-width: calc(var(--s-8) * 3); font-family: var(--ff-mono); }
.spb-add-row .inp.spb-grund { flex: 2; font-family: var(--ff); }

/* Listen */
.spb-list-card { margin-bottom: var(--s-4); }
.spb-list-h { display: flex; align-items: center; gap: var(--s-3); padding: var(--s-3) var(--s-4); border-bottom: var(--haar) solid var(--line); }
.card.flush > .spb-list-h > .card-h.spb-list-t { flex: 1; margin: 0; padding: 0; }
.spb-count { font-size: var(--fs-xs); color: var(--ink-3); }
.spb-list-h + div:not(:empty) { display: grid; gap: var(--s-2); padding: var(--s-3); }
.spb-list-h + div > .spb-err { margin: 0; }
.spb-list-h + div > .spb-sk { padding: 0; }
.spb-row-m { flex: 1; min-width: 0; }
.spb-row-n { display: flex; align-items: center; flex-wrap: wrap; row-gap: var(--s-1); font-size: var(--fs-m); font-weight: 700; color: var(--ink); }
.spb-row-u { margin-top: var(--s-1); font-family: var(--ff-mono); font-size: var(--fs-xs); color: var(--ink-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Bann-Zeile (mehrzeilig) */
.spb-ban { align-items: flex-start; }
.spb-ban-meta { margin-top: var(--s-1); font-size: var(--fs-s); line-height: 1.5; color: var(--ink-3); }
.spb-ban-meta strong { color: var(--ink-2); font-weight: 600; }
.spb-ban-grund { margin-top: var(--s-1); font-size: var(--fs-s); color: var(--ink-2); }

/* Verlauf */
.spb-zeitraum { flex: 0 0 auto; }
.spb-filter { display: flex; align-items: center; flex-wrap: wrap; gap: var(--s-2); margin-bottom: var(--s-3); }
.spb-filter .inp { flex: 1; min-width: calc(var(--s-8) * 2 + var(--s-6)); }
.spb-v-karte { margin-bottom: var(--s-2); }
.spb-v-kopf {
  display: flex; align-items: center; gap: var(--s-3); width: 100%;
  padding: var(--s-3) var(--s-4); background: transparent; border: 0; color: inherit; font: inherit; text-align: left;
  cursor: pointer; user-select: none; transition: background var(--t) var(--ease);
}
.spb-v-kopf:hover { background: var(--g-3); }
.spb-v-kopf:focus-visible { outline: var(--haar) solid var(--akz); outline-offset: calc(var(--haar) * -2); }
.spb-v-chev { display: inline-flex; width: var(--s-4); color: var(--ink-3); transition: transform var(--t) var(--ease); }
.spb-v-karte.auf .spb-v-chev { transform: rotate(90deg); }
.spb-v-m { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.spb-v-n { display: flex; align-items: center; flex-wrap: wrap; row-gap: var(--s-1); font-size: var(--fs-m); font-weight: 700; color: var(--ink); }
.spb-v-s { margin-top: var(--s-1); font-size: var(--fs-s); color: var(--ink-3); }
.spb-v-r { flex: 0 0 auto; text-align: right; font-size: var(--fs-s); color: var(--ink-2); }
.spb-v-r b { color: var(--ok); }
.spb-v-tab { overflow-x: auto; border-top: var(--haar) solid var(--line); background: var(--g-1); }
.spb-v-tab .tbl td, .spb-v-tab .tbl th { white-space: nowrap; }
.spb-v-join { color: var(--ok); font-weight: 600; }
.spb-v-leave { color: var(--ink-3); font-weight: 600; }
.spb-v-zeit { font-family: var(--ff-mono); font-size: var(--fs-s); }
@media (max-width: 640px) {
  .spb-v-r, .spb-row-u { display: none; }
}
