/* ==========================================================================
   Kwaba Sync – Cyber-Voodoo
   Palette: Void / Crypt / Loa-Violet / Spirit-Green / Ritual-Gold / Bone
   ========================================================================== */

/* Cinzel und JetBrains Mono sind die Voreinstellung. Weitere Schriften lädt
   public/js/theme.js bei Bedarf nach – nur die tatsächlich gewählte. */
@import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@500;700;900&family=JetBrains+Mono:wght@400;600&display=swap');

:root {
  --void:      #08050d;
  --crypt:     #120b1c;
  --crypt-2:   #1b1129;
  --veil:      #251838;
  --loa:       #7b3fa0;
  --loa-lit:   #b478e8;
  --spirit:    #3ff0b0;
  --spirit-dim:#1d8f6b;
  --gold:      #e8c37a;
  --bone:      #ded6ea;
  --ash:       #8d80a3;
  --blood:     #d5486b;

  /* Von public/js/theme.js überschreibbar */
  --display: 'Cinzel', 'Times New Roman', serif;
  --data: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  --body: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;

  --r: 3px;
  --glow-spirit: 0 0 12px rgba(63, 240, 176, .35);
  --glow-loa: 0 0 18px rgba(180, 120, 232, .28);
  --hairline: 1px solid rgba(180, 120, 232, .22);

  /* Von public/js/theme.js pro Gerät überschrieben. Schriftarten bleiben,
     geregelt werden Helligkeit, Schnitt und Grad. */
  --ui-title: var(--gold);
  --ui-title-weight: 700;
  --ui-text: var(--bone);
  --ui-dim: var(--ash);
  --ui-queue-size: .88rem;
}

* { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  background:
    radial-gradient(1100px 620px at 82% -8%, rgba(123, 63, 160, .30), transparent 62%),
    radial-gradient(760px 520px at 6% 104%, rgba(63, 240, 176, .10), transparent 60%),
    var(--void);
  /* Fix an den Viewport statt ans Dokument - sonst reichen die Gradienten auf
     langen Seiten (z.B. /admin) nicht bis unten und es gibt einen harten
     Schnitt, wo danach nur noch --void kommt. Aus demselben Grund ist auch
     die Veve-Textur unten (body::before) fixed. */
  background-attachment: fixed;
  color: var(--ui-text);
  font-family: var(--body);
  font-size: 15px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

/* Veve-Raster als ruhige Textur, nicht als Deko-Feuerwerk */
body::before {
  content: '';
  position: fixed; inset: 0;
  background-image:
    linear-gradient(rgba(180, 120, 232, .045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(180, 120, 232, .045) 1px, transparent 1px);
  background-size: 44px 44px;
  pointer-events: none;
  z-index: 0;
}

a { color: var(--spirit); text-decoration: none; }
a:hover { text-decoration: underline; }

::selection { background: rgba(180, 120, 232, .4); }

:focus-visible {
  outline: 2px solid var(--spirit);
  outline-offset: 2px;
}

/* ------------------------------------------------------------------ typography */

h1, h2, h3, .display {
  font-family: var(--display);
  font-weight: 700;
  letter-spacing: .06em;
  margin: 0;
}

h1 { font-size: clamp(1.6rem, 3.4vw, 2.5rem); color: var(--ui-title); font-weight: var(--ui-title-weight); }
h2 { font-size: 1.1rem; color: var(--loa-lit); text-transform: uppercase; letter-spacing: .16em; }

.eyebrow {
  font-family: var(--data);
  font-size: .68rem;
  letter-spacing: .28em;
  text-transform: uppercase;
  color: var(--ui-dim);
}

.mono { font-family: var(--data); }
.muted { color: var(--ui-dim); }
.gold { color: var(--gold); }
.spirit { color: var(--spirit); }

/* ---------------------------------------------------------------------- shell */

.wrap { position: relative; z-index: 1; max-width: 1600px; margin: 0 auto; padding: 28px 20px 64px; }

.topbar {
  position: relative; z-index: 3;
  display: flex; align-items: center; gap: 14px;
  padding: 10px 20px;
  background: linear-gradient(180deg, rgba(27, 17, 41, .96), rgba(18, 11, 28, .88));
  border-bottom: var(--hairline);
  backdrop-filter: blur(6px);
}
.topbar .brand {
  display: flex; align-items: center; gap: 11px; min-width: 0;
  color: var(--loa-lit); text-decoration: none;
  padding: 2px 6px 2px 2px; border-radius: var(--r);
  transition: background .15s;
}
.topbar .brand:hover { background: rgba(180, 120, 232, .14); text-decoration: none; }
.topbar .brand:hover b { color: #f3dcae; }
.topbar .brand svg { width: 30px; height: 30px; flex: 0 0 auto; }
.topbar .brand b {
  font-family: var(--display); font-size: 1.02rem; letter-spacing: .2em;
  color: var(--ui-title); font-weight: var(--ui-title-weight);
  text-transform: uppercase; white-space: nowrap;
}
.topbar .sub { font-family: var(--data); font-size: .68rem; color: var(--ui-dim); letter-spacing: .12em; }

/* Raumname in der Kopfzeile: klein und in Mono, darum eine Stufe kräftiger */
.topbar .room-tag {
  font-family: var(--data); font-size: .8rem; font-weight: 600;
  letter-spacing: .06em; color: var(--ui-title);
  max-width: 28ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.topbar .spacer { flex: 1; }
.topbar nav { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* Menü neben dem Namen (js/common.js mountTopbarMenu): Overlay unter dem Knopf, rechtsbuendig */
.tb-menu-wrap { position: relative; }
.tb-menu {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 20;
  display: flex; flex-direction: column; gap: 6px; min-width: 180px; padding: 8px;
  background: linear-gradient(180deg, #1b1129, var(--crypt));
  border: var(--hairline); border-radius: var(--r); box-shadow: var(--glow-loa);
}
.tb-menu[hidden] { display: none; }
#tbMenuBtn[hidden] { display: none; }
.tb-menu .btn { justify-content: flex-start; width: 100%; }

/* Termine in der Mitte der Lobby-Leiste (js/topbar-events.js; mountTopbar({ mid: true }) legt .topbar-mid an). Das Band liegt
   mittig ueber der Leiste, --mid-max (vom Skript gesetzt) haelt es zwischen Marke und Knoepfen. Bleibt dafuer zu wenig Platz
   (.mid-row), steht es zentriert in einer eigenen Zeile darunter. Schriften nur aus den Theme-Variablen ("Aa"). */
.topbar-mid { display: none; }
.topbar.has-mid .topbar-mid:not([hidden]) {
  display: flex; position: absolute; left: 50%; top: 0; bottom: 0; transform: translateX(-50%);
  align-items: center; justify-content: center; max-width: var(--mid-max, 0px); overflow: hidden;
}
.topbar.mid-row { flex-wrap: wrap; row-gap: 8px; }
.topbar.mid-row .spacer { display: none; }
.topbar.mid-row nav { flex: 1 1 0; min-width: 0; justify-content: flex-end; }
.topbar.mid-row .topbar-mid:not([hidden]) { position: static; transform: none; flex: 0 0 100%; order: 3; max-width: 100%; }
.tb-events { display: flex; align-items: center; gap: 14px; min-width: 0; }
.tb-seg { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.tb-events.tight .tb-seg { flex: 0 1 auto; min-width: 0; }
.tb-sep { flex: 0 0 auto; align-self: stretch; width: 1px; margin: 4px 0; background: rgba(180, 120, 232, .3); }
.tb-label { font-family: var(--data); font-size: .66rem; letter-spacing: .16em; text-transform: uppercase; color: var(--ui-dim); white-space: nowrap; }
.tb-when { flex: 0 0 auto; font-family: var(--data); font-size: .7rem; color: var(--ui-dim); white-space: nowrap; }
.tb-ev, .tb-count {
  flex: 0 0 auto; max-width: 22ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: pointer;
  padding: 3px 9px; font: inherit; font-family: var(--body); font-size: .8rem; color: var(--ui-text);
  background: rgba(8, 5, 13, .5); border: 1px solid rgba(180, 120, 232, .18); border-left: 3px solid var(--k, var(--gold)); border-radius: var(--r);
}
.tb-count { max-width: none; }
.tb-events.tight .tb-ev { flex: 0 1 auto; min-width: 0; }
.tb-ev:hover, .tb-ev:focus-visible, .tb-count:hover, .tb-count:focus-visible { border-color: var(--loa-lit); border-left-color: var(--k, var(--gold)); outline: none; }
.tb-ev b { margin-right: 3px; font-family: var(--data); font-weight: 600; color: var(--k); }
.tb-count b { font-family: var(--data); font-weight: 600; color: var(--ui-title); }

/* --------------------------------------------------------------------- panels */

.panel {
  background: linear-gradient(180deg, rgba(27, 17, 41, .82), rgba(15, 9, 23, .82));
  border: var(--hairline);
  border-radius: var(--r);
  padding: 18px;
  position: relative;
}
.panel::after {
  /* Ritual-Gold-Ecke oben links: markiert jede Kammer */
  content: ''; position: absolute; top: 0; left: 0;
  width: 14px; height: 14px;
  border-top: 2px solid var(--gold); border-left: 2px solid var(--gold);
  opacity: .55;
}
.panel > header { display: flex; align-items: baseline; gap: 12px; margin-bottom: 14px; }
.panel > header .spacer { flex: 1; }

/* auto-fit statt fester Breakpoints: die Spalten werden so breit wie der
   verfuegbare Platz erlaubt und fallen erst zusammen, wenn selbst eine
   Mindestbreite nicht mehr passt - keine Sprungstellen, reagiert auf jede
   Fenstergroesse (Handy bis Ultrawide), nicht nur auf zwei feste Stufen. */
.grid-2 { display: grid; gap: 18px; grid-template-columns: repeat(auto-fit, minmax(min(380px, 100%), 1fr)); }

/* Lobby mit Livestream-Panel: eine dritte Spalte links von "Räume", nicht
   verschmolzen/als Tab - siehe Feature-Spezifikation Screenshare v2. */
.grid-3 { display: grid; gap: 18px; grid-template-columns: repeat(auto-fit, minmax(min(360px, 100%), 1fr)); }

/* --------------------------------------------------------------------- inputs */

label.field { display: block; margin-bottom: 12px; }
label.field span {
  display: block; font-family: var(--data); font-size: .68rem;
  letter-spacing: .18em; text-transform: uppercase; color: var(--ui-dim); margin-bottom: 5px;
}

input[type=text], input[type=password], input[type=search], input[type=number], select, textarea {
  width: 100%;
  padding: 10px 12px;
  background: rgba(8, 5, 13, .85);
  border: 1px solid rgba(180, 120, 232, .28);
  border-radius: var(--r);
  color: var(--ui-text);
  font-family: var(--body);
  font-size: .95rem;
}
input:focus, select:focus, textarea:focus {
  border-color: var(--spirit);
  box-shadow: var(--glow-spirit);
  outline: none;
}
input::placeholder { color: #5d5270; }
/* Browser-eigene Spinner-Pfeile passen nicht zum Theme und werden ohnehin
   durch die min/max/step-Grenzen der Felder selbst ersetzt. */
input[type=number]::-webkit-inner-spin-button, input[type=number]::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
input[type=number] { -moz-appearance: textfield; }

.btn {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 9px 15px;
  font-family: var(--data); font-size: .78rem; letter-spacing: .12em; text-transform: uppercase;
  background: rgba(123, 63, 160, .18);
  color: var(--bone);
  border: 1px solid rgba(180, 120, 232, .45);
  border-radius: var(--r);
  cursor: pointer;
  transition: background .15s, border-color .15s, transform .1s, box-shadow .15s;
}
.btn.keep-case { text-transform: none; }   /* z. B. "−5s"/"+5s": Einheit klein lassen */
.btn:hover:not(:disabled) { background: rgba(180, 120, 232, .3); border-color: var(--loa-lit); text-decoration: none; }
.btn:active:not(:disabled) { transform: translateY(1px); }
.btn:disabled { opacity: .4; cursor: not-allowed; }
.btn.primary {
  background: linear-gradient(180deg, rgba(63, 240, 176, .22), rgba(29, 143, 107, .22));
  border-color: var(--spirit); color: #d7fff0;
}
.btn.primary:hover:not(:disabled) { background: rgba(63, 240, 176, .34); box-shadow: var(--glow-spirit); }
.btn.gold { border-color: var(--gold); color: var(--gold); background: rgba(232, 195, 122, .1); }
.btn.danger { border-color: var(--blood); color: #ffc9d6; background: rgba(213, 72, 107, .12); }
.btn.ghost { background: none; border-color: rgba(180, 120, 232, .25); }
.btn.sm { padding: 5px 9px; font-size: .68rem; }
.btn.icon { padding: 6px 8px; }

.row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.row.tight { gap: 6px; }

.switch { display: flex; align-items: center; gap: 8px; font-size: .85rem; color: var(--ui-dim); cursor: pointer; }
.switch input { accent-color: var(--spirit); width: 16px; height: 16px; }

.notice {
  padding: 10px 12px; border-radius: var(--r); font-size: .88rem;
  border-left: 3px solid var(--blood); background: rgba(213, 72, 107, .12); color: #ffd3de;
}
.notice.ok { border-left-color: var(--spirit); background: rgba(63, 240, 176, .1); color: #cffbec; }
.notice.hidden { display: none; }

/* -------------------------------------------------------------- login / lobby */

.gate { min-height: 100%; display: grid; place-items: center; padding: 40px 18px; }
.gate .card { width: 100%; max-width: 430px; }
.gate .sigil { display: grid; place-items: center; margin-bottom: 18px; }
.gate .sigil svg { width: 96px; height: 96px; filter: drop-shadow(0 0 16px rgba(180, 120, 232, .45)); }
.gate h1 { text-align: center; margin-bottom: 4px; }
.gate .tagline { text-align: center; margin-bottom: 22px; }
.tabs { display: flex; gap: 0; margin-bottom: 18px; border-bottom: var(--hairline); }
.tabs button {
  flex: 1; padding: 9px 0; background: none; border: none; border-bottom: 2px solid transparent;
  color: var(--ui-dim); font-family: var(--data); font-size: .74rem; letter-spacing: .18em;
  text-transform: uppercase; cursor: pointer;
}
.tabs button[aria-selected=true] { color: var(--ui-title); border-bottom-color: var(--ui-title); }

.room-list { display: grid; gap: 10px; }
/* Lobby: Karten duerfen so schmal werden wie ihre Spalte. Ohne Spaltenbreite bestimmte der Inhalt (langer Name +
   Knoepfe) die Mindestbreite der Spur, die Karte lief ueber den Kasten hinaus und lag unter der Nachbarspalte
   (nur in bestimmten Fensterbreiten). Der Name bricht dafuer notfalls mitten im Wort. Bewusst nur .grid-3 (Lobby):
   die Liste des TV-Modus (tv-rooms.html) bleibt unberuehrt. */
.grid-3 .room-list { grid-template-columns: minmax(0, 1fr); }
.grid-3 .room-card .name, .grid-3 .room-card .meta { overflow-wrap: anywhere; }
.grid-3 .panel > header { flex-wrap: wrap; row-gap: 6px; }   /* Kopf mit langer Ueberschrift + Knopf umbricht auf schmalen Bildschirmen statt ueberzulaufen */
/* Karten der Lobby (Raeume, Playlists; Livestreams siehe .stream-card): Badges und Knoepfe bilden eine Gruppe rechts.
   Reicht die Breite nicht, rutscht die Gruppe rechtsbuendig unter den Namen - der Name behaelt mindestens ca. 150 px
   und wird nicht auf ein paar Buchstaben je Zeile gequetscht (z. B. wenn ein Raum "Passwort" und "N live" traegt).
   Ohne Badges und bei genug Platz bleibt alles in einer Zeile wie bisher. */
.grid-3 .room-card { flex-wrap: wrap; row-gap: 8px; }
.grid-3 .room-card .grow { flex: 1 1 150px; }
.grid-3 .room-card .card-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: center; gap: 8px; margin-left: auto; max-width: 100%; }

/* ------------------------------------------------------------- Kalender (Lobby) */
/* Kleine Monatsansicht im Panel, dieselbe Ansicht gross im Overlay (.cal-big), Termine eines Tages und das Formular
   ebenfalls als Overlay (js/calendar.js). Farbe je Art ueber --k: Watch-Session violett, Livestream gruen, Sonstiges gold. */
.k-watch { --k: var(--loa-lit); }
.k-stream { --k: var(--spirit); }
.k-other { --k: var(--gold); }
.k-birthday { --k: var(--blood); }   /* Geburtstage: Rosenrot aus der Palette */


/* Lobby-Dashboard (public/js/dashboard.js): feste Spalten (3/2/1 je nach Breite), Kacheln je Konto anordenbar.
   Verschieben/Hinzufuegen/Entfernen nur im Modus "Anpassen" (.dash.editing), einklappen immer. */
.dash-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
.dash-bar .spacer { flex: 1; }
.dash-edit-tools { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.dash-edit-tools[hidden] { display: none; }
.dash-add-menu {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 30;
  display: flex; flex-direction: column; align-items: stretch; gap: 6px; min-width: 200px; padding: 10px;
  background: var(--crypt-2); box-shadow: 0 8px 24px rgba(0, 0, 0, .55);
}
.dash-add-menu[hidden] { display: none; }
.dash-add-menu .btn { justify-content: flex-start; }
.dash { grid-template-columns: repeat(var(--dash-cols, 3), minmax(0, 1fr)); align-items: start; }
.dash:not(.ready) > .tile { display: none; }
/* minmax(0,1fr): sonst bestimmt der Inhalt die Spurbreite und die Spalte schiebt sich unter die Nachbarspalte */
.dash-col { display: grid; grid-template-columns: minmax(0, 1fr); gap: 18px; align-content: start; min-width: 0; min-height: 60px; }
.dash-empty { padding: 18px; }
.tile-edit { display: none; flex-basis: 100%; align-items: center; justify-content: flex-end; gap: 6px; }
.dash.editing .tile-edit { display: flex; }
.tile-edit .btn.icon { font-size: .9rem; line-height: 1; padding: 5px 9px; }
.tile-grip { cursor: grab; color: var(--ash); padding: 0 4px; font-size: 1.1rem; user-select: none; margin-right: auto; }
.tile-fold {
  background: none; border: 0; padding: 0 2px; margin-right: -4px; cursor: pointer;
  color: var(--ash); font-size: 1.05rem; line-height: 1; min-width: 1.1em; align-self: center;
}
.tile-fold:hover, .tile-fold:focus-visible { color: var(--loa-lit); }
.tile.collapsed > :not(header) { display: none; }
.tile.collapsed > header { margin-bottom: 0; }
.dash.editing .dash-col { outline: 1px dashed rgba(180, 120, 232, .35); outline-offset: 6px; border-radius: var(--r); }
.dash.editing .tile { cursor: grab; }
.dash.editing .tile input, .dash.editing .tile select, .dash.editing .tile textarea { cursor: auto; }
.tile.dragging { opacity: .45; }
.cal { min-width: 0; }
.cal-num { line-height: 1; }
.cal-nav { display: flex; align-items: center; gap: 6px; margin-bottom: 8px; }
.cal-title {
  flex: 1; min-width: 0; text-align: center;
  font-family: var(--display); font-size: .98rem; letter-spacing: .05em;
  color: var(--ui-title); font-weight: var(--ui-title-weight);
}
.cal-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 3px; }
.cal-dow {
  padding: 2px 0 4px; text-align: center;
  font-family: var(--data); font-size: .6rem; letter-spacing: .14em; text-transform: uppercase; color: var(--ui-dim);
}
.cal-day {
  position: relative; min-width: 0; aspect-ratio: 8 / 7; padding: 0; cursor: pointer;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  font-family: var(--data); font-size: .76rem; color: var(--ui-text);
  background: rgba(8, 5, 13, .45); border: 1px solid transparent; border-radius: var(--r);
  transition: border-color .15s, background .15s;
}
.cal-day:hover, .cal-day:focus-visible { border-color: var(--loa-lit); background: rgba(37, 24, 56, .7); outline: none; }
.cal-day.other { opacity: .35; }
.cal-day.has { background: rgba(123, 63, 160, .24); border-color: rgba(180, 120, 232, .38); }
.cal-day.today { color: var(--gold); border-color: var(--gold); }
.cal-dots { display: flex; gap: 3px; height: 5px; }
.cal-dot { width: 5px; height: 5px; border-radius: 50%; background: var(--k); box-shadow: 0 0 6px var(--k); }

.cal-next { margin-top: 14px; display: grid; grid-template-columns: minmax(0, 1fr); gap: 6px; }   /* minmax(0,1fr): ein langer Titel darf die Spalte nicht aufziehen */
.cal-next .empty { padding: 4px 0; }
.cal-up {
  display: flex; align-items: baseline; gap: 10px; width: 100%; padding: 7px 10px; text-align: left; cursor: pointer;
  font: inherit; color: var(--ui-text);
  background: rgba(8, 5, 13, .5); border: 1px solid rgba(180, 120, 232, .18); border-left: 3px solid var(--k); border-radius: var(--r);
}
.cal-up:hover, .cal-up:focus-visible { border-color: var(--loa-lit); border-left-color: var(--k); outline: none; }
.cal-up-when { flex: 0 0 auto; font-family: var(--data); font-size: .7rem; color: var(--ui-dim); white-space: nowrap; }
.cal-up-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: .88rem; }

/* grosse Monatsansicht (Overlay) */
.overlay .sheet > header { flex-wrap: wrap; row-gap: 6px; }   /* Titel + Knoepfe duerfen umbrechen, die Knoepfe selbst nicht */
.overlay .sheet > header .btn { white-space: nowrap; }
.overlay .sheet.cal-sheet { width: min(1040px, 100%); }   /* .overlay .sheet steht weiter unten und wuerde sonst gewinnen */
.cal-big { gap: 4px; }
.cal-big .cal-day {
  aspect-ratio: auto; min-height: 104px; padding: 6px 7px; gap: 4px;
  align-items: stretch; justify-content: flex-start; text-align: left; font-size: .82rem;
}
.cal-big .cal-dots { display: none; }
.cal-chip {
  display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 1px 5px;
  font-family: var(--body); font-size: .68rem; color: var(--ui-text);
  background: rgba(8, 5, 13, .55); border-left: 2px solid var(--k); border-radius: 2px;
}
.cal-chip b { margin-right: 3px; font-family: var(--data); font-weight: 600; color: var(--k); }
.cal-more { font-family: var(--data); font-size: .62rem; color: var(--ui-dim); }
@media (max-width: 640px) {   /* schmal: keine Titel in den Feldern, nur die Punkte */
  .cal-big .cal-day { min-height: 52px; align-items: center; justify-content: center; }
  .cal-big .cal-chip, .cal-big .cal-more { display: none; }
  .cal-big .cal-dots { display: flex; }
}

/* Termine eines Tages */
.cal-day-list { display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; align-content: start; }
.cal-ev { padding: 12px 14px; background: rgba(8, 5, 13, .5); border: 1px solid rgba(180, 120, 232, .22); border-left: 3px solid var(--k); border-radius: var(--r); }
.cal-ev-head { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; font-family: var(--data); font-size: .7rem; letter-spacing: .1em; text-transform: uppercase; }
.cal-kind { color: var(--k); }
.cal-ev time { color: var(--ui-dim); white-space: nowrap; }
.cal-ev h3 { margin: 6px 0 2px; font: var(--ui-title-weight) 1.05rem var(--display); color: var(--ui-title); overflow-wrap: anywhere; }
.cal-desc { margin: 8px 0 0; font-size: .9rem; white-space: pre-wrap; overflow-wrap: anywhere; }

/* Formular */
.ev-form { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; align-content: start; }
.ev-form .field { margin: 0; }
.ev-times { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px; }
.ev-hint { margin: -4px 0 0; font-size: .76rem; }
.ev-bday { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 12px; }
.ev-bday .ev-hint { grid-column: 1 / -1; }
.ev-form input[type=datetime-local], .ev-form select { color-scheme: dark; }   /* Auswahlfenster des Browsers passend zum dunklen Theme */
/* Die allgemeine Feldgestaltung (oben) kennt datetime-local nicht - hier dieselbe, damit die Felder zum Rest passen */
.ev-form input[type=datetime-local] {
  width: 100%; min-width: 0; padding: 10px 12px;
  background: rgba(8, 5, 13, .85); border: 1px solid rgba(180, 120, 232, .28); border-radius: var(--r);
  color: var(--ui-text); font-family: var(--body); font-size: .95rem;
}
.ev-actions { margin-top: 4px; }
.room-card {
  display: flex; align-items: center; gap: 14px;
  padding: 13px 14px;
  border: var(--hairline); border-radius: var(--r);
  background: rgba(8, 5, 13, .5);
  transition: border-color .15s, background .15s;
}
.room-card:hover { border-color: var(--loa-lit); background: rgba(37, 24, 56, .6); }
.room-card .name {
  font-family: var(--display); font-size: 1.08rem;
  color: var(--ui-title); font-weight: var(--ui-title-weight);
}
.room-card .meta { font-family: var(--data); font-size: .72rem; color: var(--ui-dim); letter-spacing: .06em; }
.room-card .grow { flex: 1; min-width: 0; }
.room-card .thumb { width: 72px; height: 40px; object-fit: cover; border-radius: 4px; background: #000; flex: 0 0 auto; border: var(--hairline); }

/* Livestream-Karte in der Lobby: Vorschaubild, Name und Meta wie bei den Raeumen, Badge und Buttons als eigene
   Gruppe rechts. Reicht die Breite nicht fuer alles in einer Zeile, rutscht die Gruppe rechtsbuendig unter den
   Namen, statt ihn auf ein Wort pro Zeile zu quetschen. Nur fuer die Lobby - .room-card selbst (auch im TV-Modus)
   bleibt unberuehrt. */
.stream-card { flex-wrap: wrap; row-gap: 10px; }
.stream-card .grow { flex: 1 1 150px; }
.stream-card .card-actions { display: flex; align-items: center; gap: 8px; margin-left: auto; }

.badge {
  font-family: var(--data); font-size: .62rem; letter-spacing: .14em; text-transform: uppercase;
  padding: 2px 6px; border-radius: 2px;
  border: 1px solid rgba(180, 120, 232, .4); color: var(--loa-lit);
}
.badge.live { border-color: var(--spirit); color: var(--spirit); box-shadow: var(--glow-spirit); }
.badge.lock { border-color: var(--gold); color: var(--gold); }

/* ----------------------------------------------------------------- room layout */

/* Die Raumseite scrollt nicht: Buehne und Bedienleiste muessen immer sichtbar sein,
   gescrollt wird nur innerhalb von Playlist und Chat. */
body.room-page { height: 100dvh; overflow: hidden; display: flex; flex-direction: column; }
[hidden] { display: none !important; }

/* ------------------------------------------------------------ live-room layout */
/* Eigene, einfachere Grid-Variante fuer Livestream-Raeume: Buehne + Chat, keine
   Playlist-Spalte (siehe live.html/live.js). Chat und Steuerung lassen sich wie in den
   normalen Raeumen einklappen, dazu gibt es Kinomodus und Vollbild: die Griffe
   (.side-toggle/.fold-toggle) und das Kino-Aussehen (body.cinema ...) teilen sich die
   Regeln mit der Raumseite, hier steht nur, was am Raster der Livestream-Seite haengt. */
body.live-page { height: 100dvh; overflow: hidden; display: flex; flex-direction: column; }
.live-shell {
  position: relative; z-index: 1;
  flex: 1 1 auto; min-height: 0; overflow: hidden;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 18px clamp(340px, 27vw, 480px);
  grid-template-rows: minmax(0, 1fr);   /* die Zeile waechst nie ueber das Fenster hinaus (sonst schieben Panels den Chat) */
  gap: 10px;
  padding: 14px;
  align-items: stretch;
}
body.side-hidden .live-shell { grid-template-columns: minmax(0, 1fr) 18px; }
/* Unter dem Video liegen Steuerleiste und (fuer den Streamer) die Sendeeinstellungen. Das Video behaelt einen
   Mindestanteil der Fensterhoehe; reicht der Rest nicht, scrollt dieser Bereich, statt ueber den Chat zu ragen. */
/* Bühne links, Chat rechts: feste Spalten, damit ein ausgeblendeter Griff nichts verschiebt */
.live-shell > .stage { grid-column: 1; }
.live-shell > .side { grid-column: 3; }
.live-shell .screen-fit { min-height: clamp(140px, 42vh, 520px); }
.stage-under { display: flex; flex-direction: column; gap: 10px; flex: 0 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; scrollbar-color: var(--loa) transparent; }
.stage-under > .panel { flex: 0 0 auto; }
@media (max-width: 860px) {
  /* Schmal: normaler Seitenfluss wie auf der Raumseite - die Seite scrollt, nichts liegt uebereinander. Ohne Griffe,
     alles sichtbar (display ausdruecklich statt "revert", weil .side und .controls hier Flex-Container sind).
     Im Kinomodus bleibt das Video fensterfuellend. */
  body.live-page:not(.cinema) { height: auto; min-height: 100dvh; overflow: auto; }
  body.live-page:not(.cinema) .live-shell,
  body.live-page.side-hidden:not(.cinema) .live-shell { grid-template-columns: 1fr; grid-template-rows: none; overflow: visible; flex: 0 0 auto; }
  body.live-page:not(.cinema) .screen-fit { aspect-ratio: 16 / 9; flex: none; min-height: 0; }
  body.live-page:not(.cinema) .stage-under { overflow: visible; flex: none; }
  body.live-page:not(.cinema) .live-shell > .side { grid-column: 1; }
  body.live-page:not(.cinema) .side { min-height: 0; }
  body.live-page:not(.cinema) #chatlog { flex: none; height: 40vh; }
  body.live-page .side-toggle, body.live-page .fold-toggle { display: none !important; }
  body.live-page.side-hidden .side, body.live-page.controls-hidden .controls { display: flex; }
  body.live-page.controls-hidden #qualityPanel { display: block; }
  body.live-page:not(.cinema).controls-hidden .stage-under { display: flex; }
}
/* Kinomodus: nur das Video (Topbar, Chat, Leiste blenden die gemeinsamen body.cinema-Regeln aus) */
body.cinema .live-shell { grid-template-columns: minmax(0, 1fr); padding: 0; gap: 0; }
/* Die Sendeeinstellungen des Streamers gehoeren zur Steuerung */
body.controls-hidden #qualityPanel, body.cinema #qualityPanel { display: none; }
body.controls-hidden .stage-under, body.cinema .stage-under { display: none; }   /* sonst bliebe ein leerer Zwischenraum */
/* Knoepfe fuer Vollbild/Kino/Steuerung: bleiben zusammen, wenn die Leiste umbricht */
.live-actions .view-btns { display: flex; flex-wrap: wrap; gap: 6px; }
/* Titel und Knopfleiste standen nebeneinander gedacht (siehe live.html), aber
   .panel ist von Haus aus kein Flex-Container - ohne das hier faellt
   "justify-content" dort ins Leere und beides stapelt sich ohne Abstand. */
.live-shell .controls { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px 16px; }

/* Leiste unter dem Video: links Titel (schlichter Text, keine Box) mit Status und Zuschauerzahl, rechts die
   Bedienelemente in einer Reihe. Vorher fuellte die Qualitaets-Auswahl eine ganze Zeile und drueckte
   Lautstaerke und Zuschauerzahl darueber. Die .now-Regeln der Raumseite bleiben unberuehrt. */
.live-shell .now { flex: 1 1 220px; min-width: 0; flex-wrap: wrap; align-items: baseline; gap: 2px 12px; padding: 0; border: 0; background: none; }
.live-shell .now .title { max-width: 100%; }
.live-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 8px 16px; margin-left: auto; }
.live-actions .vol { display: flex; align-items: center; gap: 6px; }
.live-actions .vol input { width: 96px; vertical-align: middle; }
.live-actions .qsel { display: flex; align-items: center; gap: 8px; }
.live-actions .qsel:has(select[hidden]) { display: none; }   /* Beschriftung nur mit sichtbarer Auswahl */
.live-actions select { width: auto; max-width: 100%; padding: 6px 10px; font-size: .85rem; }

/* Sendeeinstellungen des Streamers: zwei beschriftete Spalten (Bild | Ton), die auf schmalem Bildschirm
   untereinander rutschen. Auswahlfelder fuellen ihre Spalte, damit lange Namen (z. B. Programme) nicht abgeschnitten
   werden; Zustand und Hilfe stehen klein darunter. */
.send-panel { padding: 12px 14px; }
.send-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr)); gap: 14px 28px; }
.send-group { display: flex; flex-direction: column; align-items: stretch; gap: 8px; min-width: 0; }
.send-group > .eyebrow { margin: 0; }
.send-group select { width: 100%; padding: 7px 10px; font-size: .9rem; }
.send-custom { display: flex; flex-wrap: wrap; gap: 8px 14px; }
.send-custom .field { margin: 0; flex: 1 1 100px; }
.send-note { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 12px; font-size: .82rem; }
.send-note:not(:has(> :not([hidden]))) { display: none; }   /* nichts zu sagen -> kein leerer Zeilenabstand */
.send-encoder { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; font-size: .82rem; }   /* Encoder-Status und Schalter (nur Desktop-App) */

.room-shell {
  position: relative; z-index: 1;
  flex: 1 1 auto; min-height: 0; overflow: hidden;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 18px clamp(340px, 27vw, 480px);
  gap: 10px;
  padding: 14px;
  align-items: stretch;
}

.stage { position: relative; display: flex; flex-direction: column; gap: 10px; min-width: 0; min-height: 0; }

/* Haelt das 16:9-Fenster mittig und so gross wie der freie Platz erlaubt */
.screen-fit { position: relative; flex: 1 1 auto; min-height: 140px; }

.screen {
  position: absolute; inset: 0; margin: auto;
  aspect-ratio: 16 / 9;
  max-width: 100%; max-height: 100%;
  background: #000;
  border: var(--hairline);
  border-radius: var(--r);
  overflow: hidden;
  box-shadow: 0 0 0 1px rgba(8, 5, 13, .9), 0 18px 60px rgba(0, 0, 0, .6);
}

/* Beide Wiedergabe-Ebenen behalten immer ihre Groesse. Ein YT-Player, der in einem
   display:none-Element erzeugt wird, bleibt schwarz – darum visibility statt display. */
.screen .layer { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; display: block; }
.screen .layer.off { visibility: hidden; pointer-events: none; }
.screen .layer > div, .screen .layer iframe { width: 100%; height: 100%; border: 0; display: block; }
.screen video.layer { object-fit: contain; background: #000; }

.screen .veil {
  position: absolute; inset: 0; z-index: 2;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px;
  background: radial-gradient(circle at 50% 45%, rgba(37, 24, 56, .94), rgba(8, 5, 13, .98));
  text-align: center; padding: 20px;
}
.screen .veil.hidden { display: none; }
.screen .veil svg { width: clamp(64px, 14vh, 120px); height: clamp(64px, 14vh, 120px); opacity: .8; }
.screen .veil p { max-width: 44ch; color: var(--ui-dim); font-size: .9rem; margin: 0; }
/* Klickfang, damit die YouTube-Oberflaeche die Synchronisation nicht unterlaeuft.
   Wer die YouTube-Bedienelemente einblendet, braucht ihn nicht – sonst kaeme man
   nicht an das Zahnrad fuer die Qualitaet. */
.screen .shield { position: absolute; inset: 0; z-index: 1; background: transparent; }
.screen .shield.off { display: none; }

/* Der Detailschub vergroessert den iframe und skaliert ihn per CSS zurueck.
   Das Ueberstehende muss verschwinden. */
.screen { contain: paint; }

/* Eigene Vorschau beim Senden (Livestream-Raum): Discord zeigt dem Streamenden auch nur ein kleines
   Bild, keinen vollen Render seines eigenen Streams - das Zusammensetzen eines grossflaechigen Videos
   kostet GPU/CPU-Zeit, die genau dann fehlt, wenn ohnehin schon aufgenommen und kodiert wird. Die
   Bilddaten fliessen unveraendert (die Statistik/das Thumbnail lesen weiter aus dem echten Frame),
   nur die Anzeigegroesse wird klein - #liveVideo bleibt technisch der volle Strom. */
.screen.self-sending #liveVideo,
.screen.self-sending #liveNativePreview {
  position: absolute; z-index: 3; inset: auto 14px 14px auto; margin: 0;
  width: min(220px, 34%); height: auto; aspect-ratio: 16 / 9;
  border-radius: 8px; border: 1px solid rgba(180, 120, 232, .45);
  box-shadow: 0 8px 24px rgba(0, 0, 0, .55);
}
/* Native Aufnahme (kwaba-video): es gibt gar keinen MediaStream zum Anzeigen - #liveVideo bleibt
   deshalb komplett leer/versteckt, die kleine Vorschau kommt direkt vom Hilfsprogramm (siehe
   video-bridge.js) in dieses Canvas. */
.screen.self-sending.native-capture #liveVideo { display: none; }
.screen:not(.native-capture) #liveNativePreview { display: none; }
#liveSelfNotice p { max-width: 34ch; }

/* Twitch-artiges Statistik-Overlay im Livestream: klein, oben links, stoert
   das Bild nicht - siehe live.js updateStats(). */
.stream-stats-btn { position: absolute; top: 8px; left: 8px; z-index: 2; font-size: .68rem; padding: 3px 9px; opacity: .8; }
.stream-stats-btn:hover { opacity: 1; }
.stream-stats {
  position: absolute; top: 38px; left: 8px; z-index: 2;
  background: rgba(8, 5, 13, .88); border: var(--hairline); border-radius: var(--r);
  padding: 8px 10px; font-family: var(--data); font-size: .68rem; line-height: 1.7;
  color: var(--ui-text); min-width: 140px; pointer-events: none;
}
/* Autoplay mit Ton ist ohne vorherigen Klick gesperrt: Bild laeuft stumm, hier wird der Ton freigegeben. */
.stream-unmute { position: absolute; bottom: 12px; left: 50%; transform: translateX(-50%); z-index: 2; }
.stream-stats .row { display: flex; justify-content: space-between; gap: 10px; }
.stream-stats .k { color: var(--ui-dim); }

.now {
  display: flex; gap: 12px; align-items: center; flex: 0 0 auto;
  padding: 9px 12px; border: var(--hairline); border-radius: var(--r);
  background: rgba(27, 17, 41, .7);
}
.now .title {
  font-family: var(--display); font-size: 1.05rem;
  color: var(--ui-title); font-weight: var(--ui-title-weight);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.now .who { font-family: var(--data); font-size: .7rem; color: var(--ui-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.now .grow { flex: 1; min-width: 0; }

.controls { flex: 0 0 auto; padding: 10px 12px; }

.bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* "⋯"-Menue unter dem Video: seltener gebrauchte Knoepfe, klappt nach oben auf (die Leiste sitzt unten). */
.more-wrap { position: relative; }
.more-menu {
  position: absolute; bottom: calc(100% + 8px); left: 0; z-index: 30;
  display: flex; flex-direction: column; align-items: stretch; gap: 6px;
  min-width: 190px; padding: 10px;
}
.more-menu .btn { justify-content: flex-start; }
.more-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; font-size: .78rem; color: var(--ui-dim); }
.seek { flex: 1; min-width: 140px; }
/* Zeit unter der Maus bzw. Zielzeit beim Ziehen ueber der Leiste (js/room.js showSeekTip); fixed, damit sie das
   Flex-Layout der Leiste nicht verschiebt - left/top setzt das Skript auf Maus-x und Oberkante der Leiste */
.seek-tip {
  position: fixed; z-index: 40; transform: translate(-50%, calc(-100% - 6px)); pointer-events: none;
  padding: 2px 6px; border: var(--hairline); border-radius: var(--r); background: var(--crypt);
  font-family: var(--data); font-size: .76rem; color: var(--ui-text); white-space: nowrap;
}
.seek-tip[hidden] { display: none; }
input[type=range] { accent-color: var(--loa-lit); width: 100%; }
.time { font-family: var(--data); font-size: .76rem; color: var(--ui-dim); white-space: nowrap; }

/* Signature: der Sync-Kreis. Zeigt Fortschritt und Zustand ohne Text.
   pathLength=100 macht stroke-dashoffset direkt zur Prozentangabe. Die Drehung
   um -90 Grad sitzt auf einer Gruppe – eine CSS-Animation auf dem Bogen selbst
   wuerde das transform-Attribut ueberschreiben und den Ring verspringen lassen. */
.circle { width: 40px; height: 40px; flex: 0 0 auto; overflow: visible; }
.circle .ring { fill: none; stroke-width: 3; stroke-linecap: butt; }
.circle .track { stroke: rgba(180, 120, 232, .18); }
.circle .arc { stroke: var(--spirit); transition: stroke .3s, stroke-dashoffset .45s linear; }
.circle .pip { fill: var(--spirit); transition: fill .3s; }
.circle.drift .arc, .circle.drift .pip { stroke: var(--gold); fill: var(--gold); }
.circle.bad .arc, .circle.bad .pip { stroke: var(--blood); fill: var(--blood); }
.circle.paused .arc, .circle.paused .pip { stroke: var(--loa-lit); fill: var(--loa-lit); }
.circle.spin .pip { animation: pip 2.2s ease-in-out infinite; }
@keyframes pip { 50% { opacity: .3; } }
@media (prefers-reduced-motion: reduce) { .circle.spin .pip { animation: none; } }

/* Schmale Fenster und Hochformat: wieder normaler Seitenfluss. Nur die Raumseite; die Livestream-Seite hat ihr eigenes
   Raster (siehe "live-room layout") und bleibt bis 860 px zweispaltig. Ohne den Vorsatz wanderte dort der Chat in die
   18-px-Griffspalte und das Video wurde auf feste 16:9-Hoehe gezwungen. :where() hat keine Spezifitaet, damit die
   Regeln auf der Raumseite genau so gegen die uebrigen ankommen wie vorher. */
@media (max-width: 1080px) {
  body.room-page { height: auto; overflow: auto; }
  .room-shell, body.side-hidden .room-shell { grid-template-columns: 1fr; overflow: visible; }
  :where(.room-page) .side-toggle, :where(.room-page) .fold-toggle { display: none !important; }
  body.side-hidden:where(.room-page) .side, body.controls-hidden:where(.room-page) .controls, body.controls-hidden:where(.room-page) .now { display: revert; }
  :where(.room-page) .screen-fit { aspect-ratio: 16 / 9; flex: none; }
  :where(.room-page) .side > .panel { max-height: none; }
  :where(.room-page) #queue { max-height: 46vh; }
  :where(.room-page) #chatlog { height: 40vh; }
}

/* ------------------------------------------------------------------- sidebar */

.side { display: flex; flex-direction: column; min-width: 0; min-height: 0; container-type: inline-size; }
.side > .panel { padding: 13px; flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }

#paneQueue, #paneChat { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
#paneRoom { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding-right: 4px; }

.pill-tabs { display: flex; gap: 6px; margin-bottom: 10px; flex: 0 0 auto; }
.pill-tabs button {
  flex: 1; padding: 6px 4px; background: rgba(8, 5, 13, .6);
  border: 1px solid rgba(180, 120, 232, .2); border-radius: var(--r);
  color: var(--ui-dim); font-family: var(--data); font-size: .69rem; letter-spacing: .1em;
  text-transform: uppercase; cursor: pointer;
}
.pill-tabs button[aria-selected=true] { color: var(--ui-title); border-color: var(--ui-title); background: rgba(232, 195, 122, .12); }

/* Scrollbalken im Theme: schmal, in Loa-Violett - fuer die ganze Seite und jeden scrollenden Bereich, nicht nur
   fuer .scroll. Sonst erscheint dort der grelle Standard-Balken des Browsers bzw. der Desktop-App.
   scrollbar-color wird vererbt; der Balken der Seite selbst richtet sich nach :root. Browser ohne diese
   Eigenschaften (aeltere WebKit) bekommen die -webkit-Fassung. */
:root { scrollbar-width: thin; scrollbar-color: var(--loa) transparent; }
@supports not (scrollbar-color: auto) {
  ::-webkit-scrollbar { width: 8px; height: 8px; }
  ::-webkit-scrollbar-track { background: transparent; }
  ::-webkit-scrollbar-thumb { background: rgba(123, 63, 160, .6); border-radius: 4px; }
}

.scroll { overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; scrollbar-color: var(--loa) transparent; }
.scroll::-webkit-scrollbar { width: 8px; }
.scroll::-webkit-scrollbar-thumb { background: rgba(123, 63, 160, .6); border-radius: 4px; }

#queue { flex: 1 1 auto; min-height: 90px; }
#chatlog { flex: 1 1 auto; min-height: 90px; }

.q-item {
  display: flex; gap: 9px; align-items: center;
  padding: 6px; border-radius: var(--r);
  border: 1px solid transparent;
  cursor: pointer; contain: layout style;
}
.q-item:hover { background: rgba(37, 24, 56, .8); border-color: rgba(180, 120, 232, .3); }
.q-item.current {
  background: rgba(63, 240, 176, .13);
  border-color: rgba(63, 240, 176, .55);
  box-shadow: inset 3px 0 0 var(--spirit);
  padding-left: 9px;
}
.q-item.current .t { color: #e8fff6; }
.q-item.current .n { color: var(--spirit); font-weight: 600; }
.q-item.dragging { opacity: .4; }
.q-item.drop-above { border-top-color: var(--gold); }
.q-item .n { font-family: var(--data); font-size: .68rem; color: var(--ui-dim); width: 1.8em; text-align: right; flex: 0 0 auto; }
.q-item .thumb { width: 56px; height: 32px; object-fit: cover; border-radius: 2px; background: #000; flex: 0 0 auto; }
.q-item .tt { flex: 1; min-width: 0; display: flex; flex-direction: row; align-items: baseline; gap: 8px; }
.q-item .t { flex: 1; min-width: 0; font-size: var(--ui-queue-size); color: var(--ui-text); line-height: 1.32; max-height: 2.7em; overflow: hidden; }
.q-item .d { font-family: var(--data); font-size: .68rem; color: var(--ui-dim); flex: 0 0 auto; }
.q-item .x { opacity: 0; flex: 0 0 auto; }
.q-item:hover .x { opacity: 1; }
/* Browse-Cursor im TV-Modus: DPAD hoch/runter bewegt diese Markierung durch
   die Playlist, unabhaengig vom tatsaechlich laufenden Video (.current) -
   beide Klassen koennen gleichzeitig auf demselben Eintrag sitzen. */
.q-item.tv-highlight { outline: 2px solid var(--gold); outline-offset: -2px; }
.q-item.blocked { opacity: .45; }
/* Schon geschaut (fuer den ganzen Raum): etwas zurueckgenommen, Haken an der Nummer */
.q-item.watched { opacity: .6; }
.q-item.watched .n::after { content: '✓'; color: var(--spirit); margin-left: 2px; }
.q-item.blocked .t { text-decoration: line-through; text-decoration-color: var(--blood); }
.q-item.blocked .n::after { content: '!'; color: var(--blood); margin-left: 2px; }

/* Die Seitenspalte ist im Desktop-Zweispaltenlayout immer nur 320-400px breit
   (siehe .room-shell), unabhaengig von der Fenster-/Bildschirmgroesse - eine
   Fensterbreiten-Media-Query wuerde das nie erfassen. Container-Query auf die
   tatsaechliche Breite der Seitenspalte reagiert korrekt in beiden Layouts.
   Muss NACH den obigen Basis-Regeln stehen: gleiche Spezifitaet, sonst
   gewinnt bei einer Cascade-Bindung die spaeter stehende Basis-Regel. */
@container (max-width: 440px) {
  .q-item { padding: 5px 4px; gap: 7px; }
  .q-item .tt { flex-direction: column; align-items: flex-start; gap: 1px; }
}

.who-list { display: flex; flex-wrap: wrap; gap: 6px; }
/* Benutzerverwaltung: Invite-Flag hinter dem Namen */
.invite-flag { margin-left: 6px; padding: 1px 7px; border: var(--hairline); border-radius: 999px; font-size: .72rem; color: var(--ui-dim); white-space: nowrap; }
.who-chip {
  display: flex; align-items: center; gap: 6px;
  font-family: var(--data); font-size: .72rem; color: var(--ui-text);
  padding: 3px 8px; border-radius: 12px;
  background: rgba(8, 5, 13, .6); border: 1px solid rgba(180, 120, 232, .3);
}
.who-chip .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--spirit); box-shadow: var(--glow-spirit); flex: 0 0 auto; }
.who-chip.buffering .dot { background: var(--gold); box-shadow: none; animation: pulse 1s ease-in-out infinite; }
/* Livestream: wer gerade sendet */
.who-chip.sending { border-color: var(--blood); }
.who-chip.sending .dot { background: var(--blood); box-shadow: none; }
@keyframes pulse { 50% { opacity: .25; } }

.msg { margin-bottom: 7px; font-size: .87rem; word-wrap: break-word; }
.msg .u { font-family: var(--data); font-size: .72rem; color: var(--loa-lit); margin-right: 6px; }
.msg.system { color: var(--ui-dim); font-style: italic; font-size: .82rem; }
.msg.mine .u { color: var(--spirit); }

/* --------------------------------------------------------------------- toasts */

#toasts {
  position: fixed; bottom: 18px; left: 50%; transform: translateX(-50%);
  z-index: 50; display: grid; gap: 8px; justify-items: center; pointer-events: none;
}
.toast {
  padding: 9px 15px; border-radius: var(--r);
  background: rgba(18, 11, 28, .96); border: 1px solid var(--loa-lit);
  font-family: var(--data); font-size: .76rem; letter-spacing: .04em;
  box-shadow: var(--glow-loa); animation: rise .25s ease-out;
}
.toast.ok { border-color: var(--spirit); color: #cffbec; }
.toast.err { border-color: var(--blood); color: #ffd3de; }
.toast.wait { border-color: var(--gold); color: var(--gold); }
.toast.info { border-color: rgba(180, 120, 232, .55); color: var(--bone); box-shadow: none; opacity: .93; }
@keyframes rise { from { opacity: 0; transform: translateY(8px); } }

/* -------------------------------------------------------------------- overlay */

/* Raumwechsel und Einstellungen ohne die Seite zu verlassen: die Wiedergabe
   laeuft weiter, der Hintergrund wird nur unscharf gelegt. */
.overlay {
  position: fixed; inset: 0; z-index: 55;
  display: grid; place-items: center; padding: 24px;
  background: rgba(8, 5, 13, .72);
  backdrop-filter: blur(10px) saturate(.7);
  animation: fadein .18s ease-out;
}
.overlay[hidden] { display: none !important; }
.overlay .sheet { width: min(680px, 100%); max-height: 82vh; display: flex; flex-direction: column; }
.overlay .sheet > .scroll { min-height: 0; }
@keyframes fadein { from { opacity: 0; } }

/* Anleitung fuer das Hilfsprogramm "Kwaba Audio" (Livestream-Raeume) */
.audio-help { line-height: 1.5; overflow-y: auto; }
.audio-help ol { padding-left: 1.3em; display: grid; gap: 12px; }
.audio-help li::marker { color: var(--loa-lit); font-weight: 700; }
.audio-help .audio-cmd {
  display: block; margin-top: 6px; padding: 7px 10px; cursor: copy;
  font-family: var(--data); font-size: .8rem; overflow-wrap: anywhere;
  background: rgba(8, 5, 13, .6); border: 1px solid rgba(180, 120, 232, .25); border-radius: 6px;
}
.audio-help .audio-cmd:hover { border-color: var(--loa-lit); }

/* Download-Fenster der Lobby (nutzt das Aussehen der Kwaba-Audio-Anleitung): Knopf + Hinweis je Datei */
.audio-help h3 { margin: 14px 0 4px; font: 700 .95rem var(--display); color: var(--ui-title); }
.dl-list { display: grid; gap: 8px; margin: 12px 0 6px; }
.dl-row { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; }
.dl-row .btn { flex: 0 0 auto; width: 270px; max-width: 100%; justify-content: center; }
.dl-code { font-family: var(--data); font-size: .8rem; padding: 1px 6px; border: var(--hairline); border-radius: 3px; background: rgba(8, 5, 13, .85); cursor: copy; }
@media (prefers-reduced-motion: reduce) { .overlay { animation: none; } }

/* ------------------------------------------------------ Darstellungs-Dialog */

.opt-group { padding: 12px 0; border-bottom: 1px solid rgba(180, 120, 232, .12); }
.opt-group:last-child { border-bottom: none; }
.opt-head { margin-bottom: 8px; }
.opt-head b {
  font-family: var(--display); font-size: .92rem; letter-spacing: .08em;
  color: var(--ui-title); font-weight: var(--ui-title-weight);
}
.opt-head span { display: block; font-size: .78rem; color: var(--ui-dim); }
.opt-row { display: flex; flex-wrap: wrap; gap: 6px; }
.opt-row .btn { flex-direction: column; align-items: flex-start; gap: 1px; line-height: 1.25; }
.opt-ratio { font-size: .62rem; opacity: .65; letter-spacing: .04em; text-transform: none; }

.opt-preview {
  padding: 12px; border: var(--hairline); border-radius: var(--r);
  background: rgba(8, 5, 13, .55);
}
.preview-title {
  font-family: var(--display); font-size: 1.08rem;
  color: var(--ui-title); font-weight: var(--ui-title-weight);
}
.preview-meta {
  font-family: var(--data); font-size: .72rem; letter-spacing: .06em;
  color: var(--ui-dim); margin: 2px 0 9px;
}
.preview-item {
  display: flex; gap: 9px; align-items: baseline;
  font-size: var(--ui-queue-size); color: var(--ui-text);
}
.preview-n { font-family: var(--data); font-size: .68rem; color: var(--ui-dim); }

/* ------------------------------------------------------------ Falt-Griffe */

.side-toggle, .fold-toggle {
  display: flex; align-items: center; justify-content: center;
  padding: 0; cursor: pointer;
  color: var(--ui-dim);
  background: rgba(27, 17, 41, .6);
  border: 1px solid rgba(180, 120, 232, .18);
  border-radius: var(--r);
  transition: color .15s, background .15s, border-color .15s;
}
.side-toggle:hover, .fold-toggle:hover {
  color: var(--ui-title);
  background: rgba(180, 120, 232, .18);
  border-color: rgba(180, 120, 232, .45);
}
/* Als SVG statt Schriftglyphe: nur so sitzt der Pfeil exakt mittig, unabhaengig
   von den Metriken der geladenen Schrift. */
.side-toggle svg, .fold-toggle svg {
  width: 14px; height: 14px; display: block;
  fill: none; stroke: currentColor; stroke-width: 2.4;
  stroke-linecap: round; stroke-linejoin: round;
  transition: transform .2s;
}

/* Playlist-Griff: eigene Rasterspalte, gleicher Abstand nach beiden Seiten */
.side-toggle { grid-column: 2; align-self: stretch; width: 18px; }
.side-toggle svg { transform: rotate(0deg); }
body.side-hidden .side-toggle svg { transform: rotate(180deg); }
body.side-hidden .room-shell { grid-template-columns: minmax(0, 1fr) 18px; }
body.side-hidden .side { display: none; }

/* Die Leiste unten erscheint nur, wenn die Steuerung eingeklappt ist */
.fold-toggle { flex: 0 0 auto; height: 16px; width: 100%; display: none; }
.fold-toggle svg { transform: rotate(-90deg); }
body.controls-hidden .fold-toggle { display: flex; }
body.controls-hidden .now,
body.controls-hidden .controls { display: none; }

/* ------------------------------------------------------------- Kinomodus */
/* Nur das Video, auf die Fenstergroesse eingepasst. Die Bedienung blendet sich
   bei Mausbewegung kurz ein und verschwindet wieder. */
body.cinema { background: #000; cursor: none; }
body.cinema.cinema-ui { cursor: auto; }
body.cinema::before { display: none; }
body.cinema .topbar,
body.cinema .side,
body.cinema .side-toggle,
body.cinema .now,
body.cinema .controls,
body.cinema .fold-toggle { display: none; }
body.cinema .room-shell { grid-template-columns: minmax(0, 1fr); padding: 0; gap: 0; }
body.cinema .stage { gap: 0; }
body.cinema .screen-fit { min-height: 0; }
body.cinema .screen { border: 0; border-radius: 0; box-shadow: none; }

.cinema-exit {
  position: fixed; top: 14px; right: 14px; z-index: 60;
  display: none; padding: 8px 14px;
  font-family: var(--data); font-size: .72rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--bone); cursor: pointer;
  background: rgba(18, 11, 28, .9);
  border: 1px solid rgba(180, 120, 232, .45); border-radius: var(--r);
  opacity: 0; transition: opacity .25s;
}
body.cinema .cinema-exit { display: block; }
body.cinema.cinema-ui .cinema-exit { opacity: 1; }
.cinema-exit:hover { border-color: var(--spirit); color: #d7fff0; }

/* --------------------------------------------------------------- TV-Modus */
/* 10-Fuss-Layout fuer Fire-TV/Android-TV: fester 60/40-Split, kein Banner,
   Playlist und Hotbar dauerhaft sichtbar, groessere Schrift/Buttons fuer
   Sofa-Abstand. Reine Layout-Variante der normalen Raumseite - siehe
   docs/superpowers/specs/2026-09-15-tv-mode-design.md. Muss nach den
   Basis-Regeln stehen, die sie ueberschreibt (gleiche Spezifitaet: .side
   Grid-Spalte, .controls .btn Basis-Groesse), sonst gewinnt bei einer
   Cascade-Bindung die spaeter stehende Basis-Regel. */
/* Topbar bleibt sichtbar, aber reduziert auf Logo+Marke und Nutzer/Punkte -
   Raumwechsel/Einladung/Darstellung/Verwaltung/Abmelden sind vom Sofa aus
   ohnehin nicht gedacht (Login ist einmalig, siehe Fixed Product Decisions). */
body.tv .topbar .sub,
body.tv .topbar #roomName,
body.tv .topbar #bInvite,
body.tv .topbar #bRooms,
body.tv .topbar #appearance,
body.tv .topbar a[href="/admin"],
body.tv .topbar #logout,
body.tv .topbar #verBadge {
  display: none !important;
}
body.tv .room-shell,
body.tv.side-hidden .room-shell {
  grid-template-columns: minmax(0, 4fr) minmax(280px, 1fr);
}
body.tv .side-toggle, body.tv .fold-toggle { display: none !important; }
/* Nur die Playlist wird im TV-Modus gebraucht - Chat ist ausgeschlossen
   (siehe body.tv #tabChat unten), Raum-Einstellungen sind vom Sofa aus
   nicht bedienbar. Mit nur einer verbleibenden Ansicht braucht es keine
   Tab-Leiste mehr; #paneQueue ist ohnehin die Standardansicht. */
body.tv .pill-tabs { display: none !important; }
/* Ohne Tab-Leiste steht sonst nirgends mehr, dass das hier die Playlist ist. */
body.tv .tv-playlist-title { display: block !important; font-size: 1.15rem; font-family: var(--display); color: var(--ui-title); margin: 0 0 10px; }
/* Hinzufuegen/Suchen/Zufall/Mischen/Leeren sind am Sofa nicht vorgesehen
   (keine Tastatur, keine Notwendigkeit fuer Playlist-Verwaltung per Fernbedienung). */
body.tv #rowAddUrl, body.tv #rowFilter, body.tv #rowPlaylistTools { display: none !important; }
/* Die Zuschauerliste zieht per JS ans Ende der Playlist-Ansicht um (statt in
   die Hotbar) - siehe room.js. Hier nur die Optik fuer die neue Position. */
body.tv #who { margin-top: 10px; padding-top: 10px; border-top: var(--hairline); }
/* Skip-Stimmen-Hinweis in der Zuschauerliste - nur sichtbar, solange eine
   Abstimmung laeuft (skipVotes > 0), siehe renderPresence() in room.js. */
.who-chip.skip-badge { border-color: var(--gold); color: var(--gold); }

body.tv.room-page { height: 100dvh; overflow: hidden; }
body.tv .room-shell { overflow: hidden; }
body.tv .screen-fit { aspect-ratio: auto; flex: 1 1 auto; }
body.tv #queue { max-height: none; }
/* body.cinema .room-shell (oben, "Kinomodus") und body.tv .room-shell (eine
   Zeile drueber) haben dieselbe Spezifitaet - ohne diese Regel gewinnt bei
   beiden Klassen gleichzeitig (seit "Vollbild" im TV-Modus per Kinomodus
   statt echtem Fullscreen laeuft, siehe bFs.onclick) die spaeter stehende
   Basisregel body.tv, und die Video-Spalte bleibt auf 4fr/5 statt auf volle
   Breite stehen - der Rest der Spalte bleibt als leerer Streifen sichtbar,
   das Video wirkt nach links verschoben. */
body.tv.cinema .room-shell { grid-template-columns: minmax(0, 1fr); }

body.tv .now .title { font-size: 1.6rem; }
body.tv .now .who { font-size: .95rem; }
body.tv .controls { padding: 16px 18px; }
body.tv .controls .btn { font-size: 1.05rem; padding: 12px 18px; }
body.tv .controls .btn.icon { padding: 12px 16px; }
body.tv .time { font-size: 1rem; }

/* Diese sechs Aktionen wandern im TV-Modus ins Menue-Popup (Task 4) statt in
   der Hotbar zu stehen - siehe Fixed Product Decisions im Spec. Lautstaerke-
   Regler und Tempo-Auswahl entfallen im TV-Modus ganz (Lautstaerke laeuft
   ueber Fernseher/AVR, Tempo ist keine MUSS-Funktion). bHideCtrl/bCinema
   werden verborgen, weil die zugehoerigen Tastenkuerzel unten deaktiviert
   werden - eine sichtbare Schaltflaeche fuer eine deaktivierte Funktion waere
   irrefuehrend. */
body.tv #bResync, body.tv #bMute, body.tv #bCC, body.tv #bFs, body.tv #bSkip,
body.tv #bSpoiler, body.tv #bCinema, body.tv #bHideCtrl,
body.tv #vol, body.tv #speed, body.tv #tabChat {
  display: none !important;
}

.tv-menu-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; margin-top: 14px; }
.tv-menu-grid .btn { font-size: 1.05rem; padding: 14px; justify-content: center; }

/* ----------------------------------------------------------- TV-Raumliste */
/* Reine Layout-Seite fuer den TV-Modus (siehe
   docs/superpowers/specs/2026-09-16-tv-room-list-design.md). body ist hier
   direkt fest auf class="tv" gesetzt, nicht bedingt wie bei room.html - die
   Seite hat kein Desktop-Aequivalent, das dieselbe Datei teilt. */
.tv-rooms-wrap { padding-top: 24px; }
.tv-rooms-wrap h2 { font-size: 1.6rem; margin-bottom: 16px; }
.room-card[tabindex] { cursor: pointer; }
.room-card:focus-visible {
  outline: none;
  border-color: var(--spirit);
  background: rgba(63, 240, 176, .1);
  box-shadow: var(--glow-spirit);
}
body.tv .room-card .name { font-size: 1.2rem; }
body.tv .room-card .meta { font-size: .82rem; }
.tv-unlock-form { display: flex; gap: 8px; margin-top: 10px; }
.tv-unlock-form input { flex: 1; min-width: 0; }

/* --------------------------------------------------------- Spoilerschutz */

.q-item.veiled .t { display: flex; align-items: center; }
.q-item.veiled .veil-bar {
  display: block; width: 100%; height: 1em; border-radius: 2px;
  background: repeating-linear-gradient(
    115deg, rgba(180, 120, 232, .30) 0 7px, rgba(37, 24, 56, .85) 7px 14px);
}
.q-item.veiled .thumb {
  background: rgba(37, 24, 56, .9);
  border: 1px dashed rgba(180, 120, 232, .35);
}
.q-item.veiled:hover .veil-bar { background: repeating-linear-gradient(
    115deg, rgba(232, 195, 122, .35) 0 7px, rgba(37, 24, 56, .85) 7px 14px); }
.q-item.veiled .d { opacity: .5; }

/* -------------------------------------------------------- Gasteinladungen */

.invite-list { display: grid; gap: 6px; }
.invite-row {
  display: flex; align-items: center; gap: 6px;
  padding: 6px 8px; border: var(--hairline); border-radius: var(--r);
  background: rgba(8, 5, 13, .5);
}
.invite-row .grow { flex: 1; min-width: 0; }
.invite-meta { font-family: var(--data); font-size: .68rem; color: var(--ui-dim); letter-spacing: .04em; }
.invite-token { font-family: var(--data); font-size: .66rem; color: var(--loa-lit); }

.invite-box {
  padding: 13px; margin-bottom: 18px;
  border: var(--hairline); border-radius: var(--r);
  background: rgba(8, 5, 13, .55);
}
.invite-room {
  font-family: var(--display); font-size: 1.15rem;
  color: var(--ui-title); font-weight: var(--ui-title-weight);
}
.invite-label { font-size: .84rem; color: var(--ui-dim); margin-top: 2px; }
.invite-facts {
  margin: 10px 0 0; padding-left: 1.1em;
  font-size: .82rem; color: var(--ui-text); line-height: 1.7;
}
.invite-facts b { color: var(--gold); font-weight: 600; }

/* --------------------------------------------------------------- Punkte */

.pts {
  display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--data); font-size: .74rem; letter-spacing: .06em;
  color: var(--gold); white-space: nowrap;
}
.pts svg { width: 11px; height: 11px; fill: currentColor; }
.pts.bump { animation: pts-bump .5s ease-out; }
@keyframes pts-bump { 40% { transform: scale(1.16); color: var(--spirit); } }
@media (prefers-reduced-motion: reduce) { .pts.bump { animation: none; } }

.board { display: grid; gap: 3px; }
.board-row {
  display: flex; align-items: baseline; gap: 8px;
  padding: 3px 5px; border-radius: 2px; font-size: .85rem;
}
.board-row.me { background: rgba(63, 240, 176, .1); }
.board-row .r { font-family: var(--data); font-size: .68rem; color: var(--ui-dim); width: 2em; text-align: right; }
.board-row .u { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.board-row .p { font-family: var(--data); font-size: .74rem; color: var(--gold); }
.board-mine { margin-top: 9px; font-family: var(--data); font-size: .7rem; color: var(--ui-dim); }

.guest-badge {
  font-family: var(--data); font-size: .62rem; letter-spacing: .12em; text-transform: uppercase;
  padding: 2px 6px; border-radius: 2px;
  border: 1px solid var(--gold); color: var(--gold);
}

.preview-body { font-size: .84rem; color: var(--ui-text); margin-top: 5px; }

/* --------------------------------------------------------------------- tables */

table.data { width: 100%; border-collapse: collapse; font-size: .87rem; }
table.data th {
  text-align: left; font-family: var(--data); font-size: .66rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--ui-dim); padding: 7px 8px; border-bottom: var(--hairline);
}
table.data td { padding: 7px 8px; border-bottom: 1px solid rgba(180, 120, 232, .1); }
table.data tr:hover td { background: rgba(37, 24, 56, .5); }

kbd {
  font-family: var(--data); font-size: .7rem; padding: 1px 5px;
  border: 1px solid rgba(180, 120, 232, .4); border-radius: 2px;
  background: rgba(8, 5, 13, .7); color: var(--ui-text);
}

.empty { padding: 26px 14px; text-align: center; color: var(--ui-dim); font-size: .88rem; }

/* --------------------------------------------------------------------- footer */
/* Admin-gepflegt (Tagline, Status, Link-Spalten, Links unten) über /admin,
   siehe mountFooter() in common.js. Nicht auf der Raum-Seite eingebunden -
   die bleibt kompakt und ist Terrain des TV-Modus. */

.site-footer {
  position: relative; z-index: 1;
  margin-top: 40px;
  border-top: var(--hairline);
  background: linear-gradient(180deg, rgba(18, 11, 28, .35), rgba(8, 5, 13, .9));
}
.footer-inner {
  max-width: 1120px; margin: 0 auto;
  padding: 36px 20px 22px;
  display: flex; flex-wrap: wrap; gap: 36px;
}
.footer-brand { flex: 1 1 220px; min-width: 200px; }
.footer-brand b {
  font-family: var(--display); font-size: 1.05rem; letter-spacing: .18em;
  color: var(--ui-title); font-weight: var(--ui-title-weight); text-transform: uppercase;
}
.footer-tagline { margin: 9px 0 14px; color: var(--ui-dim); font-size: .84rem; max-width: 32ch; }

.status-pill {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 12px; margin-top: 4px;
  border-radius: 999px; border: 1px solid currentColor;
  font-family: var(--data); font-size: .7rem; font-weight: 600; letter-spacing: .04em;
}
.status-pill i { width: 7px; height: 7px; border-radius: 50%; background: currentColor; box-shadow: 0 0 8px currentColor; }
.status-pill.status-operational { color: var(--spirit); }
.status-pill.status-degraded { color: var(--gold); }
.status-pill.status-maintenance { color: var(--loa-lit); }
.status-pill.status-down { color: var(--blood); }

.footer-groups { flex: 3 1 480px; display: flex; flex-wrap: wrap; gap: 28px; }
.footer-group { flex: 1 1 140px; min-width: 130px; }
.footer-group h4 {
  font-family: var(--data); font-size: .66rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--loa-lit); margin: 0 0 10px;
}
.footer-group ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 7px; }
.footer-group a { color: var(--ui-dim); font-size: .84rem; }
.footer-group a:hover { color: var(--bone); text-decoration: none; }

.footer-bottom {
  max-width: 1120px; margin: 0 auto;
  padding: 14px 20px 26px;
  border-top: 1px solid rgba(180, 120, 232, .12);
  display: flex; flex-wrap: wrap; gap: 12px; align-items: center; justify-content: space-between;
}
.footer-copy { font-size: .74rem; color: var(--ui-dim); }
.footer-bottom-links { display: flex; gap: 16px; flex-wrap: wrap; }
.footer-bottom-links a { font-size: .74rem; color: var(--ui-dim); }
.footer-bottom-links a:hover { color: var(--bone); text-decoration: none; }
