:root {
  --bg: #0e1116;
  --panel: #1a212b;
  --text: #e8edf4;
  --muted: #93a1b3;
  --accent: #3ddc84;
  --danger: #e0564f;
  --border: #2b3646;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html, body { height: 100%; }

body {
  background: var(--bg);
  color: var(--text);
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 14px 18px;
  border-bottom: 1px solid var(--border);
}

h1 { font-size: 1.25rem; flex-shrink: 0; }

.chip {
  font-size: 0.8rem;
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 4px 12px;
  white-space: nowrap;
  max-width: 55vw;
  overflow: hidden;
  text-overflow: ellipsis;
}

main, section {
  max-width: 900px;
  width: 100%;
  margin: 0 auto;
  padding: 18px;
}

/* Zuschauer-Modus: volle Fensterbreite und -höhe fürs Grid */
#viewUI {
  max-width: none;
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  min-height: 0;
  padding: 10px 12px;
}

#viewUI[hidden] { display: none; }

/* Zuschauen belegt bewusst den ganzen Bildschirm: das Bild IST der Inhalt.
   Die Bedienleisten liegen als Überblendung darüber und erscheinen nur auf Tipp —
   auf dem Handy fraßen sie vorher zwei Drittel der Fläche. */
#viewLive {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: #000;
  display: flex;
  flex-direction: column;
}

#viewLive[hidden] { display: none; }

.intro { color: var(--muted); margin-bottom: 18px; }

button {
  font: inherit;
  color: var(--text);
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 12px 16px;
  cursor: pointer;
  touch-action: manipulation;
}

button:active { transform: scale(0.98); }

button.big {
  display: block;
  width: 100%;
  padding: 20px;
  font-size: 1.2rem;
  margin-bottom: 14px;
  text-align: left;
}

button.big small {
  display: block;
  font-size: 0.85rem;
  color: var(--muted);
  margin-top: 4px;
}

button.go { background: #14351f; border-color: #2c6e43; text-align: center; }
button.danger { background: #3a1a18; border-color: #7a332e; }

.btnrow {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  align-items: center;
  margin: 12px 0;
}

.row { margin-bottom: 14px; }

label { display: block; color: var(--muted); font-size: 0.9rem; margin-bottom: 6px; }
label.inline { display: flex; align-items: center; gap: 8px; font-size: 1rem; color: var(--text); }
input[type="checkbox"] { width: 20px; height: 20px; }

input[type="text"],
input[type="password"],
select {
  font: inherit;
  width: 100%;
  padding: 12px;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  transition: border-color 0.15s, box-shadow 0.15s;
}

input[type="text"]:focus,
input[type="password"]:focus,
select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(61, 220, 132, 0.15);
}

input::placeholder { color: #56657a; }

/* --- Login-Karte --- */
#loginUI {
  display: flex;
  justify-content: center;
  padding-top: max(4vh, 18px);
}

#loginUI[hidden] { display: none; }

.logincard {
  width: 100%;
  max-width: 420px;
  background: linear-gradient(180deg, #1d2530, var(--panel));
  border: 1px solid var(--border);
  border-radius: 18px;
  padding: 30px 26px 22px;
  box-shadow: 0 14px 44px rgba(0, 0, 0, 0.45);
}

.loginhead { text-align: center; margin-bottom: 22px; }

.loginhead .loginicon {
  font-size: 2.6rem;
  line-height: 1;
  margin-bottom: 8px;
  filter: drop-shadow(0 4px 14px rgba(61, 220, 132, 0.35));
}

.loginhead h2 { font-size: 1.35rem; letter-spacing: 0.02em; }

.loginhead p { color: var(--muted); font-size: 0.88rem; margin-top: 4px; }

.logincard .row { margin-bottom: 16px; }

.logincard input { background: #10161e; }

#loginCode {
  text-align: center;
  font-size: 1.35rem;
  letter-spacing: 0.45em;
  font-variant-numeric: tabular-nums;
}

#loginCode::placeholder { letter-spacing: 0.45em; }

.pwwrap { position: relative; }

.pwwrap input { padding-right: 50px; }

.pweye {
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%);
  background: transparent;
  border: none;
  padding: 6px 10px;
  font-size: 1.05rem;
  opacity: 0.7;
}

.pweye:hover, .pweye.aktiv { opacity: 1; }

.loginbtn {
  text-align: center;
  margin-top: 4px;
  background: linear-gradient(180deg, #1b4a2b, #14351f);
  border-color: #2c6e43;
  box-shadow: 0 6px 18px rgba(61, 220, 132, 0.15);
}

.loginbtn:hover { border-color: var(--accent); }

#loginError { text-align: center; min-height: 1.2em; margin-top: 12px; }

.status { color: var(--muted); font-size: 0.95rem; margin: 10px 0; }

.help { margin-top: 24px; color: var(--muted); }
.help summary { cursor: pointer; padding: 8px 0; }
.help ul { padding: 8px 0 0 20px; display: grid; gap: 8px; font-size: 0.92rem; }

#camPreview {
  width: 100%;
  max-height: 60vh;
  background: #000;
  border-radius: 12px;
  border: 1px solid var(--border);
}

#grid {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  grid-template-columns: 1fr; /* wird von relayout() je nach Kamera-Anzahl gesetzt */
  gap: 6px;
  padding: 0;                 /* randlos wie bei Überwachungs-Apps — jeder Pixel zählt */
  margin: 0;
}

