/* public/css/style.css
   PANIC PROTOCOL — Retro-Sci-Fi-Konsole.
   Komplettes Styling inkl. Klassen, die effects.js (Agent D) zur Laufzeit
   togglet: #screen-game.fx-shake, body.fx-inverted, #screen-game.fx-distortion,
   #flash-overlay.fx-flash-success/.fx-flash-fail, #order-text.fx-jammed,
   .control--electrical/.control--zap/.control--damaged/.control--disabled,
   .control--loose (pro Control, loosePanel-Malfunction), .slime-blob.
   Agent D injiziert KEIN eigenes <style> — alles CSS lebt hier.
*/

/* Skins: je Skin eine eigene Datei (Host-Auswahl via data-skin auf <html>,
   s. shared/skins.js). @import muss vor allen Regeln stehen. */
@import url("skins/neon.css");
@import url("skins/battlestar.css");
@import url("skins/saga.css");
@import url("skins/lcars.css");
@import url("skins/odyssey.css");
@import url("skins/nostromo.css");
@import url("skins/cassette.css");


/* ---------------------------------------------------------------------- */
/* Tokens                                                                  */
/* ---------------------------------------------------------------------- */
:root {
  --bg: #0a0e14;
  --bg-deep: #05070b;
  --panel-bg: #111823;
  --panel-bg-2: #0c121b;
  --border: #1c2836;
  --border-bright: #2a3d52;

  --cyan: #2de2e6;
  --cyan-dim: #155b5c;
  --cyan-glow: rgba(45, 226, 230, 0.55);

  --amber: #ffb100;
  --amber-dim: #7a5200;
  --amber-glow: rgba(255, 177, 0, 0.5);

  --magenta: #ff3ec8;
  --magenta-dim: #7a1a63;
  --magenta-glow: rgba(255, 62, 200, 0.5);

  --green: #35ff8f;
  --green-glow: rgba(53, 255, 143, 0.55);
  --red: #ff3b3b;
  --red-glow: rgba(255, 59, 59, 0.55);

  --text: #d8f4ff;
  --text-dim: #7488a0;
  --text-faint: #3f5266;

  --font-mono: ui-monospace, "SF Mono", "Cascadia Code", "Roboto Mono", Menlo,
    Consolas, "Liberation Mono", monospace;

  --radius: 10px;
  --touch-min: 44px;

  /* -- Skin-Tokens ------------------------------------------------------- */
  /* Alles, was frueher hart in den Regeln stand und sich pro Skin aendert
     (s. Skin-Bloecke am Dateiende, gesetzt ueber data-skin auf <html>). */
  --active-text: #fff; /* Schrift auf aktiven Tasten/Labels */
  --critical-deep: #6b0000; /* dunkles Ende der Kritisch-Gradients */
  --lever-grad: linear-gradient(180deg, #d8dee8, #8b94a3); /* Switch-Hebel */
  --thumb-border: #fff; /* Rand des Slider-Daumens */
  --fire-grad: radial-gradient(circle at 40% 35%, #4a0033, var(--bg-deep));
  --keypad-active-bg: rgba(255, 176, 0, 0.18);
  --nav-active-bg: rgba(0, 255, 140, 0.15);
  --btn-radius: 8px; /* nummerierte Taster */
  --key-radius: 6px; /* Keypad/Text/Nav/Switch-Track */
  --control-texture: none; /* Material der Kacheln (Skins: Metall/Grunge) */
  /* CRT-Overlay ueber dem ganzen Screen (body::before) */
  --screen-overlay: repeating-linear-gradient(
      to bottom,
      rgba(255, 255, 255, 0.025) 0px,
      rgba(255, 255, 255, 0.025) 1px,
      transparent 1px,
      transparent 3px
    ),
    radial-gradient(ellipse at 50% 0%, rgba(45, 226, 230, 0.05), transparent 60%);
}

/* ---------------------------------------------------------------------- */
/* Reset & base                                                            */
/* ---------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
  padding: 0;
  overflow-x: hidden;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-mono);
  -webkit-tap-highlight-color: transparent;
  transition: transform 0.6s ease;
  transform: none;
  transform-origin: 50% 50%;
  min-height: 100vh;
  position: relative;
}

/* CRT scanline texture — subtil, dezent */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 9000;
  background-image: var(--screen-overlay);
  mix-blend-mode: overlay;
}

/* Autoren-Regeln mit explizitem display (z.B. .lobby-room{display:flex}) würden das
   [hidden]-Attribut sonst überstimmen (Autor schlägt User-Agent-Default immer,
   unabhängig von Spezifität) - deshalb hier robust erzwingen. */
[hidden] { display: none !important; }

button, input {
  font-family: inherit;
  color: inherit;
}

a { color: var(--cyan); }

h1, h2, h3, p { margin: 0; }

/* ---------------------------------------------------------------------- */
/* Header (immer sichtbar)                                                 */
/* ---------------------------------------------------------------------- */
.app-header {
  position: sticky;
  top: 0;
  z-index: 500;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.5rem 0.9rem;
  background: linear-gradient(to bottom, var(--bg-deep), var(--bg));
  border-bottom: 1px solid var(--border-bright);
  box-shadow: 0 0 18px rgba(45, 226, 230, 0.08);
}

.app-header__title {
  font-size: 0.85rem;
  letter-spacing: 0.25em;
  color: var(--cyan);
  text-shadow: 0 0 8px var(--cyan-glow);
  white-space: nowrap;
}

.app-header__controls {
  position: relative; /* Anker für das Audio-Overlay */
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

/* Audio-Overlay: hängt unter dem Toggle, öffnet/schließt per [hidden] */
.audio-panel {
  position: absolute;
  top: calc(100% + 0.5rem);
  right: 0;
  z-index: 600;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  min-width: 200px;
  padding: 0.75rem;
  background: var(--panel-bg);
  border: 1px solid var(--border-bright);
  border-radius: 8px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.6), 0 0 14px var(--cyan-glow);
}
.audio-panel[hidden] { display: none; }

.audio-panel__row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.audio-panel__label {
  flex: 0 0 3.2rem;
  font-size: 0.65rem;
  letter-spacing: 0.15em;
  color: var(--text-faint);
}

.audio-panel__mute {
  min-height: var(--touch-min);
  padding: 0.35rem 0.6rem;
  font-size: 0.7rem;
  letter-spacing: 0.15em;
  color: var(--cyan);
  background: transparent;
  border: 1px solid var(--border-bright);
  border-radius: 4px;
  cursor: pointer;
}
.audio-panel__mute[aria-pressed="false"] {
  color: var(--text-faint);
  border-color: var(--border);
}

.volume-slider {
  flex: 1;
  height: 22px;
  min-width: 0;
  accent-color: var(--cyan);
  cursor: pointer;
}

.audio-toggle {
  min-width: var(--touch-min);
  min-height: var(--touch-min);
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 2px solid var(--border-bright);
  background: var(--panel-bg);
  color: var(--cyan);
  font-size: 1.2rem;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: box-shadow 0.15s ease, border-color 0.15s ease, transform 0.1s ease;
}
.audio-toggle:hover { border-color: var(--cyan); }
.audio-toggle:active { transform: scale(0.92); }
.audio-toggle[aria-expanded="true"] {
  box-shadow: 0 0 12px var(--cyan-glow);
  border-color: var(--cyan);
}
.audio-toggle.is-muted {
  color: var(--text-faint);
  border-color: var(--border);
}

/* ---------------------------------------------------------------------- */
/* Screens                                                                 */
/* ---------------------------------------------------------------------- */
#app {
  max-width: 960px;
  margin: 0 auto;
  padding: 0.75rem;
  padding-bottom: 3rem;
}

.screen {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  min-height: calc(100vh - 60px);
}

/* ---------------------------------------------------------------------- */
/* Lobby                                                                   */
/* ---------------------------------------------------------------------- */
.lobby-intro {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.5rem;
  text-align: center;
  padding-top: 2rem;
}

.app-title {
  font-size: clamp(2rem, 9vw, 3.2rem);
  letter-spacing: 0.15em;
  color: var(--cyan);
  text-shadow: 0 0 12px var(--cyan-glow), 0 0 40px var(--cyan-glow);
  animation: titleFlicker 6s infinite;
}

@keyframes titleFlicker {
  0%, 93%, 100% { opacity: 1; }
  94% { opacity: 0.82; }
  95% { opacity: 1; }
  96% { opacity: 0.88; }
}

.app-subtitle {
  color: var(--text-dim);
  font-size: 0.95rem;
  max-width: 32ch;
  line-height: 1.5;
}

.join-form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  width: 100%;
  max-width: 360px;
}

.field {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  text-align: left;
}

.field__label {
  font-size: 0.75rem;
  letter-spacing: 0.2em;
  color: var(--amber);
}

.field input[type="text"] {
  min-height: var(--touch-min);
  padding: 0.6rem 0.8rem;
  background: var(--panel-bg-2);
  border: 2px solid var(--border-bright);
  border-radius: var(--radius);
  color: var(--text);
  font-size: 1.1rem;
  letter-spacing: 0.08em;
  outline: none;
  text-transform: none;
}
.field input[type="text"]:focus {
  border-color: var(--cyan);
  box-shadow: 0 0 10px var(--cyan-glow);
}
/* -- Sprachauswahl (Startseite + Lobby-Host) -- */
.lang-select {
  min-height: var(--touch-min);
  padding: 0.6rem 0.8rem;
  background: var(--panel-bg-2);
  border: 2px solid var(--border-bright);
  border-radius: var(--radius);
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 0.95rem;
  letter-spacing: 0.08em;
  outline: none;
  cursor: pointer;
}
.lang-select:focus {
  border-color: var(--cyan);
  box-shadow: 0 0 10px var(--cyan-glow);
}

.field__hint {
  margin: 0;
  color: var(--text-dim);
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  text-align: left;
}

/* Sprachumschaltung in der Raum-Lobby: sieht nur der Host (main.js/ui.js). */
.lobby-lang {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  width: 100%;
  max-width: 360px;
}

.join-form__actions {
  display: flex;
  gap: 0.75rem;
  flex-wrap: wrap;
}

.btn {
  flex: 1 1 auto;
  min-height: var(--touch-min);
  min-width: 44px;
  padding: 0.7rem 1rem;
  border-radius: var(--radius);
  border: 2px solid var(--border-bright);
  background: var(--panel-bg);
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 0.95rem;
  letter-spacing: 0.1em;
  cursor: pointer;
  transition: transform 0.08s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}
.btn:active { transform: scale(0.96); }
.btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
  filter: grayscale(0.6);
}

.btn--primary { border-color: var(--cyan); color: var(--cyan); }
.btn--primary:hover:not(:disabled) { box-shadow: 0 0 14px var(--cyan-glow); }

.btn--secondary { border-color: var(--magenta); color: var(--magenta); }
.btn--secondary:hover:not(:disabled) { box-shadow: 0 0 14px var(--magenta-glow); }

.btn--ready { border-color: var(--amber); color: var(--amber); }
.btn--ready.is-active {
  background: var(--amber-dim);
  color: #fff6e0;
  box-shadow: 0 0 16px var(--amber-glow);
}

.btn--start { border-color: var(--green); color: var(--green); }
.btn--start:hover:not(:disabled) { box-shadow: 0 0 14px var(--green-glow); }

.lobby-error {
  color: var(--red);
  font-size: 0.85rem;
  min-height: 1.2em;
}

/* -- Local-Modus-Hinweis (ersetzt Offene-Spiele-Liste bei ?local=1) -- */
.local-mode-notice {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
  width: 100%;
  max-width: 360px;
  padding: 1rem;
  border: 2px dashed var(--border-bright);
  border-radius: var(--radius);
}
.local-mode-notice__text {
  color: var(--text-dim);
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  text-align: center;
}

/* -- Offene Spiele (Lobby-Browser) -- */
.open-games {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  width: 100%;
  max-width: 420px;
  text-align: left;
}
.open-games__title {
  font-size: 0.75rem;
  letter-spacing: 0.2em;
  color: var(--amber);
  text-align: center;
}
.open-games__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}
.open-games__item {
  width: 100%;
  min-height: var(--touch-min);
  padding: 0.7rem 1rem;
  border-radius: var(--radius);
  border: 2px solid var(--cyan-dim);
  background: var(--panel-bg);
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 0.85rem;
  letter-spacing: 0.04em;
  text-align: center;
  cursor: pointer;
  transition: box-shadow 0.15s ease, border-color 0.15s ease, transform 0.08s ease;
}
.open-games__item:hover {
  border-color: var(--cyan);
  box-shadow: 0 0 14px var(--cyan-glow);
}
.open-games__item:active { transform: scale(0.98); }
/* Sprachkuerzel des Raums am Eintrag (DE/EN) - beim Beitritt gilt diese Sprache. */
.open-games__lang {
  margin-left: 0.5rem;
  padding: 0.1rem 0.35rem;
  border: 1px solid var(--cyan-dim);
  border-radius: 4px;
  color: var(--cyan);
  font-size: 0.7rem;
  letter-spacing: 0.1em;
}
/* L3: volle Räume (n/max mit n>=max) werden von main.js als disabled
   gerendert statt klickbar - defensiv, auch falls der Server sie künftig
   schon vorher aus der Liste filtert. */
.open-games__item:disabled,
.open-games__item.open-games__item--full {
  opacity: 0.45;
  cursor: not-allowed;
  filter: grayscale(0.6);
  border-color: var(--border);
}
.open-games__item:disabled:hover,
.open-games__item.open-games__item--full:hover {
  border-color: var(--border);
  box-shadow: none;
}
.open-games__empty {
  text-align: center;
  color: var(--text-dim);
  font-size: 0.85rem;
  letter-spacing: 0.05em;
  padding: 0.5rem;
}

.lobby-room {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.25rem;
  padding-top: 1.5rem;
}

/* Klein & unauffällig (nur zum Weitergeben eines Direktlinks) - bewusst kein
   zentrales/großes Element mehr, seit der Lobby-Browser (offene Spiele) den
   Raum-Code als primären Einstiegsweg abgelöst hat. */
.room-code-display {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  padding: 0.3rem 0.7rem;
  border: 1px solid var(--border-bright);
  border-radius: 999px;
  background: var(--panel-bg);
}
.room-code-display__label {
  font-size: 0.6rem;
  letter-spacing: 0.15em;
  color: var(--text-dim);
}
.room-code-display__value {
  font-size: 0.95rem;
  letter-spacing: 0.2em;
  color: var(--cyan);
}

.player-list {
  list-style: none;
  margin: 0;
  padding: 0;
  width: 100%;
  max-width: 420px;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.player-list__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.6rem 0.9rem;
  background: var(--panel-bg);
  border: 1px solid var(--border-bright);
  border-radius: var(--radius);
  min-height: var(--touch-min);
}
.player-list__item--disconnected { opacity: 0.45; }

.player-list__name {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.95rem;
}

.player-list__badge {
  font-size: 0.65rem;
  letter-spacing: 0.1em;
  color: var(--amber);
  border: 1px solid var(--amber-dim);
  border-radius: 4px;
  padding: 0.1rem 0.35rem;
}

.player-list__ready {
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  padding: 0.2rem 0.55rem;
  border-radius: 999px;
  border: 1px solid var(--border-bright);
  color: var(--text-dim);
}
.player-list__ready--yes {
  color: var(--green);
  border-color: var(--green);
  box-shadow: 0 0 8px var(--green-glow);
}

.lobby-room__actions {
  display: flex;
  gap: 0.75rem;
  width: 100%;
  max-width: 420px;
}

/* ---------------------------------------------------------------------- */
/* HUD                                                                     */
/* ---------------------------------------------------------------------- */
.hud {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  padding: 0.75rem 1rem;
  background: var(--panel-bg);
  border: 1px solid var(--border-bright);
  border-radius: var(--radius);
}