.tile {
  position: relative;
  background: #000;
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
  min-width: 0;
  min-height: 0;
}

/* Eine Kachel groß („Solo"): die übrigen werden ausgeblendet, relayout() gibt ihr
   dadurch automatisch die volle Fläche. Bewusst KEIN natives Vollbild des Videos —
   dabei verschwinden alle eigenen Bedienelemente, genau der bisherige Ärger. */
.tile.solo { border-radius: 0; border-color: transparent; }

/* Zurück in die Übersicht führt derselbe ⛶-Knopf, der groß gemacht hat —
   ein zusätzlicher Knopf in der Ecke wäre doppelt. */

/* 🔍 Lupe: nur in der Großansicht sinnvoll — in der Übersicht sind die Kacheln zu klein */
.tile:not(.solo) .btnzoom { display: none; }

/* Zoomen ist reine Anzeige: transform auf dem Video, von .tile { overflow:hidden } beschnitten.
   touch-action: none, damit das eigene Zwei-Finger-Zoomen nicht mit dem des Browsers kämpft. */
.tile.zoommodus video { touch-action: none; cursor: zoom-in; }
.tile.zoommodus.zoomed video { cursor: grab; }
.tile.zoommodus.zoomed video:active { cursor: grabbing; }

/* Der Faktor steht im Knopf selbst — eine eigene Anzeige wäre zusätzliches Gedränge */
.tile .tools button.btnzoom { min-width: 44px; font-variant-numeric: tabular-nums; }

/* 🎯 Fokus wählen: nur in der Großansicht — in der Übersicht sind die Kacheln zu
   klein zum Zielen. Ob der Knopf überhaupt erscheint, steuert die Kamera per fcap:. */
.tile:not(.solo) .btnfocus,
.tile:not(.solo) .btnfocusauto { display: none; }

.tile.fokusmodus video { cursor: crosshair; }

/* 🔃 Drehen: nur in der Großansicht. Die Knöpfe erscheinen erst, wenn die Kamera
   per rot: meldet, dass sie den Befehl versteht (alte Kamera-Stände senden das nicht). */
.tile:not(.solo) .btnrotl,
.tile:not(.solo) .btnrotr { display: none; }

/* Zielquadrat an der getippten Stelle — wie in Kamera-Apps. Wird grün, sobald die
   Kamera den Punkt bestätigt (focus:ok). */
.fokusmarke {
  position: absolute;
  width: 58px;
  height: 58px;
  margin: -29px 0 0 -29px;
  border: 2px solid rgba(255, 214, 64, 0.95);
  border-radius: 12px;
  pointer-events: none;
  z-index: 45;
  animation: fokusein 0.45s ease-out;
}

.fokusmarke::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: 6px;
  height: 6px;
  margin: -3px;
  border-radius: 50%;
  background: rgba(255, 214, 64, 0.95);
}

.fokusmarke.ok { border-color: var(--accent); }
.fokusmarke.ok::after { background: var(--accent); }