.hud__integrity {
  flex: 1 1 220px;
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

.hud__integrity-label {
  font-size: 0.65rem;
  letter-spacing: 0.15em;
  color: var(--text-dim);
  white-space: nowrap;
}

.hud__bar-track {
  flex: 1 1 auto;
  height: 20px;
  background: var(--panel-bg-2);
  border: 1px solid var(--border-bright);
  border-radius: 999px;
  overflow: hidden;
}

.hud__bar-fill {
  height: 100%;
  width: 100%;
  background: linear-gradient(90deg, var(--cyan-dim), var(--cyan));
  box-shadow: 0 0 10px var(--cyan-glow);
  transition: width 0.3s ease, background 0.3s ease;
}
.hud__bar-fill.integrity--warning {
  background: linear-gradient(90deg, var(--amber-dim), var(--amber));
  box-shadow: 0 0 10px var(--amber-glow);
}
.hud__bar-fill.integrity--critical {
  background: linear-gradient(90deg, var(--critical-deep), var(--red));
  box-shadow: 0 0 12px var(--red-glow);
  animation: criticalPulse 1s infinite;
}

@keyframes criticalPulse {
  0%, 100% { filter: brightness(1); }
  50% { filter: brightness(1.4); }
}

.hud__integrity-text {
  font-size: 0.9rem;
  color: var(--text);
  min-width: 3.5ch;
  text-align: right;
}

.hud__stats {
  display: flex;
  gap: 1rem;
  font-size: 0.85rem;
  color: var(--text-dim);
  letter-spacing: 0.05em;
}
.hud__stat strong { color: var(--amber); font-size: 1rem; }

/* ---------------------------------------------------------------------- */
/* Order box                                                               */
/* ---------------------------------------------------------------------- */
.order {
  padding: 1rem;
  background: var(--panel-bg);
  border: 2px solid var(--cyan-dim);
  border-radius: var(--radius);
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  min-height: 90px;
  position: relative;
}

.order--fresh { border-color: var(--cyan); }
.order--urgent {
  border-color: var(--red);
  box-shadow: 0 0 18px var(--red-glow);
  animation: orderUrgent 0.6s infinite;
}
@keyframes orderUrgent {
  0%, 100% { box-shadow: 0 0 8px var(--red-glow); }
  50% { box-shadow: 0 0 22px var(--red-glow); }
}

.order__status {
  align-self: flex-start;
  font-size: 0.8rem;
  line-height: 1.1;
  letter-spacing: 0.15em;
  padding: 0.2rem 0.6rem;
  border-radius: 999px;
  border: 1px solid var(--border-bright);
  /* Immer im Layout vorhanden (reservierte Höhe) - erscheint nur über
     visibility/opacity, nie über display/[hidden]. Verhindert Layout-Shift
     im Order-Bereich bei CONFIRMED/VERFEHLT (siehe .order__status--visible).
     Reservierte Höhe = tatsächliche Content-Box (line-height + vertikales
     Padding + Border), sonst reserviert min-height:1.5em (≈19.2px) zu wenig
     gegenüber dem real ~23px hohen gefüllten Badge und die Order-Box springt
     beim ersten CONFIRMED/VERFEHLT um ein paar Pixel. */
  min-height: calc(1.1em + 0.4rem + 2px);
  box-sizing: border-box;
  visibility: hidden;
  opacity: 0;
  transition: opacity 0.15s ease;
}
.order__status--visible {
  visibility: visible;
  opacity: 1;
}
.order__status--complete {
  color: var(--green);
  border-color: var(--green);
  box-shadow: 0 0 10px var(--green-glow);
}
.order__status--failed {
  color: var(--red);
  border-color: var(--red);
  box-shadow: 0 0 10px var(--red-glow);
}

.order__symbol {
  align-self: center;
  font-size: clamp(2.4rem, 10vw, 3.6rem);
  color: var(--magenta);
  text-shadow: 0 0 14px var(--magenta-glow);
  line-height: 1;
  /* Reservierte Höhe wie .order__status - symbolische Instructions sind nur
     ab Level 5 und nicht garantiert, dürfen aber beim Auftauchen/Verschwinden
     keinen Layout-Shift auslösen. */
  height: 1em;
  visibility: hidden;
}
.order__symbol--visible {
  visibility: visible;
}

.order__text {
  font-size: clamp(1.1rem, 4.5vw, 1.6rem);
  font-weight: 700;
  letter-spacing: 0.03em;
  color: var(--text);
  line-height: 1.3;
  /* Reserviert exakt Platz für 2 Zeilen, unabhängig davon ob der Text 1 oder
     2 Zeilen braucht - verhindert Layout-Shift im Order-Bereich beim
     Zeilenumbruch. Längere Texte werden abgeschnitten statt zu wachsen. */
  height: calc(1.3em * 2);
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
.order__text.fx-jammed {
  color: var(--red);
  text-shadow: 1px 0 var(--cyan), -1px 0 var(--magenta);
  animation: jamGlitch 0.25s infinite;
}
@keyframes jamGlitch {
  0%, 100% { transform: translate(0, 0); }
  25% { transform: translate(1px, 0); }
  50% { transform: translate(-1px, 0); }
  75% { transform: translate(0, 1px); }
}

.order__progress-track {
  height: 10px;
  border-radius: 999px;
  background: var(--panel-bg-2);
  border: 1px solid var(--border-bright);
  overflow: hidden;
}
.order__progress-fill {
  height: 100%;
  width: 100%;
  background: linear-gradient(90deg, var(--cyan-dim), var(--cyan));
}
.order--urgent .order__progress-fill {
  background: linear-gradient(90deg, var(--critical-deep), var(--red));
}

/* ---------------------------------------------------------------------- */
/* Panel grid & controls                                                   */
/* ---------------------------------------------------------------------- */
.panel {
  display: grid;
  /* Zeilen-/Spaltenzahl UND die Position jedes Controls (grid-column/grid-row mit
     span) kommen als Inline-Style aus dem Server-Layout (shared/board.js,
     Board-Generator); ui.js setzt sie nur durch. Die Werte hier greifen
     ausschliesslich vor dem ersten Render. */
  grid-template-columns: repeat(4, minmax(0, 1fr));
  grid-template-rows: repeat(4, minmax(0, 1fr));
  /* Das Board ist ein QUADRATISCHES Raster - mit aspect-ratio bleibt jede Zelle
     auf jeder Breite quadratisch, ohne Breakpoint-Sonderlayout. --panel-max
     bindet die Kantenlaenge zusaetzlich an die Viewporthoehe, damit der
     Game-Screen nie scrollen muss. */
  aspect-ratio: 1 / 1;
  width: 100%;
  /* --panel-max deckelt die Kantenlaenge des Quadrats. Auf dem Desktop hat #app
     keine gebundene Hoehe (die Seite darf scrollen), max-height: 100% greift dort
     also nicht - der calc()-Term haelt stattdessen Platz fuer Header + HUD +
     Order-Box frei, damit der Game-Screen auch dort ohne Scrollen auskommt. */
  --panel-max: min(58vh, calc(100vh - 25rem));
  max-width: min(100%, var(--panel-max));
  /* Reisleine: reicht die Resthoehe trotz --panel-max nicht, gewinnt max-height
     gegen aspect-ratio - die Kacheln werden dann leicht flach statt dass das
     Panel aus dem Screen laeuft. */
  max-height: 100%;
  /* .screen ist ein Column-Flex-Container: align-self zentriert waagerecht,
     flex-grow: 0 verhindert, dass das Panel senkrecht gestreckt wird (das wuerde
     aspect-ratio aushebeln). */
  flex: 0 1 auto;
  align-self: center;
  /* margin: auto schluckt den Freiraum im Column-Flex und zentriert das Quadrat
     senkrecht - ein quadratisches Board kann einen hohen Handy-Screen nie
     ausfuellen, mittig sieht das gewollt aus statt oben angeklebt. */
  margin: auto;
  gap: 0.6rem;
  transition: transform 0.2s ease, opacity 0.2s ease;
}

.panel.panel--locked {
  pointer-events: none;
  opacity: 0.45;
  filter: grayscale(0.4);
}

.control {
  background: var(--panel-bg);
  background-image: var(--control-texture);
  border: 1px solid var(--border-bright);
  border-radius: var(--radius);
  padding: 0.65rem 0.5rem 0.85rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.4rem;
  /* Die Kachelgroesse gibt das Grid vor (1-3 Zellen breit/hoch) - das Control
     darf deshalb keine eigene Mindesthoehe erzwingen, sonst sprengt eine 1x1-
     Kachel das Raster. */
  min-height: 0;
  min-width: 0;
  /* Label + Instrument als zusammenhaengende Gruppe mittig statt an die
     Kachelraender auseinandergezogen: die Kacheln sind jetzt 1-3 Zellen hoch,
     space-between wuerde bei einer 3 Zellen hohen Kachel ein winziges Toggle
     ganz nach unten schieben. */
  justify-content: center;
  position: relative;
  overflow: hidden;
  /* Container fuer die cq-Einheiten der Instrumente: cqi (Label-Schriftgroesse)
     und cqmin (quadratischer PushButton). `size` statt `inline-size`, weil cqmin
     auch die Kachelhoehe braucht - die Kachel bekommt ihre Groesse vom Grid, haengt
     also nicht vom Inhalt ab (Voraussetzung fuer size containment). */
  container-type: size;
}

.control__label {
  /* 7cqi = 7% der Kachelbreite, gedeckelt auf 0.5-0.72rem. Browser ohne
     Container-Query-Einheiten ignorieren nur den mittleren Term und landen auf
     dem Maximum - also dem bisherigen Wert. */
  font-size: clamp(0.5rem, 7cqi, 0.72rem);
  letter-spacing: 0.04em;
  text-align: center;
  color: var(--text-dim);
  line-height: 1.2;
  /* Lange Komposita (IONENKOMPENSATOR) muessen in schmale Kacheln passen:
     erst Silbentrennung versuchen (lang-Attribut auf <html>), harter
     Wortumbruch nur als letzte Reisleine. */
  hyphens: auto;
  overflow-wrap: break-word;
  word-break: break-word;
  /* Hoechstens 3 Zeilen, danach Ellipse - sonst draengt ein sehr langer Name das
     Instrument aus der Kachel. */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  flex: 0 0 auto;
}

.control__symbol {
  font-size: 1.6rem;
  color: var(--magenta);
  text-shadow: 0 0 8px var(--magenta-glow);
}

.control--disabled {
  opacity: 0.4;
  filter: grayscale(0.7);
}
.control--disabled * { cursor: not-allowed !important; }

.control--damaged {
  border-color: var(--amber-dim);
  background-image: repeating-linear-gradient(
    135deg,
    rgba(255, 177, 0, 0.06) 0px,
    rgba(255, 177, 0, 0.06) 6px,
    transparent 6px,
    transparent 14px
  );
  animation: damagedFlicker 2.4s infinite;
}
@keyframes damagedFlicker {
  0%, 88%, 100% { filter: none; }
  90% { filter: brightness(0.7); }
  92% { filter: brightness(1.1); }
}

.control--electrical {
  border-color: var(--amber);
  box-shadow: 0 0 10px var(--amber-glow);
  animation: arcFlicker 0.5s infinite;
}
@keyframes arcFlicker {
  0%, 100% { box-shadow: 0 0 6px var(--amber-glow); border-color: var(--amber); }
  45% { box-shadow: 0 0 18px var(--amber-glow); border-color: #fff3cf; }
  55% { box-shadow: 0 0 2px var(--amber-glow); border-color: var(--amber-dim); }
}

.control--zap {
  animation: zapFlash 0.3s ease-out;
}
@keyframes zapFlash {
  0% { box-shadow: 0 0 30px 6px #fff, 0 0 60px 10px var(--amber-glow); }
  100% { box-shadow: 0 0 6px var(--amber-glow); }
}

/* -- toggle -- */
.toggle {
  min-width: 0;
  min-height: var(--touch-min);
  max-width: 100%;
  border: none;
  background: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  cursor: pointer;
  color: var(--text);
}
.toggle__track {
  width: 64px;
  max-width: 100%;
  height: 32px;
  border-radius: 999px;
  background: var(--panel-bg-2);
  border: 2px solid var(--border-bright);
  position: relative;
  display: block;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.toggle__thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--text-faint);
  transition: transform 0.15s ease, background 0.15s ease, box-shadow 0.15s ease;
}
.toggle[aria-checked="true"] .toggle__track {
  border-color: var(--green);
  box-shadow: 0 0 12px var(--green-glow);
}
.toggle[aria-checked="true"] .toggle__thumb {
  transform: translateX(32px);
  background: var(--green);
  box-shadow: 0 0 8px var(--green-glow);
}
.toggle__state {
  font-size: 0.75rem;
  letter-spacing: 0.15em;
  color: var(--text-dim);
}
.toggle[aria-checked="true"] .toggle__state { color: var(--green); }

/* -- switch (SwitchH / SwitchV, 2 Stellungen) -- */
/* Der Switch ist ein Kipphebel, kein Optionsfeld: EIN Klickbereich, in dem
   der Hebel je nach control.orientation waagerecht oder senkrecht umspringt.
   .control--h / .control--v kommen aus ui.js (buildControlElement). */
.switch {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  width: 100%;
  flex: 1;
  min-height: 0;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  color: inherit;
}
.switch__track {
  position: relative;
  border: 2px solid var(--border-bright);
  border-radius: var(--key-radius);
  background: var(--panel-bg-2);
  box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.6);
  flex: 0 0 auto;
}
.switch__lever {
  position: absolute;
  border-radius: 4px;
  background: var(--lever-grad);
  box-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
  transition: transform 0.12s ease;
}
/* horizontal: Hebel wandert nach rechts */
.control--h .switch__track { width: min(100%, 74px); height: 34px; }
.control--h .switch__lever { top: 3px; left: 3px; width: 30px; height: 24px; }
.control--h .switch--on .switch__lever { transform: translateX(36px); }
/* vertikal: Hebel wandert nach oben (AN ist oben) */
.control--v .switch__track { width: 38px; height: min(100%, 78px); }
.control--v .switch__lever { left: 3px; bottom: 3px; width: 28px; height: 30px; }
.control--v .switch--on .switch__lever { transform: translateY(-38px); }
.switch--on .switch__track {
  border-color: var(--cyan);
  box-shadow: 0 0 10px var(--cyan-glow), inset 0 0 10px rgba(0, 0, 0, 0.6);
}
.switch__state {
  font-size: 0.72rem;
  letter-spacing: 0.15em;
  color: var(--text-dim);
}
.switch--on .switch__state { color: var(--green); }

/* Kurze Kacheln (1 Zelle hoch): Label + Track + Statuszeile passen sonst nicht
   uebereinander - Track und Texte gehen eine Stufe runter, das Label auf max.
   2 Zeilen. Verhindert die Ueberlappung von Label und Hebel, die vorher in
   1x-hohen SwitchH-/ToggleButton-Kacheln auftrat (alle Skins). */
@container (max-height: 110px) {
  .control { gap: 0.25rem; }
  .control__label { -webkit-line-clamp: 2; }
  .control--h .switch__track { height: 26px; }
  .control--h .switch__lever { width: 24px; height: 18px; }
  .control--h .switch--on .switch__lever { transform: translateX(28px); }
  .control--v .switch__track { height: min(100%, 60px); }
  .control--v .switch--on .switch__lever { transform: translateY(-20px); }
  .switch__state, .toggle__state { font-size: 0.58rem; letter-spacing: 0.08em; }
  .toggle__track { width: 48px; height: 24px; }
  .toggle__thumb { width: 16px; height: 16px; }
  .toggle[aria-checked="true"] .toggle__thumb { transform: translateX(22px); }
}

/* -- slider -- */
/* Orientierung wird pro Control deterministisch aus der controlId gewählt
   (siehe ui.js buildSlider) - horizontal bleibt wie bisher, vertikal nutzt
   .slider--vertical (braucht eine mind. 2 Zellen hohe Kachel - der
   Board-Generator setzt SliderV nur in solche, s. dims in controlLibrary.js). */
.slider {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
}
.slider--vertical {
  flex: 1;
  justify-content: flex-start;
}
.slider__row {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  flex: 1;
  min-width: 0;
  max-width: 100%;
}
.slider__track {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
}
.slider__value {
  font-size: 1.3rem;
  color: var(--cyan);
  text-shadow: 0 0 6px var(--cyan-glow);
  font-weight: 700;
}
.slider__input {
  width: 100%;
  height: var(--touch-min);
  -webkit-appearance: none;
  appearance: none;
  background: transparent;
  cursor: pointer;
}
/* Kein natives vertikales <input type="range"> browserübergreifend verfügbar -
   die rotate(-90deg)-Technik ist die robusteste Lösung ohne Custom-Widget:
   der Input behält seine horizontale Box (110x44, damit Flex ihn im
   .slider__track sauber zentriert) und wird nur visuell gedreht dargestellt.
   Ergebnis: min unten, max oben (wie ein Lautstärkeregler). */
.slider--vertical .slider__input {
  /* Absolut positioniert + zentriert gedreht: ein rotiertes Element belegt im
     Layout weiter seine UNgedrehte Box (110px breit) - frei fliessend sprengt es
     damit jede schmale Kachel. So bestimmt der Track die Layoutbreite (44px =
     die sichtbare Breite nach der Drehung), der Input liegt nur noch darin. */
  position: absolute;
  left: 50%;
  top: 50%;
  width: 120px;
  height: var(--touch-min);
  transform: translate(-50%, -50%) rotate(-90deg);
}
.slider__input::-webkit-slider-runnable-track {
  height: 8px;
  border-radius: 999px;
  background: var(--panel-bg-2);
  border: 1px solid var(--border-bright);
}
.slider__input::-moz-range-track {
  height: 8px;
  border-radius: 999px;
  background: var(--panel-bg-2);
  border: 1px solid var(--border-bright);
}
.slider__input::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 26px;
  height: 26px;
  margin-top: -9px;
  border-radius: 50%;
  background: var(--cyan);
  box-shadow: 0 0 10px var(--cyan-glow);
  border: 2px solid var(--thumb-border);
}
.slider__input::-moz-range-thumb {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--cyan);
  box-shadow: 0 0 10px var(--cyan-glow);
  border: 2px solid var(--thumb-border);
}
.slider__ticks {
  width: 100%;
  display: flex;
  justify-content: space-between;
  font-size: 0.65rem;
  color: var(--text-dim);
  padding: 0 2px;
}
/* column-reverse: DOM-Reihenfolge bleibt min→max (wie beim horizontalen
   Slider), visuell landet min unten / max oben - passend zur rotate(-90deg)-
   Ausrichtung des vertikalen Inputs. */