@keyframes fokusein {
  from { transform: scale(1.5); opacity: 0.3; }
  to { transform: scale(1); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .fokusmarke { animation: none; }
}

/* 🎬 Ruhiger Blick: nur in der Großansicht und nur mit Maus — auf dem Handy gäbe es
   keine Zeigerbewegung, mit der man die Bedienung zurückholen könnte. */
/* Sitzt in der Bedienleiste (nicht in den Kachel-Werkzeugen), damit er unabhängig
   davon auffindbar ist, ob die Kachel-Knöpfe gerade eingeblendet sind. */
#viewLive:not(.sologan) #btnKino { display: none; }

/* 📺 „Nur Bild" — Gegenstück zum ruhigen Blick, aber für die ÜBERSICHT und für Finger.
   Wird unten im Bedienart-Block eingeblendet. */
#btnBarsAus { display: none; }

/* Ausgeblendete Leisten sollen in der Übersicht auch wirklich Platz freigeben —
   bloßes Durchsichtigmachen würde die Kacheln nicht größer machen. In der
   Großansicht überlagern sie ohnehin, dort genügt das Ausblenden per opacity. */
#viewLive:not(.sologan):not(.bars) .viewbar { display: none; }

#viewLive .viewbar,
#viewLive .tile .tools,
#viewLive .tile .tileinfo,
#viewLive .tile .label { transition: opacity 0.25s ease; }

#viewLive.kino.ruhig #viewBottom,
#viewLive.kino.ruhig .tile .tools,
#viewLive.kino.ruhig .tile .tileinfo,
#viewLive.kino.ruhig .tile .label { opacity: 0; pointer-events: none; }

/* Die Kopfleiste bleibt bestehen, damit ✕ sichtbar bleibt — nur ihr übriger Inhalt
   verschwindet. (Ein Kind einer Ebene mit opacity:0 lässt sich nicht zurückholen.) */
#viewLive.kino.ruhig #viewTop { background: none; }
#viewLive.kino.ruhig #viewTop > *:not(#btnViewStop) { opacity: 0; }
#viewLive.kino.ruhig #btnViewStop { opacity: 0.45; }
#viewLive.kino.ruhig { cursor: none; }

.tile video { width: 100%; height: 100%; object-fit: contain; background: #000; }

.tile .label {
  position: absolute;
  left: 8px; top: 8px;
  background: rgba(0,0,0,0.55);
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.8rem;
}

.tile .state {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  text-align: center;
  padding: 12px;
  color: var(--muted);
  background: rgba(14,17,22,0.85);
  font-size: 0.9rem;
}

.tile .state[hidden] { display: none; }

.tile .tools {
  position: absolute;
  right: 8px; bottom: 8px;
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 6px;
}

.tile .tileinfo {
  position: absolute;
  right: 8px; top: 8px;
  background: rgba(0, 0, 0, 0.55);
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.72rem;
  color: var(--muted);
  white-space: nowrap;
}

.tile .tileinfo[hidden] { display: none; }
.tile .tileinfo.ok::before { content: '● '; color: var(--accent); }
.tile .tileinfo.mid::before { content: '● '; color: #e8b34b; }
.tile .tileinfo.bad::before { content: '● '; color: var(--danger); }

/* 👀 Bewegung erkannt: Rahmen pulsiert rot.
   Eigene Ebene ÜBER dem Video und entlang der tatsächlichen BILDFLÄCHE (Position setzt
   setzeAlarmRahmen in viewer.js). Vorher war es ein Schatten der Kachel — der lag
   außerhalb, wurde in der Großansicht vom Bildschirmrand abgeschnitten und von
   abgerundeten Handy-Ecken verschluckt. */
.tile .motionframe {
  position: absolute;
  display: none;
  z-index: 40;
  pointer-events: none;
  border: 4px solid rgba(224, 86, 79, 0.9);
}

.tile.motion .motionframe {
  display: block;
  animation: motionpuls 0.7s ease-in-out 3;
}

@keyframes motionpuls {
  0%, 100% {
    border-color: rgba(224, 86, 79, 0.85);
    box-shadow: 0 0 22px rgba(224, 86, 79, 0.5), inset 0 0 22px rgba(224, 86, 79, 0.28);
  }
  50% {
    border-color: rgba(255, 148, 138, 1);
    box-shadow: 0 0 48px rgba(224, 86, 79, 0.9), inset 0 0 44px rgba(224, 86, 79, 0.5);
  }
}

@media (prefers-reduced-motion: reduce) {
  .tile.motion .motionframe {
    animation: none;
    border-color: rgba(255, 148, 138, 1);
    box-shadow: 0 0 30px rgba(224, 86, 79, 0.7), inset 0 0 30px rgba(224, 86, 79, 0.35);
  }
}

/* Hinweis-Popups oben rechts (3 s) */
#toasts {
  position: fixed;
  /* über der Zuschauer-Ansicht (z-index 60) und nicht unter der Notch/Kopfleiste */
  top: max(14px, calc(env(safe-area-inset-top) + 10px));
  right: max(14px, env(safe-area-inset-right));
  z-index: 200;
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: flex-end;
  pointer-events: none;
}

/* Standard-Hinweis (z. B. „Funktion nicht verfügbar") */
.toast {
  background: rgba(26, 33, 43, 0.97);
  border: 1px solid var(--border);
  color: var(--text);
  padding: 12px 18px;
  border-radius: 12px;
  font-size: 0.95rem;
  max-width: min(80vw, 460px);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
  animation: toastein 0.25s ease-out;
}

/* Bewegungs-Warnung: deutlich größer und gelb */
.toast.warnung {
  background: #e8b34b;
  border: 2px solid #fff0cf;
  color: #241c05;
  font-size: 1.7rem;
  font-weight: 700;
  padding: 20px 30px;
  border-radius: 18px;
  box-shadow: 0 12px 40px rgba(232, 179, 75, 0.5), 0 0 0 4px rgba(232, 179, 75, 0.25);
}

@keyframes toastein {
  from { opacity: 0; transform: translateX(24px); }
  to { opacity: 1; transform: none; }
}

/* Rundgang-Intervall-Auswahl in der Steuerleiste */
.btnrow select.qsel {
  font: inherit;
  font-size: 0.9rem;
  width: auto;
  padding: 10px 8px;
  background: var(--panel);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 10px;
}

.tile .tools button { padding: 6px 10px; font-size: 0.9rem; background: rgba(0,0,0,0.55); touch-action: none; user-select: none; -webkit-user-select: none; }

.tile .tools button.talking {
  background: var(--danger);
  border-color: #ff8a80;
  box-shadow: 0 0 10px rgba(224, 86, 79, 0.7);
}

.tile .tools button.torch-on {
  background: #6b5b1e;
  border-color: #e8b34b;
  box-shadow: 0 0 10px rgba(232, 179, 75, 0.6);
}

/* Einheitlicher Aktiv-Zustand für alle Schalter (grün = an) —
   Ausnahmen: Taschenlampe leuchtet gelb (torch-on), Sprechen rot (talking) */
button.wide-on,
button.on {
  background: #14351f;
  border-color: var(--accent);
  box-shadow: 0 0 10px rgba(61, 220, 132, 0.45);
}

.tile .tools select.qsel {
  font: inherit;
  font-size: 0.85rem;
  width: auto;
  padding: 5px 6px;
  background: rgba(0, 0, 0, 0.55);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 8px;
}

/* Analyse-Video der Bewegungserkennung: bewusst winzig statt display:none —
   versteckte Videos werden von Browsern pausiert, dann stünde die Erkennung still.
   Die Analyse nutzt die volle intrinsische Auflösung, die CSS-Größe ist dafür egal. */
#camAnalyse {
  position: fixed;
  left: 0;
  bottom: 0;
  width: 2px;
  height: 2px;
  opacity: 0.01;
  pointer-events: none;
  border: 0;
  z-index: 0;
}

/* 🚫 Zonen-Modus: Ignorier-Bereiche über dem Livebild bemalen */
.zonetools { align-items: center; }

.zone {
  position: absolute;
  inset: 0;
  z-index: 5;
}

.zone[hidden] { display: none; }

.zone .zcan {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  touch-action: none;      /* sonst scrollt/zoomt die Seite beim Malen mit */
  cursor: crosshair;
}

.zone .zctl {
  position: absolute;
  left: 8px;
  right: 8px;
  bottom: 8px;
  z-index: 6;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  background: rgba(0, 0, 0, 0.78);
  border: 1px solid var(--border);
  border-radius: 12px;
  font-size: 0.8rem;
}

.zone .zctl input[type="range"] { flex: 0 1 120px; min-width: 90px; }
.zone .zctl button { padding: 5px 9px; font-size: 0.8rem; }
/* Groß/klein ist hier der einzige Weg in die Großansicht — entsprechend greifbar */
.zone .zctl .zbig { padding: 6px 11px; font-size: 1rem; line-height: 1; }
.zone .zctl select.qsel {
  font: inherit;
  font-size: 0.8rem;
  width: auto;
  padding: 4px 6px;
  background: rgba(0, 0, 0, 0.55);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 8px;
}

.zone .zval {
  min-width: 1.6em;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* Live-Balken: die Alarmschwelle sitzt genau in der Mitte */
.zone .zbar {
  position: relative;
  flex: 1 1 90px;
  min-width: 70px;
  height: 10px;
  background: rgba(255, 255, 255, 0.15);
  border-radius: 999px;
  overflow: hidden;
}

.zone .zbar::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
  width: 2px;
  background: rgba(255, 255, 255, 0.7);
}

.zone .zfill {
  height: 100%;
  width: 0;
  background: var(--accent);
  transition: width 0.12s linear;
}

.zone .zfill.ueber { background: var(--danger); }

/* ============ ⠿ Kacheln per Griff umsortieren ============
   Die Punkte sind als SVG gezeichnet statt als Schriftzeichen — Braille-Glyphen fehlen
   auf manchen Geräten und würden dort als leeres Kästchen erscheinen. */
.tile .grip {
  display: none;
  position: absolute;
  left: 8px;
  bottom: 8px;
  z-index: 9;
  width: 38px;
  height: 38px;
  padding: 0;
  border-radius: 10px;
  background-color: rgba(0, 0, 0, 0.55);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 20'%3E%3Cg fill='%2393a1b3'%3E%3Ccircle cx='4' cy='4' r='1.7'/%3E%3Ccircle cx='10' cy='4' r='1.7'/%3E%3Ccircle cx='4' cy='10' r='1.7'/%3E%3Ccircle cx='10' cy='10' r='1.7'/%3E%3Ccircle cx='4' cy='16' r='1.7'/%3E%3Ccircle cx='10' cy='16' r='1.7'/%3E%3C/g%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 13px 19px;
  cursor: grab;
  touch-action: none;      /* sonst scrollt/zoomt die Seite beim Ziehen mit */
  user-select: none;
  -webkit-user-select: none;
}

/* Griffe nur zeigen, wenn es etwas zu tauschen gibt — und nicht beim Zonenmalen,
   im Rundgang oder in der Großansicht, wo sie sinnlos wären. */
#viewLive.mehrkameras:not(.sologan):not(.tourrun):not(.zonenmodus) .tile .grip { display: block; }

.tile .grip:hover { background-color: rgba(0, 0, 0, 0.8); border-color: var(--accent); }
.tile .grip:active { cursor: grabbing; }

#viewLive.sortieren { cursor: grabbing; }
#viewLive.sortieren .tile { user-select: none; -webkit-user-select: none; }

.tile.dragging { opacity: 0.4; }

.tile.droptarget {
  outline: 3px solid var(--accent);
  outline-offset: -3px;
  box-shadow: 0 0 24px rgba(61, 220, 132, 0.45) inset;
}

/* Bestätigung nach dem Tausch: nur Farbe, keine Layout-Bewegung — die würde das Bild stören */
@keyframes swapblitz {
  0% { box-shadow: 0 0 0 3px rgba(61, 220, 132, 0.95), 0 0 26px rgba(61, 220, 132, 0.6); }
  100% { box-shadow: 0 0 0 3px rgba(61, 220, 132, 0); }
}

.tile.swapped { animation: swapblitz 0.7s ease-out 1; }

/* Schwebende Beschriftung am Finger/Zeiger — zeigt, was gerade bewegt wird */
.dragghost {
  position: fixed;
  z-index: 300;
  pointer-events: none;    /* darf die Trefferprüfung unter dem Zeiger nicht stören */
  transform: translate(-50%, -170%);
  background: rgba(26, 33, 43, 0.96);
  border: 1px solid var(--accent);
  border-radius: 999px;
  padding: 7px 15px;
  font-size: 0.85rem;
  white-space: nowrap;
  box-shadow: 0 10px 26px rgba(0, 0, 0, 0.6);
}

@media (prefers-reduced-motion: reduce) {
  .tile.swapped { animation: none; outline: 3px solid var(--accent); outline-offset: -3px; }
}

/* ============ Bedienleisten der Zuschauer-Ansicht ============
   Liegen als Überblendung über dem Bild und erscheinen auf Tipp. --barh wird von
   viewer.js auf die gemessene Höhe der unteren Leiste gesetzt, damit sich Kamera-
   Werkzeuge und Zonen-Regler zuverlässig darüber legen — auch wenn sie umbrechen. */
.viewbar {
  position: absolute;
  left: 0;
  right: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 8px;
  /* Die Leiste selbst fängt keine Tipps ab — nur ihre Bedienelemente. Sonst wären
     das Bild darunter und der Zurück-Knopf der Großansicht nicht mehr erreichbar. */
  pointer-events: none;
  padding: 10px 12px;
  padding-left: max(12px, env(safe-area-inset-left));
  padding-right: max(12px, env(safe-area-inset-right));
  transition: opacity 0.2s ease, transform 0.2s ease;
}

.viewbar.top {
  top: 0;
  padding-top: max(10px, env(safe-area-inset-top));
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0.88), rgba(0, 0, 0, 0));
}