.slider--vertical .slider__track {
  position: relative;
  height: 120px;
  width: var(--touch-min);
  flex: 0 0 auto;
}
.slider__ticks--vertical {
  flex-direction: column-reverse;
  justify-content: space-between;
  align-items: center;
  width: auto;
  height: 120px;
  padding: 4px 0;
}
/* Wertanzeige sitzt beim vertikalen Slider in der Zeile (s. ui.js buildSlider). */
.slider--vertical .slider__value { font-size: 1rem; flex: 0 0 auto; }

/* -- dial -- */
/* Redesign: die Positions-Zahlen liegen als eigene klickbare Buttons AUSSEN
   um den Knopf (siehe ui.js buildDial) - der Knopf selbst (.dial) ist nur
   noch eine Zeiger-Anzeige, nicht mehr interaktiv (kein Klick-zum-
   Durchschalten mehr). --label-radius bestimmt den Abstand der Zahlen vom
   Zentrum und wird in der 400px-Media-Query verkleinert, ohne die Formel
   der Zeiger-/Label-Ausrichtung anzufassen. */
.dial-wrap {
  /* Der Dial sitzt laut Bibliothek immer in einer mind. 2x2 Zellen großen
     Kachel; er muss dort ohne Clipping (.control{overflow:hidden}) hineinpassen.
     Wrap-Größe = 2*(--label-radius + halbe .dial__label-Breite), damit die auf
     --label-radius sitzenden 44x44px-Labels (Touch-Minimum, siehe
     .dial__label) am Rand nicht abgeschnitten werden: 2*(52+22) = 148px. */
  --label-radius: 52px;
  /* Kantenlaenge der Positions-Buttons (= deren Hit-Area). Als Variable, damit
     die Mobile-Media-Query Radius UND Buttongroesse gemeinsam verkleinern kann,
     ohne die Ausrichtungsformel in .dial__label anzufassen. */
  --dial-label-size: var(--touch-min);
  position: relative;
  width: 148px;
  height: 148px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.dial {
  --dial-angle: 0deg;
  width: 64px;
  height: 64px;
  border-radius: 50%;
  border: 3px solid var(--border-bright);
  background: radial-gradient(circle at 50% 40%, var(--panel-bg-2), var(--bg-deep));
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}
.dial__pointer {
  /* Rein relativ zur eigenen (.dial) Box - kein Pixel-Offset nötig, bleibt
     in jeder Dial-Größe korrekt zentriert (siehe frühere Media-Query-Bugs
     mit festen px-Werten). */
  position: absolute;
  bottom: 50%;
  left: 50%;
  width: 4px;
  height: 42%;
  margin-left: -2px;
  background: var(--cyan);
  box-shadow: 0 0 8px var(--cyan-glow);
  border-radius: 2px;
  /* Nadel liegt OBERHALB des Zentrums, Drehpunkt ist ihr unteres Ende (=
     Zentrum) - so ist 0deg oben, passend zu .dial__label (0deg = oben,
     im Uhrzeigersinn). Mit top/transform-origin:50% 0% zeigte sie nach
     unten und stand damit exakt 180deg falsch. */
  transform-origin: 50% 100%;
  transform: rotate(var(--dial-angle));
  transition: transform 0.18s ease;
}
.dial__value {
  font-size: 1.3rem;
  font-weight: 700;
  color: var(--text);
  z-index: 1;
}
.dial__labels {
  position: absolute;
  inset: 0;
}
.dial__label {
  /* 44x44px = --touch-min (Projekt-Standard): bei 6 Werten/60deg-Abstand und
     --label-radius:52px ist der Mittelpunktabstand (Sehne) 2*r*sin(30deg) = r
     = 52px, also > 44px Label-Durchmesser - keine Überlappung. */
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 1;
  width: var(--dial-label-size);
  height: var(--dial-label-size);
  margin: calc(var(--dial-label-size) / -2);
  border-radius: 50%;
  border: 2px solid var(--border-bright);
  background: var(--panel-bg-2);
  color: var(--text-dim);
  font-size: 0.8rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  /* Standard-Technik zum gleichmäßigen Verteilen von Elementen auf einem
     Kreis mit aufrechtem Inhalt: erst hin-, dann herausdrehen, dann wieder
     zurückdrehen - 0deg = oben, im Uhrzeigersinn (wie .dial__pointer). */
  transform: rotate(var(--label-angle)) translate(0, calc(-1 * var(--label-radius)))
    rotate(calc(-1 * var(--label-angle)));
  transition: box-shadow 0.15s ease, border-color 0.15s ease, color 0.15s ease,
    background 0.15s ease;
}
.dial__label:focus-visible {
  outline: 2px solid var(--cyan);
  z-index: 3;
}
.dial__label--active {
  color: var(--active-text);
  border-color: var(--cyan);
  background: var(--cyan-dim);
  box-shadow: 0 0 10px var(--cyan-glow);
  /* Bei 8 oder 9 Positionen ueberlappen sich benachbarte Labels - das aktive
     muss oben liegen, sonst schneidet der Nachbar Rand und Glow an.
     Die uebrigen Labels haben z-index: 1 (s. .dial__label). */
  z-index: 2;
}

/* -- fire button -- */
.fire-button {
  /* IMMER quadratisch. aspect-ratio + max-width/max-height reicht dafuer nicht:
     greift eine der beiden Schranken, bleibt die andere Achse stehen und der Knopf
     wird oval. Stattdessen bekommen beide Achsen denselben Wert aus cqmin - der
     kleineren Kachelkante (.control ist der Container, s. container-type: size).
     52% laesst Platz fuer Label und Padding, gedeckelt auf die Wunschgroesse. */
  width: min(84px, 52cqmin);
  height: min(84px, 52cqmin);
  flex-shrink: 0;
  border-radius: 50%;
  border: 3px solid var(--magenta);
  background: var(--fire-grad);
  color: var(--magenta);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 0 14px var(--magenta-glow), inset 0 0 12px rgba(0, 0, 0, 0.6);
  transition: transform 0.08s ease, box-shadow 0.15s ease;
}
.fire-button__core { font-size: 2rem; }
.fire-button:active,
.fire-button.is-pressed {
  transform: scale(0.9);
  box-shadow: 0 0 30px var(--magenta-glow), inset 0 0 18px rgba(0, 0, 0, 0.7);
}

/* -- buttons (ButtonsH_2..8 / ButtonsV_2..8 / Buttons_2x2) --------------------
   Eine Reihe, eine Spalte oder ein 2x2-Block nummerierter Taster. Die Anzahl
   liefert ui.js als --button-count, die Anordnung als Modifier-Klasse. */
.button-group {
  display: grid;
  gap: 0.3rem;
  width: 100%;
  flex: 1;
  min-height: 0;
}
.button-group--horizontal {
  grid-template-columns: repeat(var(--button-count, 2), minmax(0, 1fr));
  grid-auto-rows: minmax(0, 1fr);
}
.button-group--vertical {
  grid-template-rows: repeat(var(--button-count, 2), minmax(0, 1fr));
  grid-auto-columns: minmax(0, 1fr);
}
/* Eine einzelne Reihe (bzw. Spalte) aus 2-4 Tastern soll nicht die ganze Kachel
   ausfuellen: sonst werden die Taster in einer hohen Kachel zu langen Schlaeuchen
   und dominieren das Panel. Die Kreuzachse geht auf ein Viertel der Kachel. Die
   --wrap-Varianten (6/8 Taster, zweizeilig) brauchen den Platz und bleiben aussen
   vor. Zentriert wird ueber align-self bzw. .control{align-items:center}.
   Der Boden von 32px liegt bewusst unter dem 44px-Touch-Minimum: in einer 1x1-
   Kachel (~85px auf dem Telefon) ist er sonst nicht einzuhalten, ohne dass die
   Tastenreihe die ganze Kachel frisst (gleiche Abwaegung wie bei dial/keypad,
   s. Mobile-Query weiter unten). */
.button-group--horizontal:not(.button-group--wrap) {
  flex: 0 0 auto;
  height: max(25%, 32px);
  align-self: center;
}
.button-group--vertical:not(.button-group--wrap) {
  width: max(25%, 32px);
  max-width: 100%;
  align-self: center;
}

/* Genau zwei Taster: beide bleiben QUADRATISCH (Kantenlaenge = Kreuzachse der
   Gruppe), der Rest der Kachel wird zum Abstand dazwischen. Deshalb Flex statt
   Grid - Grid wuerde die Spalten/Zeilen auf volle Breite/Hoehe strecken. */
.button-group--pair:not(.button-group--wrap) {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.9rem;
  flex: 0 0 auto;
}
.button-group--pair:not(.button-group--wrap) .button-group__btn {
  aspect-ratio: 1 / 1;
  flex: 0 0 auto;
}
.button-group--pair.button-group--horizontal:not(.button-group--wrap) {
  flex-direction: row;
  width: auto;
  max-width: 100%;
}
.button-group--pair.button-group--horizontal:not(.button-group--wrap) .button-group__btn {
  height: 100%;
  width: auto;
}
.button-group--pair.button-group--vertical:not(.button-group--wrap) {
  flex-direction: column;
  height: auto;
  max-height: 100%;
}
.button-group--pair.button-group--vertical:not(.button-group--wrap) .button-group__btn {
  width: 100%;
  height: auto;
}
.button-group--grid {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-template-rows: repeat(2, minmax(0, 1fr));
}
/* 6 und 8 Taster: zweizeilig/zweispaltig umbrechen, sonst werden die einzelnen
   Ziele in einer 3 Zellen breiten Kachel unbedienbar schmal. */
.button-group--wrap.button-group--horizontal {
  grid-template-columns: repeat(calc(var(--button-count, 6) / 2), minmax(0, 1fr));
  grid-template-rows: repeat(2, minmax(0, 1fr));
}
.button-group--wrap.button-group--vertical {
  grid-template-rows: repeat(calc(var(--button-count, 6) / 2), minmax(0, 1fr));
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.button-group__btn {
  min-height: 0;
  min-width: 0;
  border-radius: var(--btn-radius);
  border: 2px solid var(--border-bright);
  background: var(--panel-bg-2);
  color: var(--text-dim);
  font-size: 0.95rem;
  font-weight: 700;
  cursor: pointer;
  transition: box-shadow 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.button-group__btn--active {
  color: var(--active-text);
  border-color: var(--magenta);
  background: var(--magenta-dim);
  box-shadow: 0 0 12px var(--magenta-glow);
}

/* -- keypad (3x3, 9 Zustaende, nur in 3x3-Kacheln) -------------------------- */
.keypad {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-template-rows: repeat(3, minmax(0, 1fr));
  gap: 0.25rem;
  width: 100%;
  flex: 1;
  min-height: 0;
}
.keypad__key {
  min-height: 0;
  min-width: 0;
  border-radius: var(--key-radius);
  border: 2px solid var(--border-bright);
  background: var(--panel-bg-2);
  color: var(--text-dim);
  font-size: 0.95rem;
  font-weight: 700;
  cursor: pointer;
  transition: box-shadow 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.keypad__key--active {
  color: var(--active-text);
  border-color: var(--amber);
  background: var(--keypad-active-bg);
  box-shadow: 0 0 12px var(--amber-glow);
}

/* -- text (TextButton / TextButtons_2 / TextButtons_3 / SingleTextButton) ---- */
/* Trigger-Verben statt Zahlen (shared/lang/*.js game.values.verbs). Immer als
   Spalte, damit auch lange Woerter wie TROCKEN lesbar bleiben. */
.text-buttons {
  display: grid;
  grid-template-rows: repeat(var(--button-count, 2), minmax(0, 1fr));
  gap: 0.25rem;
  width: 100%;
  flex: 1;
  min-height: 0;
}
.text-buttons__btn {
  min-height: 0;
  min-width: 0;
  border-radius: var(--key-radius);
  border: 2px solid var(--border-bright);
  background: var(--panel-bg-2);
  color: var(--text-dim);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  cursor: pointer;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: box-shadow 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.text-buttons__btn--active {
  color: var(--active-text);
  border-color: var(--cyan);
  background: var(--cyan-dim);
  box-shadow: 0 0 12px var(--cyan-glow);
}

/* -- nav (NavigationStick) --------------------------------------------------- */
/* Steuerkreuz. Im Original ohne supportedDimensions und damit nie auf einem
   Board - der Stil existiert, damit der Typ sofort funktioniert, sobald man ihm
   in shared/controlLibrary.js dims gibt. */
.nav-stick {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-template-rows: repeat(3, minmax(0, 1fr));
  gap: 0.2rem;
  width: 100%;
  flex: 1;
  min-height: 0;
}
.nav-stick__btn {
  min-height: 0;
  min-width: 0;
  border-radius: var(--key-radius);
  border: 2px solid var(--border-bright);
  background: var(--panel-bg-2);
  color: var(--text-dim);
  font-size: 1rem;
  cursor: pointer;
  transition: box-shadow 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
/* Modifier kommen aus der POSITION im Wertevorrat (public/js/ui.js NAV_KEYS),
   nicht aus dem uebersetzten Richtungswort - sonst braeuchte jede Sprache
   eigene Regeln. */
.nav-stick__btn--north { grid-area: 1 / 2; }
.nav-stick__btn--west { grid-area: 2 / 1; }
.nav-stick__btn--east { grid-area: 2 / 3; }
.nav-stick__btn--south { grid-area: 3 / 2; }
.nav-stick__btn--active {
  color: var(--active-text);
  border-color: var(--green);
  background: var(--nav-active-bg);
  box-shadow: 0 0 12px var(--green-glow);
}

/* ---------------------------------------------------------------------- */
/* Overlays                                                                */
/* ---------------------------------------------------------------------- */
.overlay {
  position: fixed;
  inset: 0;
  z-index: 800;
  pointer-events: none;
}
.overlay[hidden] { display: none; }

/* -- hazard overlay -- */
#hazard-overlay {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
}
#hazard-overlay.hazard-overlay--modal {
  pointer-events: auto;
  background: rgba(5, 7, 11, 0.82);
}
#hazard-overlay.hazard-overlay--passive {
  align-items: flex-start;
  justify-content: center;
  padding-top: 70px;
  background: transparent;
}

.hazard-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
  padding: 1.5rem;
  background: var(--panel-bg);
  border: 3px solid var(--red);
  border-radius: var(--radius);
  box-shadow: 0 0 30px var(--red-glow);
  max-width: 420px;
  text-align: center;
}
.hazard-card__title {
  font-size: 1.2rem;
  letter-spacing: 0.1em;
  color: var(--red);
  text-shadow: 0 0 10px var(--red-glow);
}
.hazard-card__countdown {
  font-size: 0.85rem;
  color: var(--text-dim);
}
.hazard-card__progress {
  font-size: 0.95rem;
  color: var(--amber);
}
.hazard-card__done {
  color: var(--green);
  font-weight: 700;
  letter-spacing: 0.1em;
}

.hazard-banner {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.5rem 1rem;
  background: rgba(10, 14, 20, 0.9);
  border: 2px solid var(--amber);
  border-radius: 999px;
  color: var(--amber);
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  box-shadow: 0 0 16px var(--amber-glow);
}

/* -- warp overlay -- */
#warp-overlay {
  background: radial-gradient(circle, #0a1a2c, var(--bg-deep) 70%);
  display: none;
}

/* -- phase overlay (COUNTDOWN / LEVEL_START) -- */
#phase-overlay {
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(5, 7, 11, 0.6);
}
.phase-overlay__value {
  font-family: var(--font-mono);
  font-weight: bold;
  letter-spacing: 0.15em;
  color: #fff;
  text-shadow: 0 0 20px rgba(255, 255, 255, 0.8), 0 0 50px var(--cyan-glow);
}
.phase-overlay__value--countdown {
  font-size: clamp(4rem, 22vw, 9rem);
}
.phase-overlay__value--level {
  font-size: clamp(2rem, 8vw, 4.5rem);
}

/* -- flash overlay -- */
#flash-overlay {
  background: transparent;
}
#flash-overlay.fx-flash-success {
  background: radial-gradient(circle, rgba(53, 255, 143, 0.35), transparent 70%);
  animation: flashPulse 0.6s ease-out;
}
#flash-overlay.fx-flash-fail {
  background: radial-gradient(circle, rgba(255, 59, 59, 0.4), transparent 70%);
  animation: flashPulse 0.6s ease-out;
}
@keyframes flashPulse {
  0% { opacity: 0; }
  15% { opacity: 1; }
  100% { opacity: 0; }
}