.viewbar.bottom {
  bottom: 0;
  padding-bottom: max(10px, env(safe-area-inset-bottom));
  background: linear-gradient(to top, rgba(0, 0, 0, 0.9), rgba(0, 0, 0, 0));
  flex-direction: column;
  gap: 6px;
}

.viewbar .barrow {
  display: flex;
  flex-wrap: nowrap;          /* nie umbrechen — sonst wächst die Leiste in die Höhe */
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  max-width: 100%;
  overflow-x: auto;           /* zu viele Knöpfe schieben sich zur Seite, nicht nach unten */
  scrollbar-width: none;
}

.viewbar .barrow::-webkit-scrollbar { display: none; }
.viewbar .barrow > * { flex-shrink: 0; }
.viewbar .barrow[hidden] { display: none; }

/* In der ÜBERSICHT nehmen die Leisten Platz ein, statt Kacheln zu verdecken —
   sie sind schmal genug. Erst in der Großansicht legen sie sich über das Bild
   und blenden weg, damit die ganze Fläche dem Video gehört. */
#viewLive:not(.sologan) .viewbar {
  position: static;
  background: rgba(0, 0, 0, 0.45);
  border-color: var(--border);
}

#viewLive:not(.sologan) #viewTop { order: -1; border-bottom: 1px solid var(--border); }
#viewLive:not(.sologan) #grid { order: 0; }
#viewLive:not(.sologan) #viewBottom { order: 1; border-top: 1px solid var(--border); }