/* ---------------------------------------------------------------------- */
/* Global fx classes                                                       */
/* ---------------------------------------------------------------------- */
#screen-game.fx-shake {
  animation: panicShake 0.32s infinite;
}
@keyframes panicShake {
  0%, 100% { transform: translate(0, 0); }
  20% { transform: translate(-4px, 2px); }
  40% { transform: translate(3px, -3px); }
  60% { transform: translate(-3px, -1px); }
  80% { transform: translate(4px, 2px); }
}

body.fx-inverted { transform: rotate(180deg); }

#screen-game.fx-distortion {
  filter: saturate(1.35) contrast(1.12);
  will-change: transform;
}

/* ---------------------------------------------------------------------- */
/* Slime malfunction (drip-Look; effects.js setzt Position/Größe inline)   */
/* ---------------------------------------------------------------------- */
.slime-blob {
  position: relative;
}
.slime-blob::after {
  content: "";
  position: absolute;
  bottom: -9px;
  left: 50%;
  width: 7px;
  height: 16px;
  background: inherit;
  border-radius: 0 0 60% 60%;
  transform: translateX(-50%);
}

/* ---------------------------------------------------------------------- */
/* Loose panel malfunction (pro Control, siehe effects.js startLooseControl) */
/* Haengt an genau einer oberen Ecke (transform-origin per JS auf die Ecke   */
/* gesetzt) und wird NUR rotiert (rotate(), keine Translation) - dadurch     */
/* bleibt die Ecke beim Drehen automatisch fix. transform/transition kommen */
/* inline aus effects.js (Ruhe-Pendeln per rAF bzw. Snap-/Rueckschwing-      */
/* Animation beim Loslassen); hier nur Optik + Nagel-Visual an der Ecke.    */
/* ---------------------------------------------------------------------- */
.control.control--loose {
  cursor: grab;
  touch-action: none;
  border-color: var(--amber);
  box-shadow: 0 0 20px var(--amber-glow);
  z-index: 20;
}

.control.control--loose.control--loose-grabbing {
  cursor: grabbing;
}

.control--loose-left::before,
.control--loose-right::before {
  content: '';
  position: absolute;
  top: 4px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 35%, #fff9, var(--amber) 70%);
  box-shadow: 0 0 5px var(--amber-glow), inset 0 0 2px rgba(0, 0, 0, 0.5);
  z-index: 2;
  pointer-events: none;
}
.control--loose-left::before {
  left: 4px;
}
.control--loose-right::before {
  right: 4px;
}

/* ---------------------------------------------------------------------- */
/* Game over                                                               */
/* ---------------------------------------------------------------------- */
.gameover {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.5rem;
  padding-top: 2.5rem;
  text-align: center;
}
.gameover__title {
  font-size: clamp(1.8rem, 8vw, 2.8rem);
  color: var(--red);
  letter-spacing: 0.15em;
  text-shadow: 0 0 16px var(--red-glow);
}
.gameover__stats {
  width: 100%;
  max-width: 440px;
  border-collapse: collapse;
  font-size: 0.9rem;
}
.gameover__stats td {
  padding: 0.5rem 0.6rem;
  border-bottom: 1px solid var(--border);
  text-align: left;
}
.gameover__stats td:last-child {
  text-align: right;
  color: var(--cyan);
  font-weight: 700;
}