/* Ausgeblendet, bis getippt wird — und dann ohne Klick-Sperre über dem Bild */
#viewLive:not(.bars) .viewbar { opacity: 0; pointer-events: none; }
#viewLive:not(.bars) .viewbar.top { transform: translateY(-10px); }
#viewLive:not(.bars) .viewbar.bottom { transform: translateY(10px); }

.viewbar button {
  padding: 9px 14px;
  font-size: 0.92rem;
  background: rgba(26, 33, 43, 0.82);
  backdrop-filter: blur(6px);
}

.viewbar .ic { font-style: normal; }
.viewbar .lbl { margin-left: 6px; }

.viewbar select.qsel {
  font: inherit;
  font-size: 0.85rem;
  width: auto;
  padding: 8px 8px;
  background: rgba(26, 33, 43, 0.82);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 10px;
}

.iconbtn {
  flex-shrink: 0;
  padding: 8px 13px;
  font-size: 1.05rem;
  line-height: 1;
  border-radius: 999px;
}

.viewbar > *,
.viewbar .barrow > * { pointer-events: auto; }

#viewTop #viewStatus {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  pointer-events: none;  /* reine Textanzeige, darf das Bild nicht blockieren */
}

/* Großansicht: die Kopfleiste zeigt den Kameranamen, deshalb entfällt das Schild
   in der Kachel — sonst stünde beides übereinander. Werte wandern nach unten links,
   der Zurück-Knopf über die Leisten, damit er nie unerreichbar wird. */
.tile.solo .label { display: none; }
.tile.solo .tileinfo {
  top: calc(var(--tarh, 52px) + 6px);   /* direkt unter die Kopfleiste, weg von den Werkzeugen */
  bottom: auto;
  left: 8px;
  right: auto;
}
#viewLive.sologan #viewVer { display: none; }

.chip.mini { font-size: 0.68rem; padding: 3px 8px; opacity: 0.65; flex-shrink: 0; }

/* Großansicht: die Kamera-Knöpfe rutschen über die untere Leiste (--barh ist deren
   gemessene Höhe, siehe messeBars in viewer.js). Am Desktop bleiben sie dabei fest
   unten rechts stehen und dauerhaft sichtbar — dort braucht es kein Antippen. */
.tile.solo .tools {
  bottom: calc(var(--barh, 66px) + 10px);
  z-index: 22;
}

/* Zonen-Regler nur in der Großansicht anheben — in der Übersicht sitzen sie
   ohnehin innerhalb ihrer Kachel und die Leisten überlagern nichts. */
.tile.solo .zctl { bottom: calc(var(--barh, 66px) + 10px); }

/* ============ Handy / Tablet / schmale Fenster ============
   WICHTIG: nicht nur nach Breite entscheiden. Ein iPhone Pro Max ist quer 956 px breit
   und bekäme sonst Desktop-Regeln (Bedienung nicht mehr per Tipp erreichbar, „ruhiger
   Blick" ohne Maus). `pointer: coarse` erkennt die Eingabeart — ein Windows-Rechner mit
   Touchscreen meldet weiterhin `fine`, weil dort die Maus der Hauptzeiger ist.
   Diese Bedingung muss identisch zu schmal() in js/viewer.js bleiben. */