.restart-hint {
  color: var(--text-dim);
  font-size: 0.85rem;
  letter-spacing: 0.05em;
}

/* ---------------------------------------------------------------------- */
/* Debug overlay & connection banner                                       */
/* ---------------------------------------------------------------------- */
.debug-overlay {
  position: fixed;
  bottom: 8px;
  left: 8px;
  z-index: 950;
  background: rgba(5, 7, 11, 0.85);
  border: 1px solid var(--border-bright);
  border-radius: 6px;
  padding: 0.4rem 0.6rem;
  font-size: 0.65rem;
  color: var(--text-dim);
  pointer-events: none;
}
.debug-overlay[hidden] { display: none; }

.connection-banner {
  position: fixed;
  top: 58px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 960;
  background: rgba(122, 0, 0, 0.9);
  border: 2px solid var(--red);
  border-radius: 999px;
  padding: 0.4rem 1rem;
  font-size: 0.8rem;
  letter-spacing: 0.05em;
  color: #fff;
  box-shadow: 0 0 16px var(--red-glow);
}
.connection-banner[hidden] { display: none; }

/* ---------------------------------------------------------------------- */
/* Mobile: alles auf EINEN Bildschirm, kein Scrollen im Spiel              */
/* ---------------------------------------------------------------------- */
/* Das Panel-Layout selbst hat KEINEN Breakpoint mehr: es ist auf jeder Breite
   dasselbe quadratische Raster aus dem Server-Layout (s. .panel). Diese Query
   bindet nur den Game-Screen an die Viewport-Höhe (kein Scrollen im Spiel) und
   verkleinert die Instrumente, die nicht von sich aus mitschrumpfen.

   Bewusste Abweichung vom 44px-Touch-Minimum: in einer 1x1-Kachel eines
   4x4-Boards (~85px auf dem Telefon) sind bei dial/keypad keine 44px großen
   Ziele unterzubringen - 6 Dial-Positionen à 44px bräuchten allein einen Ring
   von >130px Durchmesser. */
@media (max-width: 640px) {
  /* -- Grundgerüst: Header fix, #app füllt den Rest ------------------------ */
  body {
    height: 100vh;
    height: 100dvh; /* dvh: Höhe ohne ein-/ausfahrende Browser-UI */
    min-height: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
  }
  .app-header { flex: 0 0 auto; }

  #app {
    flex: 1 1 auto;
    min-height: 0;
    padding: 0.45rem;
    padding-bottom: 0.45rem;
    /* Lobby und Game-Over dürfen weiterhin scrollen (Spielerliste,
       Statistiktabelle) - der Game-Screen unten ist so bemessen, dass er es
       nie muss. */
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
  }

  .screen {
    min-height: 0; /* statt calc(100vh - 60px), sonst überläuft #app */
    gap: 0.5rem;
  }

  /* -- Game-Screen: HUD + Order fest, Panel nimmt den Rest ---------------- */
  #screen-game {
    height: 100%;
    overflow: hidden;
  }
  #hud,
  #order { flex: 0 0 auto; }

  #panel {
    flex: 0 1 auto;
    min-height: 0;
    /* Auf schmalen Viewports fressen HUD + Order mehr Anteil - das Quadrat wird
       enger an die Viewporthöhe gebunden, damit der Game-Screen scrollfrei
       bleibt (siehe --panel-max in .panel). */
    --panel-max: 52vh;
    gap: 0.4rem;
  }

  /* -- HUD -- */
  .hud { padding: 0.35rem 0.6rem; gap: 0.3rem 0.7rem; }
  .hud__bar-track { height: 12px; }
  .hud__integrity-label { font-size: 0.55rem; }
  .hud__integrity-text { font-size: 0.75rem; }
  .hud__stats { gap: 0.7rem; font-size: 0.65rem; }
  .hud__stat strong { font-size: 0.8rem; }

  /* -- Order-Box (reservierte Höhen bleiben erhalten, nur kleiner) -- */
  .order { padding: 0.5rem 0.6rem; gap: 0.3rem; min-height: 0; }
  .order__status {
    font-size: 0.6rem;
    padding: 0.1rem 0.45rem;
    min-height: calc(1.1em + 0.2rem + 2px);
  }
  .order__symbol { font-size: 1.9rem; }
  .order__text { font-size: clamp(0.95rem, 4.2vw, 1.2rem); }
  .order__progress-track { height: 7px; }

  /* -- Kacheln -- */
  .control {
    min-height: 0; /* Höhe kommt ausschließlich aus grid-auto-rows: 1fr */
    padding: 0.35rem 0.3rem 0.4rem;
    gap: 0.5rem;
    /* space-between (Desktop) zieht Label und Instrument bei nur 2 Zeilen
       Panelhöhe an die Kachelränder auseinander - auf Mobile bleiben sie als
       zusammengehörige Gruppe mittig. */
    justify-content: center;
  }
  .control__label { font-size: 0.58rem; line-height: 1.2; letter-spacing: 0.01em; }
  .control__symbol { font-size: 1.2rem; }

  /* -- toggle (Thumb-Weg = Track-Innenbreite - Ränder - Thumb) -- */
  .toggle { gap: 0.25rem; }
  .toggle__track { width: 52px; height: 28px; }
  .toggle__thumb { width: 20px; height: 20px; }
  .toggle[aria-checked="true"] .toggle__thumb { transform: translateX(24px); }
  .toggle__state { font-size: 0.62rem; }

  /* -- Instrumente füllen die Resthöhe der Kachel, statt eine feste
        Mindesthöhe zu behalten. Zwei Effekte: auf normalen Telefonen werden die
        Ziele DEUTLICH größer als 44px, und auf sehr kurzen Viewports schrumpfen
        sie mit, statt aus der Kachel zu laufen (.control hat overflow:hidden,
        würde also sichtbar abschneiden). -- */
  .switch,
  .button-group,
  .keypad,
  .text-buttons,
  .nav-stick {
    flex: 1 1 auto;
    min-height: 0;
  }
  .switch { gap: 0.2rem; }
  .switch__state { font-size: 0.6rem; letter-spacing: 0.05em; }
  .control--h .switch__track { height: 28px; }
  .control--h .switch__lever { width: 26px; height: 20px; }
  .control--h .switch--on .switch__lever { transform: translateX(30px); }
  .control--v .switch__track { width: 32px; }
  .control--v .switch__lever { width: 24px; height: 26px; }
  .control--v .switch--on .switch__lever { transform: translateY(-34px); }

  /* -- slider (Orientierung kommt aus der Bibliothek, siehe ui.js buildSlider) -- */
  .slider { gap: 0.2rem; }
  .slider__value { font-size: 1rem; }
  .slider__input { height: 34px; }
  .slider__ticks { font-size: 0.5rem; }

  /* -- dial: Ring passt in die ~100px breite Kacheln-Innenfläche.
        Wrap = 2*(--label-radius + halbe --dial-label-size) = 2*(32+15) = 94px.
        Überlappungscheck wie oben: Sehne bei 6 Positionen = --label-radius
        (32px) > --dial-label-size (30px). -- */
  .dial-wrap { --label-radius: 32px; --dial-label-size: 30px; width: 94px; height: 94px; }
  .dial { width: 38px; height: 38px; border-width: 2px; }
  .dial__value { font-size: 0.9rem; }
  .dial__label { font-size: 0.55rem; border-width: 1px; }

  /* -- button -- */
  .fire-button { width: min(56px, 52cqmin); height: min(56px, 52cqmin); border-width: 2px; }
  .fire-button__core { font-size: 1.3rem; }

  /* -- Tastenfelder: enger, kleinere Schrift -- */
  .button-group, .keypad, .text-buttons, .nav-stick { gap: 0.2rem; }
  .button-group__btn, .keypad__key { font-size: 0.8rem; }
  .text-buttons__btn { font-size: 0.58rem; letter-spacing: 0.04em; }
}

/* Kurze Viewports (altes iPhone SE & Co., ~568px hoch): das Panel bekommt sonst
   nur ~95px hohe Zeilen. Header/HUD/Order geben Höhe ab, die festen Instrumente
   (dial/button/toggle - die einzigen, die nicht mitschrumpfen können) gehen eine
   Stufe runter. Beide Bedingungen nötig: nur so bleibt der Normalfall
   (375x812 & Co.) unangetastet. */
@media (max-width: 640px) and (max-height: 700px) {
  .app-header { padding: 0.25rem 0.7rem; }
  .app-header__title { font-size: 0.7rem; letter-spacing: 0.18em; }
  .audio-toggle { width: 34px; height: 34px; min-width: 34px; min-height: 34px; font-size: 0.95rem; }

  .hud { padding: 0.25rem 0.5rem; }
  .hud__bar-track { height: 9px; }
  .hud__stats { font-size: 0.6rem; }

  .order { padding: 0.35rem 0.5rem; gap: 0.2rem; }
  .order__symbol { font-size: 1.4rem; }
  .order__text { font-size: 0.95rem; }
  .order__progress-track { height: 5px; }

  .control { padding: 0.25rem 0.2rem 0.3rem; gap: 0.3rem; }
  .control__label { font-size: 0.52rem; }

  .fire-button { width: min(44px, 52cqmin); height: min(44px, 52cqmin); }
  .fire-button__core { font-size: 1.1rem; }
  .toggle__state { font-size: 0.55rem; }
  .slider__input { height: 28px; }
}

/* Sehr schmale Geräte (~320px): Kachel-Innenbreite fällt unter die 94px des
   Dial-Rings - Ring, Button und Toggle nochmals eine Stufe kleiner.
   Wrap = 2*(28+13) = 82px, Sehne 28px > --dial-label-size 26px. */
@media (max-width: 360px) {
  .dial-wrap { --label-radius: 28px; --dial-label-size: 26px; width: 82px; height: 82px; }
  .dial { width: 34px; height: 34px; }
  .dial__value { font-size: 0.8rem; }
  .fire-button { width: min(50px, 52cqmin); height: min(50px, 52cqmin); }
  .toggle__track { width: 46px; height: 26px; }
  .toggle__thumb { width: 18px; height: 18px; }
  .toggle[aria-checked="true"] .toggle__thumb { transform: translateX(20px); }
}

/* ---------------------------------------------------------------------- */
/* Reduced motion                                                          */
/* ---------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