@media (max-width: 899px), (pointer: coarse) {
  /* Beschriftungen weg, nur Symbole — sonst brechen sechs Knöpfe auf drei Zeilen um */
  .viewbar .lbl,
  .hidemobil { display: none; }

  .viewbar button { padding: 10px 12px; font-size: 1.02rem; }
  .viewbar.bottom { padding-top: 22px; }

  /* Das Rundgang-Intervall erscheint erst, wenn der Rundgang läuft —
     sonst kostet es dauerhaft eine Knopfbreite in einer ohnehin engen Zeile. */
  #viewLive:not(.tourrun) #tourSecs { display: none; }

  /* Übersicht: die Kacheln bleiben frei — Bild statt Knöpfe.
     Bedienung gibt es, sobald man eine Kamera groß öffnet. */
  #viewLive:not(.sologan) .tile .tools,
  #viewLive:not(.sologan) .tile .tileinfo { display: none; }


  .tile .label { font-size: 0.72rem; padding: 2px 8px; left: 6px; top: 6px; }
  .tile .tools button { padding: 9px 12px; font-size: 1rem; }
  .tile .tools select.qsel { padding: 8px 6px; }
  .tile .state { font-size: 0.82rem; padding: 8px; }

  /* Geräte-Anzeige ist Beiwerk und kostet in der Kopfleiste zu viel Platz */
  #onlinePanel { display: none; }


  #grid { gap: 4px; }
  .tile { border-radius: 8px; }

  .zone .zctl { gap: 6px; padding: 7px 8px; font-size: 0.75rem; }
  .zone .zctl input[type="range"] { flex: 1 1 70px; min-width: 60px; }
  .zone .zctl button { padding: 6px 8px; font-size: 0.75rem; }
}

/* ============ Bedienart: Finger statt Maus ============
   Bewusst getrennt von der Breite oben. Beides zu vermischen ging zweimal schief:
   ein iPhone Pro Max ist quer 956 px breit (bekam Maus-Verhalten, Antippen wirkungslos),
   und ein Desktop mit 150 % Browser-Zoom meldet unter 900 px (bekam Finger-Verhalten,
   der „ruhige Blick" fehlte). Hier zählt deshalb NUR, womit bedient wird.
   Muss identisch zu tippModus() in js/viewer.js bleiben. */
@media (hover: none), (pointer: coarse) {
  /* Ruhiger Blick braucht Mausbewegung, um zurückzukommen — ohne Maus wäre man ausgesperrt */
  #btnKino { display: none; }

  /* Dafür hier das Finger-Gegenstück: In der Übersicht die Leisten wegräumen, damit
     die Kacheln den ganzen Bildschirm bekommen. Zurück holt sie jeder Tipp aufs Bild. */
  #viewLive:not(.sologan) #btnBarsAus { display: block; }

  /* In der Großansicht erscheinen die Kamera-Knöpfe erst auf Tipp — und dann mittig über
     die volle Breite, weil sie sonst nicht nebeneinander passen. Die Messwerte oben links
     gehen mit: ein Tipp räumt das Bild ganz frei. */
  .tile.solo .tileinfo { display: none; }
  /* :not([hidden]) ist nötig — sonst überstimmt diese Regel das Ausblenden aus tickInfo */
  .tile.solo.showtools .tileinfo:not([hidden]) { display: block; }
  .tile.solo .tools { display: none; }
  .tile.solo.showtools .tools {
    display: flex;
    left: 6px;
    right: 6px;
    justify-content: center;
  }
}

/* Quer gehaltenes Handy: Hier ist Höhe das knappste Gut — volle Leisten fraßen ein
   Drittel des Bildschirms. Sie legen sich deshalb als schmale, schwebende Gruppe über
   das Bild (wie bei Überwachungs-Apps), statt Platz wegzunehmen. */
@media (max-width: 899px) and (orientation: landscape), (pointer: coarse) and (orientation: landscape) {
  #viewLive:not(.sologan) .viewbar { position: absolute; border: 0; background: none; }
  #viewLive:not(.sologan) #viewTop {
    background: linear-gradient(to bottom, rgba(0, 0, 0, 0.85), rgba(0, 0, 0, 0));
  }

  .viewbar { padding-top: 6px; padding-bottom: 6px; }
  .viewbar.bottom { padding-top: 6px; background: none; }
  .viewbar button { padding: 7px 11px; font-size: 0.98rem; }

  /* Die Knopfgruppe wird zur Pille — sie verdeckt nur ihre eigene Breite */
  .viewbar.bottom .barrow {
    width: auto;
    max-width: 100%;
    background: rgba(0, 0, 0, 0.6);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 5px 8px;
  }

  .viewbar.bottom .barrow.zonetools { margin-top: 4px; }

  /* Kameranamen nach unten: oben liegt hier die schwebende Kopfleiste darüber */
  #viewLive:not(.sologan) .tile .label { top: auto; bottom: 6px; }

  /* …dort sitzt aber auch der Sortier-Griff — der Name rückt dann daneben */
  #viewLive.mehrkameras:not(.sologan):not(.tourrun):not(.zonenmodus) .tile .label { left: 52px; }
}

.overlay {
  position: fixed;
  inset: 0;
  background: rgba(10,12,16,0.96);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 18px;
  text-align: center;
  padding: 24px;
  z-index: 50;
}

.overlay[hidden] { display: none; }

#blackout {
  position: fixed;
  inset: 0;
  background: #000;
  z-index: 100;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding-bottom: 40px;
}

#blackout[hidden] { display: none; }

#blackout p { color: #222; font-size: 0.85rem; }

.warn { color: #e8b34b; }
.ok { color: var(--accent); }

.settingsH { font-size: 1.05rem; margin: 26px 0 6px; }

/* QR-Code zum Einrichten des Einmal-Codes */
.qrbox {
  display: inline-block;
  background: #fff;
  padding: 14px 14px 6px;
  border-radius: 14px;
  margin: 10px 0;
  max-width: 100%;
}

.qrbox canvas { display: block; max-width: 100%; height: auto; image-rendering: pixelated; }
.qrbox .status { color: #333; margin: 8px 0 0; font-size: 0.82rem; max-width: 320px; }
.qrbox .btnrow { margin: 8px 0 4px; }
.qrbox button { background: #1a212b; font-size: 0.85rem; padding: 8px 12px; }

/* Ergebnis-Meldung der Einstellungen: fest im Sichtfeld, egal wo man scrollt */
#settingsMsg:not(:empty) {
  position: fixed;
  left: 50%;
  bottom: 18px;
  transform: translateX(-50%);
  z-index: 300;
  max-width: min(92vw, 560px);
  background: #1d2530;
  border: 1px solid var(--accent);
  color: var(--text);
  padding: 12px 18px;
  border-radius: 12px;
  font-size: 0.95rem;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.55);
  animation: toastein 0.25s ease-out;
}

button:disabled { opacity: 0.55; cursor: wait; }

/* Geräteliste in den Einstellungen */
.devrow {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  border: 1px solid var(--border);
  border-left-width: 5px;
  border-radius: 10px;
  margin: 8px 0;
  background: var(--panel);
}

.devrow button { flex-shrink: 0; }
.devrow .status { margin: 2px 0 0; font-size: 0.82rem; }

/* Ampel: grün = gerade aktiv · grau = angemeldet aber inaktiv
   orange = abgemeldet · rot = abgelaufen (beides braucht neuen Login) */
.devrow.st-live { border-left-color: var(--accent); }
.devrow.st-inaktiv { border-left-color: #7b8899; }
.devrow.st-abgemeldet { border-left-color: #e8934b; opacity: 0.85; }
.devrow.st-abgelaufen { border-left-color: var(--danger); opacity: 0.75; }

.ampel {
  display: inline-block;
  width: 11px; height: 11px;
  border-radius: 50%;
  margin-right: 8px;
  vertical-align: baseline;
}

.ampel.gruen { background: var(--accent); box-shadow: 0 0 8px rgba(61, 220, 132, 0.85); }
.ampel.grau { background: #7b8899; }
.ampel.orange { background: #e8934b; box-shadow: 0 0 6px rgba(232, 147, 75, 0.6); }
.ampel.rot { background: var(--danger); box-shadow: 0 0 6px rgba(224, 86, 79, 0.6); }

/* „Gerade online"-Anzeige im Zuschauer-Modus (klein, unauffällig) */
.onlinepanel {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
  margin-left: auto;
}

.onlinepanel .dev {
  font-size: 0.76rem;
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 2px 10px;
  white-space: nowrap;
}

.onlinepanel .dev.live { color: var(--text); border-color: var(--accent); }
.onlinepanel .dev.ich { background: rgba(61, 220, 132, 0.12); }
#settingsUI a { color: var(--accent); word-break: break-all; }
#settingsUI button { margin-top: 4px; }

code {
  background: var(--panel);
  border: 1px solid var(--border);
  padding: 2px 8px;
  border-radius: 6px;
  word-break: break-all;
}
