/* ============================================================================
   SongWunschApp v1.31.29 | 2026-10-01
   File: style.css | Last modified: 2026-10-01
   ============================================================================ */

:root {
  --bg: #0f0f14;
  --bg-elevated: #1a1a24;
  --bg-card: #201f2b;
  --border: #322f42;
  --text: #f2f1f7;
  --text-dim: #a8a5b8;
  --accent: #8b5cf6;
  --accent-hover: #7c3aed;
  --accent-text: #ffffff;
  --success: #22c55e;
  --danger: #ef4444;
  --warn: #f59e0b;
  --radius: 12px;
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

/* Heller Modus (siehe Design-Tab / app.js applyTheme()) - ueberschreibt nur
   die Basis-Palette, --accent/--accent-hover kommen weiterhin dynamisch von
   applyTheme() (funktioniert in beiden Modi identisch). html[data-theme]
   wird schon vor dem eigentlichen Login/Render gesetzt (siehe boot() ->
   loadTheme()), damit auch oeffentliche Besucher sofort den richtigen Modus
   sehen und es nicht kurz "dunkel aufblitzt". */
html[data-theme="light"] {
  --bg: #f4f3f9;
  --bg-elevated: #ffffff;
  --bg-card: #ffffff;
  --border: #ddd8ea;
  --text: #1c1a24;
  --text-dim: #6b6780;
}

/* Automatisch (Auto) Mode: Respektiere System-Einstellung wenn kein data-theme gesetzt */
@media (prefers-color-scheme: light) {
  html:not([data-theme]) {
    --bg: #f4f3f9;
    --bg-elevated: #ffffff;
    --bg-card: #ffffff;
    --border: #ddd8ea;
    --text: #1c1a24;
    --text-dim: #6b6780;
  }
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  -webkit-font-smoothing: antialiased;
}

body {
  min-height: 100vh;
  padding-bottom: env(safe-area-inset-bottom);
}

/* v1.7.1: Die Seitenbreite ist einstellbar (Einstellungen -> Ansichten).
   Sie liegt rein im Browser jedes Einzelnen - wer am 27-Zoeller sitzt,
   verstellt damit niemandem am Laptop die Ansicht.

   Eine Handy-Sonderregel braucht es NICHT: max-width greift nur, wenn der
   Bildschirm groesser ist. Auf dem Telefon ist die Seite so oder so
   randvoll. */
#app {
  max-width: var(--seiten-breite, 1200px);
  margin: 0 auto;
  /* v1.7.2: Der Abstand nach oben war 16 px plus die 12 px der alten
     Kopfzeile - zusammen fast eine halbe Bildschirmzeile auf dem Telefon,
     ohne dass er etwas leistet. Halbiert; seitlich bleibt es, sonst kleben
     die Karten am Rand. */
  padding: 8px 14px 16px;
  padding-top: max(8px, env(safe-area-inset-top));
}

/* Fliesstext waechst NICHT mit. Ueber ~90 Zeichen verliert das Auge beim
   Zeilenwechsel die Spur - Tabellen und Listen gewinnen durch Breite,
   Absaetze verlieren. */
#app > .card > p,
#app > .card > .hint {
  max-width: 78ch;
}

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

h1, h2, h3 { margin: 0 0 8px; font-weight: 700; }
h1 { font-size: 22px; }
h2 { font-size: 18px; }
p { margin: 0 0 12px; color: var(--text-dim); line-height: 1.5; }

/* ==========================================================================
   v1.7.2 - Kopfzeile
   ==========================================================================
   Die alte .header war EINE Flex-Zeile ohne Umbruch. Am Handy lief sie
   deshalb ueber den Bildschirmrand hinaus - Logo links und Logout rechts
   waren abgeschnitten, und die ganze Seite liess sich seitlich schieben.
   Jetzt: eine Zeile, die umbrechen DARF, und eine feste Mindestbreite von 0
   auf den Kindern, damit Flex sie schrumpfen laesst statt zu ueberlaufen.

   Der Rand nach oben ist bewusst klein (war 12/20 px) - er kostete auf dem
   Telefon eine halbe Bildschirmzeile, ohne etwas zu leisten.
   ========================================================================== */
.kopf { padding: 6px 2px 8px; }

.kopf-haupt {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

/* Das Bild oben links. Fest quadratisch, damit ein Profilbild denselben
   Platz einnimmt wie das App-Zeichen und beim Wechsel nichts springt. */
.kopf-bild {
  flex: 0 0 auto;
  width: 40px; height: 40px; padding: 0;
  border: none; border-radius: 11px;
  background: linear-gradient(135deg, var(--accent), #ec4899);
  display: flex; align-items: center; justify-content: center;
  font-size: 19px; cursor: pointer;
  overflow: hidden;
}
.kopf-bild[disabled] { cursor: default; }
.kopf-bild.hat-bild { background: none; }
.kopf-bild img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Der Name: zurueckhaltend, nicht die Ueberschrift der Seite. Er sagt, WO
   man ist - die Arbeit passiert darunter. */
.kopf-titel {
  display: flex; flex-direction: column; gap: 2px;
  min-width: 0; flex: 1 1 auto;
}
.kopf-app {
  font-size: 13px; font-weight: 600; letter-spacing: .02em;
  color: var(--text-dim); opacity: .72;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.kopf-zeile2 {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
}

.kopf-rechts {
  display: flex; align-items: center; gap: 8px;
  margin-left: auto; flex: 0 0 auto;
}
/* Name oben, Rolle klein darunter - spart Breite gegenueber
   "Admin  Eaglelator" nebeneinander. */
.kopf-user { display: flex; flex-direction: column; line-height: 1.15; min-width: 0; }
.kopf-user .ku-name {
  font-size: 13px; font-weight: 700;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 11em;
}
.kopf-user .ku-rolle { font-size: 10px; color: var(--text-dim); opacity: .8; }

/* Alarme. "mobil" = eigene Zeile unter der Kopfzeile, "breit" = in der
   Luecke der Tab-Leiste. Immer nur eines von beidem sichtbar. */
.kopf-alarme { display: flex; gap: 6px; align-items: center; }
.kopf-alarme.gestapelt { flex-direction: column; align-items: stretch; }
.kopf-alarme.mobil { margin-top: 8px; }
.kopf-alarme.breit { justify-content: center; }
.tab-luecke { display: flex; align-items: center; justify-content: center; flex: 1 1 auto; min-width: 8px; }

/* Ab hier ist Platz fuer die Alarme in der Tab-Leiste - darunter bekommen
   sie ihre eigene Zeile. */
@media (min-width: 900px) {
  .kopf-alarme.mobil { display: none; }
}
@media (max-width: 899px) {
  .kopf-alarme.breit { display: none; }
  .tab-luecke { flex: 0 0 auto; min-width: 0; }
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
}
.brand .logo {
  width: 36px; height: 36px;
  border-radius: 10px;
  background: linear-gradient(135deg, var(--accent), #ec4899);
  display: flex; align-items: center; justify-content: center;
  font-size: 18px;
}
.brand span.title { font-weight: 800; font-size: 17px; }

.tabs {
  display: flex;
  gap: 6px;
  background: var(--bg-elevated);
  padding: 4px;
  border-radius: 999px;
  border: 1px solid var(--border);
  overflow-x: auto;
  /* v1.2.1: die Leiste hatte gar keinen Abstand nach unten - der folgende
     Inhalt klebte direkt daran. Bewusst nur 3px, das reicht zum Trennen,
     ohne Platz zu verschenken. */
  margin-bottom: 3px;
}
/* v1.7.1: Symbol oben, kleiner Text darunter.
   Die alte einzeilige Leiste war bei 1280 px abgeschnitten - gestapelt
   braucht jeder Tab knapp die Haelfte der Breite. */
.tab-btn {
  position: relative;
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  border: none;
  background: transparent;
  color: var(--text-dim);
  padding: 5px 10px;
  border-radius: 14px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  line-height: 1.15;
  min-width: 3.4em;
}
.tab-btn .tab-sym { font-size: 17px; display: block; }
/* Der Text ist bewusst klein: er erklaert das Symbol, er traegt es nicht. */
.tab-btn .tab-txt { font-size: 10.5px; letter-spacing: .01em; opacity: .95; }
.tab-btn.active {
  background: var(--accent);
  color: var(--accent-text);
}
.tab-btn:hover:not(.active) { background: rgba(255,255,255,.06); }

/* Zaehler als Blase am Symbol statt "(3)" im Text - faellt mehr auf und
   kostet keine Breite. */
.tab-zahl {
  position: absolute; top: 1px; right: 3px;
  min-width: 15px; height: 15px; padding: 0 4px;
  border-radius: 999px;
  font-size: 9.5px; line-height: 15px; font-weight: 700;
  background: #ef4444; color: #fff;
  box-shadow: 0 0 0 2px var(--bg-elevated);
}
/* "ruhig" = eine Gesamtzahl, kein "das wartet auf dich" (siehe Raids). */
.tab-zahl.ruhig {
  background: var(--border); color: var(--text-dim);
  font-weight: 600;
}
.tab-btn.active .tab-zahl { box-shadow: 0 0 0 2px var(--accent); }

/* Trennstrich: schiebt alles danach an den rechten Rand. */
.tab-trenn {
  flex: 1 1 auto;
  position: relative;
  min-width: 14px;
}
.tab-trenn::after {
  content: ''; position: absolute; right: 6px; top: 15%; bottom: 15%;
  width: 1px; background: var(--border);
}

/* v1.7.2: Am Rechner ein paar Pixel mehr - Symbole und Schrift waren dort
   unnoetig klein, Platz ist genug. Der Rand nach oben und unten bleibt
   bewusst knapp, damit es modern und nicht aufgeblasen wirkt. */
@media (min-width: 900px) {
  .tabs { padding: 3px 14px 3px 4px; gap: 4px; }
  .tab-btn { padding: 4px 13px; min-width: 4em; }
  .tab-btn .tab-sym { font-size: 20px; }
  .tab-btn .tab-txt { font-size: 11.5px; }
  .tab-zahl { min-width: 16px; height: 16px; font-size: 10px; line-height: 16px; }
}

/* Schmales Fenster: nur noch Symbole. Der volle Name steht im title, und
   die Leiste passt statt zu scrollen. */
@media (max-width: 620px) {
  .tab-btn .tab-txt { display: none; }
  .tab-btn { min-width: 0; padding: 7px 9px; }
  .tab-trenn { flex: 0 0 auto; }
}

.card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
  margin-bottom: 14px;
}

.field { margin-bottom: 14px; }
.field label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-dim);
  margin-bottom: 6px;
}
/* v1.28.25 - SASCHAS BEFUND: "die editfelder sind streckenweise sehr gross
   und weiss... das sieht einfach unschoen aus."
   Ursache: Diese Regel kannte nur text/password/url. Zahlenfelder,
   Suchfelder, E-Mail-, Zeit- und Datumsfelder fielen durch und standen im
   Browser-Weiss mitten im dunklen Fenster (siehe Medienwiedergabe:
   Schwellen, Sekunden, Fahnentexte). Jetzt gilt sie fuer alle. */
input[type=text], input[type=password], input[type=url], input[type=number],
input[type=search], input[type=email], input[type=tel], input[type=time],
input[type=date], input[type=datetime-local], textarea, select {
  width: 100%;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  color: var(--text);
  padding: 8px 10px;
  border-radius: 8px;
  font-size: 14px;
  font-family: inherit;
}
/* Damit auch die browser-eigenen Teile mitziehen: die kleinen Pfeile am
   Zahlenfeld, der Kalender am Datumsfeld, die Rollbalken. Ohne diese Zeile
   malt der Browser sie hell, egal was daneben steht. */
html[data-theme="dark"], html:not([data-theme]) { color-scheme: dark; }
html[data-theme="light"] { color-scheme: light; }
@media (prefers-color-scheme: light) { html:not([data-theme]) { color-scheme: light; } }
input:focus, textarea:focus, select:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
textarea { resize: vertical; min-height: 70px; }

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  border: none;
  border-radius: 8px;
  padding: 11px 18px;
  font-size: 15px;
  font-weight: 700;
  cursor: pointer;
  background: var(--accent);
  color: var(--accent-text);
}
.btn:hover { background: var(--accent-hover); }
.btn:disabled { opacity: .5; cursor: not-allowed; }
.btn.secondary { background: var(--bg-elevated); color: var(--text); border: 1px solid var(--border); }
.btn.danger { background: var(--danger); }
.btn.success { background: var(--success); }
.btn.warn { background: var(--warn); color: #2a1500; }
.btn.small { padding: 6px 10px; font-size: 13px; }
.btn.mini { padding: 4px 8px; font-size: 12px; }
.btn.settings-icon-btn {
  background: transparent;
  color: var(--text);
  border: none;
  padding: 6px;
  font-size: 18px;
  cursor: pointer;
  border-radius: 6px;
}
.btn.settings-icon-btn:hover {
  background: var(--bg-elevated);
  color: var(--accent);
}

/* Public Theme Picker - nur über Zahnrad-Icon kontrollierbar */
.public-theme-picker {
  transition: all 0.3s ease;
}
.public-theme-picker:not([open]) {
  display: none !important;
}
.public-theme-picker summary {
  cursor: default !important;
  user-select: none;
}
.public-theme-picker summary::-webkit-details-marker {
  display: none;
}

.btn.block { width: 100%; }

.row { display: flex; gap: 10px; flex-wrap: wrap; }
.row > * { flex: 1; min-width: 140px; }
/* Bugfix: .toolbar-sep ist als duenne 1px-Trennlinie gedacht (siehe unten),
   wurde aber innerhalb eines .row-Containers durch die generische "jedes
   Kind flex:1 + min-width:140px"-Regel oben zu einem grossen leeren Kasten
   aufgeblasen (sichtbar z.B. bei "Meine Ansicht" auf der oeffentlichen
   Liste, zwischen "Hell" und "Farblos"). Hier gezielt wieder zurueckgesetzt. */
.row > .toolbar-sep { flex: 0 0 auto; min-width: 0; }

.wish-list { display: flex; flex-direction: column; gap: 10px; }

.wish-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px;
}
.wish-top {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 10px;
}
/* v1.2.4: Das Albumbild steht jetzt links vor Titel und Interpret. Damit der
   Statusbadge weiterhin ganz rechts sitzt, muss der Textblock dazwischen den
   freien Platz einnehmen - sonst wuerde space-between alle drei Teile
   gleichmaessig auseinanderziehen. */
.wish-top > div { flex: 1 1 auto; min-width: 0; }
.wish-title { font-size: 16px; font-weight: 700; margin-bottom: 2px; }
.wish-artist { color: var(--text-dim); font-size: 14px; }

/* --- Albumbild (v1.2.4) ---
   Groesse kommt ueber --cover-size aus dem HTML, damit dieselbe Funktion in
   DJ-Anzeige (34px) und Musikfeed (44px) verwendet werden kann. */
.wish-cover {
  width: var(--cover-size, 34px);
  height: var(--cover-size, 34px);
  flex: 0 0 auto;
  border-radius: 6px;
  object-fit: cover;
  background: color-mix(in srgb, var(--cat-color, var(--accent)) 18%, transparent);
  display: block;
}
/* Kein Bild vorhanden: eingefaerbte Kachel mit Note, wie bisher. */
.wish-cover.leer {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: calc(var(--cover-size, 34px) * 0.45);
  line-height: 1;
}
.djview-cover { border-radius: 7px; }
.wish-card-cover { margin-top: 2px; }
.wish-meta {
  display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px;
}
.badge {
  font-size: 12px;
  font-weight: 700;
  padding: 3px 9px;
  border-radius: 999px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  color: var(--text-dim);
}
.badge.status-offen { background: rgba(245,158,11,.15); color: var(--warn); border-color: rgba(245,158,11,.3); }
.badge.status-gespielt { background: rgba(34,197,94,.15); color: var(--success); border-color: rgba(34,197,94,.3); }
.badge.status-abgelehnt { background: rgba(239,68,68,.15); color: var(--danger); border-color: rgba(239,68,68,.3); }
.wish-comment {
  margin-top: 8px;
  font-size: 14px;
  color: var(--text-dim);
  font-style: italic;
}
.wish-actions {
  display: flex; gap: 6px; flex-wrap: wrap; margin-top: 12px;
  border-top: 1px solid var(--border);
  padding-top: 10px;
}

.empty-state {
  text-align: center;
  padding: 40px 16px;
  color: var(--text-dim);
}

.toast {
  position: fixed;
  bottom: 16px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  color: var(--text);
  padding: 12px 18px;
  border-radius: 10px;
  font-size: 14px;
  z-index: 999;
  box-shadow: 0 8px 24px rgba(0,0,0,.4);
  max-width: 90vw;
}
.toast.error { border-color: var(--danger); color: #fecaca; }
.toast.success { border-color: var(--success); }

.userbar {
  display: flex; align-items: center; gap: 10px; font-size: 13px; color: var(--text-dim);
}
.userbar .role-pill {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  padding: 2px 8px;
  border-radius: 999px;
  font-weight: 700;
  color: var(--text);
}

.filters { display: flex; gap: 8px; margin-bottom: 12px; flex-wrap: wrap; }
.filters select { width: auto; flex: 0 0 auto; }

/* ---- Musikfeed-Werkzeugleiste: Filter, Ansicht-Umschalter und Export/
   Löschen-Aktionen alle in EINER Zeile, die sich am Browserfenster anpasst
   (auf schmalen Bildschirmen bricht sie einfach um, statt fest gestapelt zu
   sein) - damit die Werkzeugleiste nicht mit wachsender Anzahl an Buttons
   immer weiter nach unten waechst. ---- */
.dashboard-toolbar {
  /* v1.3.4.1: zwei feste Reihen (.tb-zeile) statt einer frei umbrechenden
     Flaeche - sonst haengt die Anordnung von der Textlaenge ab und sieht
     auf Deutsch anders aus als auf Englisch. */
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
  /* Container-Query-Kontext fuer die Anbieter-Auswahl (siehe .provider-btn
     unten) - reagiert bewusst auf die tatsaechlich verfuegbare Breite DIESER
     Werkzeugleiste statt nur auf die Fensterbreite, damit es auch dann
     richtig schrumpft, wenn die Toolbar durch ein Seitenpanel o.ae. weniger
     Platz hat als der Browser insgesamt. */
  container-type: inline-size;
  container-name: dashtoolbar;
}
.dashboard-toolbar select { width: auto; flex: 0 0 auto; }
.dashboard-toolbar .btn.active { background: var(--accent); color: var(--accent-text); }

/* ---- Anbieter-Auswahl (Tidal/Spotify/Apple Music) direkt im Musikfeed,
   siehe musicSearchControlsHtml() - Buttons statt Dropdown (Nutzer-Wunsch),
   der aktive/primaere Anbieter ist farbig, andere aktive Anbieter erscheinen
   in Graustufen, in den Einstellungen deaktivierte Anbieter sind
   durchgestrichen und nicht klickbar. Groesse reagiert per Container-Query
   auf .dashboard-toolbar oben: bei wenig Platz verschwindet zuerst der Name
   (nur noch Icon + Punkt fuer "primaer"), dann werden auch die Icons kleiner
   - genau wie im abgestimmten Mockup. ---- */
.provider-group { display: flex; gap: 6px; flex-wrap: nowrap; }
.provider-btn {
  display: flex; align-items: center; gap: 6px;
  background: var(--bg-card); border: 1.5px solid var(--border);
  border-radius: 8px; padding: 6px 10px; cursor: pointer; position: relative;
  transition: all .15s; font-size: 12.5px; color: var(--text); white-space: nowrap;
}
.provider-btn img { width: 22px; height: 22px; border-radius: 6px; object-fit: contain; flex: 0 0 auto; transition: filter .15s, opacity .15s; }
.provider-btn.active { border-color: var(--accent); background: rgba(var(--accent-rgb, 139,92,246), 0.12); box-shadow: 0 0 0 1px var(--accent) inset; }
.provider-btn.active .lbl { color: var(--accent); font-weight: 700; }
.provider-btn:not(.active):not(.disabled) img { filter: grayscale(1); opacity: .55; }
.provider-btn:not(.active):not(.disabled):hover img { filter: grayscale(.3); opacity: .85; }
.provider-btn.disabled { cursor: not-allowed; opacity: .4; }
.provider-btn.disabled img { filter: grayscale(1); }
.provider-btn.disabled::after {
  content: ""; position: absolute; left: 6px; right: 6px; top: 50%; height: 2px;
  background: var(--danger); transform: rotate(-8deg); border-radius: 2px;
}
.provider-btn .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); display: none; flex: 0 0 auto; }

/* Schrumpfstufen - siehe container-type auf .dashboard-toolbar oben.
   Zusaetzlich zu den Anbieter-Buttons blendet die zweite Stufe auch
   "wide-label"-Texte anderer Toolbar-Elemente aus (z.B. "Autovervollständigung"),
   damit die ganze Leiste gleichmaessig schrumpft statt nur ein Element. */
@container dashtoolbar (max-width: 620px) {
  .provider-btn .lbl { display: none; }
  /* Bugfix: nur der AKTIVE/primaere Button soll den kleinen Punkt zeigen -
     ohne ".active" waere er faelschlich bei allen drei Anbietern sichtbar,
     nicht erkennbar welcher gerade primaer ist. */
  .provider-btn.active .dot { display: block; }
  .provider-btn { padding: 6px 8px; }
}
@container dashtoolbar (max-width: 420px) {
  .dashboard-toolbar .wide-label { display: none; }
  .provider-btn img { width: 18px; height: 18px; }
  .provider-btn { padding: 5px 6px; gap: 0; }
  .provider-group { gap: 4px; }
}
.toolbar-sep {
  width: 1px;
  align-self: stretch;
  min-height: 22px;
  background: var(--border);
  margin: 0 2px;
}
@media (max-width: 460px) {
  .toolbar-sep { display: none; }
}

.list-item-row {
  display: flex; justify-content: space-between; align-items: center;
  padding: 10px 0; border-bottom: 1px solid var(--border);
  gap: 10px; flex-wrap: wrap;
}
.list-item-row:last-child { border-bottom: none; }

.hint { font-size: 13px; color: var(--text-dim); margin-top: -6px; }
.hint.fade-on-input { transition: opacity .2s ease; }
.hint.fade-on-input.faded { opacity: .3; }

.link-inline { font-size: 13px; }

@media (max-width: 480px) {
  .wish-actions .btn { flex: 1; }
}

/* Kategorie-Chip mit dynamischer Farbe (via inline style --cat-color gesetzt) */
.cat-chip {
  display: inline-flex;
  align-items: center;
  font-size: 12px;
  font-weight: 800;
  /* v1.3.4: gleichmaessige Polsterung, seit der Farbpunkt weg ist - vorher
     war links weniger Platz, weil dort der Punkt sass. */
  padding: 3px 9px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--cat-color, #8b5cf6) 18%, transparent);
  color: var(--cat-color, #8b5cf6);
  border: 1px solid color-mix(in srgb, var(--cat-color, #8b5cf6) 45%, transparent);
}
.wish-card { border-left: 4px solid var(--cat-color, var(--border)); }

.badge.refund-muss_erstattet_werden { background: rgba(239,68,68,.15); color: var(--danger); border-color: rgba(239,68,68,.3); }
.badge.refund-erstattet, .badge.refund-nicht_noetig { background: rgba(34,197,94,.15); color: var(--success); border-color: rgba(34,197,94,.3); }
.badge.duplicate { background: rgba(245,158,11,.15); color: var(--warn); border-color: rgba(245,158,11,.3); }

.checkbox-row {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  color: var(--text);
  font-weight: 500;
  margin-bottom: 8px;
}
.checkbox-row input[type=checkbox] { width: 18px; height: 18px; flex: 0 0 auto; }

/* ---- Icon-Stil-Auswahl (Musik-Anbieter, siehe viewEinstellungen) - zwei
   normale <input type=radio>, aber als kleine Buttons statt Radiokreise
   dargestellt (Nutzer-Wunsch: "nicht per Dropdown, sondern per
   Auswahlbutton"). Bewusst ohne JS: der aktive Zustand kommt allein aus
   :has(:checked), das Radio selbst bleibt unsichtbar aber fokussierbar. ---- */
.icon-style-radio-row { display: flex; gap: 8px; flex-wrap: wrap; }
.icon-style-radio {
  display: flex; align-items: center; gap: 6px;
  padding: 7px 14px; border: 1.5px solid var(--border); border-radius: 8px;
  cursor: pointer; font-size: 13px; font-weight: 600; color: var(--text-dim);
  background: var(--bg-elevated); transition: all .15s; margin-bottom: 0;
}
.icon-style-radio input { position: absolute; opacity: 0; width: 0; height: 0; }
.icon-style-radio:has(input:checked) {
  border-color: var(--accent); color: var(--accent);
  background: rgba(var(--accent-rgb, 0,255,0), 0.12);
}

.count-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
  gap: 10px;
}
.count-tile {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 18px 14px;
  text-align: center;
}
.count-tile .num { font-size: 32px; font-weight: 800; }
.count-tile .lbl { font-size: 13px; color: var(--text-dim); margin-top: 4px; }

/* ==========================================================================
   v1.28.26 - DIE KATEGORIE-ZEILE ALS RASTER

   Saschas Befund 11.09.: "das Feld für 'Standard' oder für die abkürzung und
   erstrecht für die Reihenfolge also nummerierung NIEMALS so riesig sein wie
   das Feld was es jeweils bekommen hat."

   Im Browser nachgemessen: alle drei standen bei 215 px, weil die Zeile ein
   flex-Verbund mit `flex: 1 1 100px` war - da bekommt jedes Feld denselben
   Anteil, und die max-width-Regeln von damals wurden von einer spaeteren
   Regel ueberstimmt. Ein RASTER mit benannten Spalten kann das nicht
   passieren: jede Spalte hat ihr eigenes Mass, unabhaengig von der Zahl der
   Felder daneben.

   Vier Breiten, wie verlangt (voll / normal / schmal / Handy) - die
   Umbruchpunkte stehen weiter unten.
   ========================================================================== */
.cat-row {
  display: grid;
  /* Farbe | Name (waechst) | Kuerzel | Reihenfolge | aktiv | Speichern | Loeschen */
  grid-template-columns: auto minmax(140px, 1fr) 7rem 4.5rem auto auto auto;
  align-items: center;
  gap: 8px;
  padding: 9px 0;
  border-bottom: 1px solid var(--border);
}
.cat-row:last-child { border-bottom: none; }
.cat-row input[type=color] {
  width: 38px; height: 38px; padding: 2px; border-radius: 8px;
  border: 1px solid var(--border); background: var(--bg-elevated);
}
.cat-row input[type=text] { width: 100%; min-width: 0; max-width: none; }
/* Die Zahl bekommt Platz fuer 3-4 Ziffern - mehr braucht eine Reihenfolge
   nicht. Waechst der Inhalt doch einmal (sechsstellig), macht sich das Feld
   von selbst breiter, siehe @supports unten. */
.cat-row .cat-order { text-align: center; }
.cat-row.inactive { opacity: .5; }

/* Moderne Browser lassen Felder mit ihrem Inhalt wachsen. Wo das geht, darf
   die Zahl (und das Kuerzel) sich ausdehnen, statt abzuschneiden - gedeckelt,
   damit die Zeile nicht aus dem Ruder laeuft. */
@supports (field-sizing: content) {
  .cat-row .cat-order { field-sizing: content; min-width: 3.4rem; max-width: 7rem; }
  .cat-row .cat-kuerzel { field-sizing: content; min-width: 5rem; max-width: 11rem; }
}

.search-result-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  padding: 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  margin-bottom: 6px;
  cursor: pointer;
  background: var(--bg-elevated);
}
.search-result-item:hover { border-color: var(--accent); }

.modal-card {
  position: fixed; inset: 0; margin: auto;
  max-width: 480px; max-height: 90vh; overflow: auto;
  z-index: 1000; height: fit-content;
  box-shadow: 0 20px 60px rgba(0,0,0,.6);
}
.modal-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 999; }

/* ---- Kompakte DJ-Ansicht (viel Info auf wenig Hoehe) ---- */
.view-toggle {
  display: flex;
  gap: 6px;
  margin-bottom: 10px;
}
.view-toggle .btn.active { background: var(--accent); color: var(--accent-text); }

.wish-table {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}
.wish-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 8px;
  border-bottom: 1px solid var(--border);
  border-left: 4px solid var(--cat-color, var(--border));
  background: var(--bg-card);
  font-size: 13px;
  min-height: 34px;
}
.wish-row:last-child { border-bottom: none; }
.wish-row:hover { background: var(--bg-elevated); }
.wish-row.is-abgelehnt { opacity: .55; }

.row-status-dot {
  flex: 0 0 auto;
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--text-dim);
}
.row-status-dot.offen { background: var(--warn); }
.row-status-dot.gespielt { background: var(--success); }
.row-status-dot.abgelehnt { background: var(--danger); }

.row-cat {
  flex: 0 0 auto;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: .02em;
  padding: 2px 7px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--cat-color, #8b5cf6) 20%, transparent);
  color: var(--cat-color, #8b5cf6);
  white-space: nowrap;
}

.row-main {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: baseline;
  gap: 6px;
  overflow: hidden;
  white-space: nowrap;
  cursor: pointer;
}
.row-main .row-title { font-weight: 700; overflow: hidden; text-overflow: ellipsis; }
.row-main .row-artist { color: var(--text-dim); font-size: 12px; overflow: hidden; text-overflow: ellipsis; flex: 0 1 auto; }
.row-main .row-flags { flex: 0 0 auto; font-size: 12px; }

.row-from {
  flex: 0 0 auto;
  color: var(--text-dim);
  font-size: 12px;
  max-width: 110px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  display: none;
}
@media (min-width: 640px) {
  .row-from { display: block; }
}

.row-actions {
  display: flex;
  gap: 3px;
  flex: 0 0 auto;
}
.icon-btn {
  width: 25px; height: 25px;
  border: none;
  border-radius: 6px;
  background: var(--bg-elevated);
  color: var(--text);
  font-size: 12px;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
}
.icon-btn:hover { background: var(--border); }
.icon-btn.success { background: rgba(34,197,94,.18); color: var(--success); }
.icon-btn.danger { background: rgba(239,68,68,.18); color: var(--danger); }

.row-detail {
  padding: 6px 12px 10px 16px;
  border-bottom: 1px solid var(--border);
  border-left: 4px solid var(--cat-color, var(--border));
  background: var(--bg-elevated);
  font-size: 13px;
  color: var(--text-dim);
}
.row-detail .wish-comment { margin: 4px 0 0; }

/* ---- Musikfeed: eingebettete "Erledigt"-Unterliste (gespielt/abgelehnt/
   Duplikat, siehe isArchivedWish in app.js) - bewusst deutlich kleiner als
   die normale Karten-/Kompaktansicht, mit eigenem Scrollbereich, damit sie
   bei vielen Eintraegen nicht die Uebersicht der aktiv verwalteten Liste
   sprengt. Farbiger linker Rand zeigt den Grund auf einen Blick. ---- */
.played-wishes-card { padding: 12px 14px; }
.played-wishes-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 8px;
}
/* margin-right:auto schiebt die beiden Knöpfe rechts zusammen, statt sie
   quer über die Zeile zu verteilen (v1.3.3: „Bereinigen" kam dazu). */
.played-wishes-header h2 { font-size: 14px; margin: 0 auto 0 0; color: var(--text-dim); }
.played-wishes-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  max-height: 260px;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: 8px;
}
.played-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 3px 8px;
  font-size: 12px;
  min-height: 24px;
  border-bottom: 1px solid var(--border);
  border-left: 3px solid var(--border);
  opacity: .8;
}
.played-row:last-child { border-bottom: none; }
.played-row:hover { background: var(--bg-elevated); opacity: 1; }
.played-row.is-gespielt { border-left-color: var(--success); }
.played-row.is-abgelehnt { border-left-color: var(--danger); }
.played-row.is-duplicate { border-left-color: var(--warn); }
.played-row-title { font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 0 1 auto; }
.played-row-artist { color: var(--text-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 0 1 auto; }
.played-row-from { color: var(--text-dim); margin-left: auto; flex: 0 0 auto; font-size: 11px; }
.played-row-actions { display: flex; gap: 3px; flex: 0 0 auto; }
.played-row-actions .icon-btn { width: 20px; height: 20px; font-size: 10px; }

/* ---- Positions-Nummer (aus der echten Reihenfolge), siehe
   buildPositionMap() in app.js - taucht ueberall dort auf, wo Songs/Notizen
   fuer das Team angezeigt werden (Musikfeed, Notizen-Tab, DJ-Anzeige), NICHT
   auf der oeffentlichen Liste. ---- */
.pos-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  color: var(--text-dim);
  margin-right: 4px;
  flex: 0 0 auto;
}

/* ---- Uhrzeit direkt neben dem #Positions-Badge (siehe timeBadgeHtml() in
   app.js) - bewusst KEIN eigener Kasten/Hintergrund wie beim Pos-Badge
   (Nutzer-Feedback: "wird sonst zu viel und zu groß"), nur schlichter,
   gedimmter Text in Tabellenziffern-Breite. ---- */
.pos-time {
  font-size: 10px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
  opacity: .8;
  margin-right: 6px;
  flex: 0 0 auto;
}

/* ---- Kurzes Aufblinken fuer einen gerade selbst eingetragenen Wunsch
   (siehe scrollToJustAddedWish() in app.js) - rein additiv (Farben/
   Schraffuren/Status-Optik der Karte/Zeile selbst bleiben unangetastet,
   siehe Nutzer-Wunsch), verschwindet nach gut 2 Sekunden von selbst. ---- */
@keyframes wishFlashNew {
  0% { box-shadow: 0 0 0 3px rgba(var(--accent-rgb, 139,92,246), 0.55); }
  100% { box-shadow: 0 0 0 3px rgba(var(--accent-rgb, 139,92,246), 0); }
}
.wish-flash-new { animation: wishFlashNew 2.4s ease-out 1; }

/* ---- "Zuletzt hinzugefuegt"-Leiste (siehe recentlyAddedStripHtml() in
   app.js) - Nutzer-Wunsch: neu eingetragene Wuensche landen in einer langen
   Liste ganz unten und sind fuer den, der sie gerade eingetragen hat, schwer
   wiederzufinden. Bewusst winzig/einzeilig (wraps nur bei sehr schmalem
   Fenster), sitzt zwischen Toolbar/Anbieter-Auswahl und der eigentlichen
   Liste. ---- */
.recent-added-strip {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  padding: 6px 12px;
  margin: 6px 0;
  font-size: 11px;
  color: var(--text-dim);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.recent-added-label {
  font-weight: 700;
  flex: 0 0 auto;
  opacity: .85;
}
.recent-added-item {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 6px;
  border-radius: 999px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  flex: 0 0 auto;
}
.recent-cat-dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--cat-color, #8b5cf6);
  flex: 0 0 auto;
}
.recent-added-text {
  color: var(--text);
  font-weight: 600;
  white-space: nowrap;
}
.recent-added-pos {
  font-variant-numeric: tabular-nums;
  color: var(--accent, #8b5cf6);
  font-weight: 700;
}
.recent-added-time {
  font-variant-numeric: tabular-nums;
  opacity: .75;
}

/* ---- Sehr kleine Notiz-Zeile im Musikfeed (interleaved zwischen den
   aktiven Songs, siehe dashboardNoteRow in app.js) ---- */
.dash-note-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  padding: 6px 10px;
  border-radius: 8px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--cat-color, #38bdf8);
  background: var(--bg-elevated);
  font-size: 13px;
}
.dash-note-text { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dash-note-row .icon-btn { flex: 0 0 auto; }

.reorder-col {
  display: flex;
  flex-direction: column;
  gap: 1px;
  flex: 0 0 auto;
}
.reorder-col .icon-btn {
  width: 20px;
  height: 12px;
  font-size: 8px;
  border-radius: 3px;
}

/* ---- Dedizierte DJ-Anzeige (kein Nav sichtbar, nur gezielte Mini-Aktionen) ---- */
.djview-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 14px;
  background: var(--bg-elevated);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
}
.djview-topbar .brand {
  font-weight: 800;
  font-size: 14px;
  color: var(--text-dim);
  display: flex;
  align-items: center;
  gap: 6px;
}
.djview-topbar .leave {
  font-size: 12px;
  color: var(--text-dim);
  background: var(--bg-card);
  border: 1px solid var(--border);
  padding: 4px 10px;
  border-radius: 6px;
  cursor: pointer;
}
.djview-topbar-right { display: flex; align-items: center; gap: 14px; }

/* ---- Skalierungs-Regler ("DJ-Fader") fuer die DJ-Anzeige - rein lokal im
   Browser der/des Einloggten gespeichert (localStorage, siehe
   DJVIEW_SCALE_KEY/getDjViewScale in app.js), betrifft nie andere
   Zuschauer/das Team. Bewusst im Mixer-Fader-Look statt einer schlichten
   +/- Pille (Nutzer-Wunsch "muss nicht einfach sein, soll gut aussehen") -
   passt thematisch zur DJ-Anzeige. Steuert per CSS "zoom" die gesamte
   .djview-table (Kopfzeile mit dem Regler selbst bleibt immer normal groß),
   damit der DJ je nach Monitor/Fenstergröße mehr oder weniger Zeilen
   gleichzeitig sehen kann. ---- */
.djview-scale-fader { display: flex; align-items: center; gap: 8px; user-select: none; }
.djview-scale-track-wrap { width: 96px; }
.djview-scale-track {
  position: relative;
  height: 5px;
  border-radius: 999px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  cursor: pointer;
}
.djview-scale-fill {
  position: absolute; left: 0; top: 0; bottom: 0; border-radius: 999px;
  background: linear-gradient(to right, color-mix(in srgb, var(--accent) 55%, transparent), var(--accent));
  pointer-events: none;
}
.djview-scale-ticks { display: flex; justify-content: space-between; margin-top: 4px; padding: 0 1px; pointer-events: none; }
.djview-scale-ticks span { width: 1px; height: 4px; background: var(--border); }
.djview-scale-handle {
  position: absolute; top: 50%; width: 13px; height: 18px;
  background: linear-gradient(180deg, #ffffff, #d8d3e6);
  border-radius: 3px;
  transform: translate(-50%, -50%);
  box-shadow: 0 1px 4px rgba(0,0,0,.45), inset 0 0 0 1px rgba(0,0,0,.12);
  cursor: grab;
}
.djview-scale-handle::after {
  content: '';
  position: absolute; left: 2px; right: 2px; top: 50%; height: 2px;
  background: var(--accent);
  transform: translateY(-50%);
}
.djview-scale-handle:active { cursor: grabbing; }
.djview-scale-readout {
  font-size: 11px; font-weight: 800; font-variant-numeric: tabular-nums;
  color: var(--text-dim);
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: 5px; padding: 3px 7px; min-width: 38px; text-align: center;
}
@media (max-width: 560px) {
  .djview-scale-track-wrap { width: 60px; }
}

.djview-table { display: flex; flex-direction: column; }
.djview-row {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 2px 12px;
  border-bottom: 1px solid var(--border);
  border-left: 3px solid var(--cat-color, var(--border));
  font-size: 13px;
  min-height: 30px;
  position: relative;
}

/* v1.3.4 - Kategoriefarbe als Hintergrund, nicht nur als Strich am Rand.
   Vorbild ist der Kategorie-Chip ("STD · Standard") und der Tidal-Knopf:
   dieselbe Farbe, nur stark verduennt. Der Verlauf laeuft nach rechts aus,
   damit die Schrift auf voller Deckkraft lesbar bleibt und die Zeile nicht
   wie ein farbiger Balken wirkt.

   color-mix statt fester rgba-Werte, weil die Kategoriefarbe frei einstellbar
   ist - so passt sich die Tuenung jeder Farbe an, auch im hellen Modus. */
.djview-row::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
    to right,
    color-mix(in srgb, var(--cat-color, transparent) 16%, transparent) 0%,
    color-mix(in srgb, var(--cat-color, transparent) 7%, transparent) 38%,
    transparent 72%
  );
}
/* Der Inhalt muss ueber der Tuenung liegen, sonst waere er ueberdeckt. */
.djview-row > * { position: relative; z-index: 1; }

/* Zeilenwechsel bleibt erhalten, liegt aber UNTER der Farbtuenung. */
.djview-row:nth-child(odd) { background: rgba(255,255,255,0.015); }
html[data-theme="light"] .djview-row:nth-child(odd) { background: rgba(0,0,0,0.02); }

/* Der farblose Modus braucht hier keine Sonderregel: html.public-mono-theme
   legt einen grayscale-Filter ueber die ganze Seite, die Tuenung wird also
   automatisch mitentsaettigt. */
.djview-empty { padding: 40px 20px; text-align: center; color: var(--text-dim); }

.djview-main {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
}
.djview-song {
  display: flex;
  align-items: baseline;
  gap: 4px;
  overflow: hidden;
  white-space: nowrap;
}
.djview-song .row-title { font-weight: 700; overflow: hidden; text-overflow: ellipsis; }
.djview-song .row-artist { color: var(--text-dim); font-size: 12px; overflow: hidden; text-overflow: ellipsis; }
.djview-comment {
  font-size: 12px;
  color: var(--text-dim);
  font-style: italic;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.mini-icon { font-size: 11px; opacity: .8; flex: 0 0 auto; }

/* row-cat innerhalb der DJ-Anzeige bewusst auf feste Breite gezwungen (statt
   der sonst ueblichen auto-Breite) - siehe .djview-from/.djview-actions
   unten fuer die Begruendung: nur so stehen die Spalten (Titel-Start,
   Von-Spalte, Buttons) bei JEDER Zeile an derselben Stelle, egal wie lang
   Label/Name/Titel gerade sind. */
.djview-row .row-cat {
  flex: 0 0 88px;
  width: 88px;
  overflow: hidden;
  text-overflow: ellipsis;
  text-align: center;
  /* Etwas kraeftiger als das Standard-.row-cat anderswo (siehe Nutzer-
     Wunsch "Kaesten sollen ausgenutzt werden, nicht 5cm Rahmen um 2mm
     Schrift") - Titel/Interpret/Von bleiben bewusst unveraendert, aber die
     Badges/Icons drumrum duerfen die eh schon vorhandene Zeilenhoehe mehr
     ausfuellen. */
  font-size: 10.5px;
  padding: 3px 8px;
}

.djview-from {
  flex: 0 0 116px;
  width: 116px;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 4px;
  color: var(--text-dim);
  font-size: 12px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Feste Breite (statt auto/inhaltsabhaengig) - der Grund fuer das
   "unruhige"/verschobene Aussehen der Buttons war, dass .djview-from
   (Von-Name) unterschiedlich lang ist und dadurch ALLES rechts davon
   (Buttons + Positions-Badge) pro Zeile an einer anderen Stelle landete.
   Mit fester Breite hier UND bei .djview-from stehen die Buttons jetzt
   spaltenweise sauber untereinander. Ausnahme: aktiver Alarm/offener
   Alarm-Picker duerfen breiter werden (siehe weiter unten), weil die dort
   bewusst auffallen sollen. */
.djview-actions {
  display: flex;
  align-items: center;
  gap: 4px;
  /* v1.3.4.1: 124px statt 100px - seit dem Erstattungsknopf koennen hier
     VIER Knoepfe stehen (4 x 27px + 3 x 4px Abstand = 120px). Mit 100px
     quollen sie nach links ueber den "Gewuenscht von"-Namen. Weiterhin
     fest, damit die Spalten zeilenuebergreifend fluchten. */
  flex: 0 0 124px;
  width: 124px;
  justify-content: flex-end;
}
.djview-row-note.has-alarm .djview-actions,
.djview-row-note.has-picker .djview-actions {
  flex: 0 1 auto;
  width: auto;
}
/* Buttons in der DJ-Anzeige duerfen etwas groesser sein als das normale
   .icon-btn anderswo (Musikfeed/Notizen-Tab) - hier ist die Zeile durch
   Icon-Kachel/Badge ohnehin schon so hoch, da soll der Button die Flaeche
   mit ausnutzen statt winzig drin zu "schwimmen". 4px Abstand (siehe .gap
   oben) bleibt bewusst bestehen, damit man beim Klicken nichts verfehlt. */
.djview-actions .icon-btn {
  width: 27px;
  height: 27px;
  font-size: 13px;
  border-radius: 7px;
}

/* ---- Positions-Nummer in der DJ-Anzeige: groesser, ganz rechts in der
   Zeile (steht als letztes Element im Markup - djview-main davor ist
   flex:1 und schiebt sie automatisch nach rechts). Position 1 ("als
   naechstes dran") wird gruen + leicht pulsierend hervorgehoben. ---- */
/* Nutzer-Feedback: "wenn die Zeile eh schon 1cm hoch ist, wieso ist die
   Positionsnummer so winzig" - deshalb jetzt align-self:stretch, damit das
   Badge die tatsaechlich vorhandene Zeilenhoehe (auch bei zweizeiligen
   Zeilen mit Kommentar) mit ausfuellt, statt klein mittig zu haengen. */
.pos-badge-lg {
  flex: 0 0 auto;
  align-self: stretch;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 38px;
  text-align: center;
  font-size: 14px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  padding: 4px 12px;
  border-radius: 8px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  color: var(--text-dim);
  margin-left: 6px;
}
/* v1.27.0 - .pos-badge-lg.next (gruener Pulsknopf fuer Platz 1) ist
   entfallen. Nutzer: Platz 1 soll aussehen wie alle - die DJ-Anzeige ist
   eine Liste, keine laufende Wiedergabe. app.js vergibt die Klasse nicht
   mehr; die Regel und ihre Puls-Animation sind mit raus. */

/* Auf schmalen Bildschirmen (Handy) reicht die Breite nicht fuer Titel +
   Interpret + Name + 3 Buttons in einer Zeile - dann wird zweizeilig
   umgebrochen, statt alles unleserlich zusammenzuquetschen. */
@media (max-width: 560px) {
  .djview-row { flex-wrap: wrap; row-gap: 3px; padding: 6px 10px; }
  .djview-main { order: 4; flex-basis: 100%; }
  .djview-from { order: 2; flex: 0 0 auto; width: auto; max-width: none; margin-left: auto; }
  .djview-actions { order: 3; flex: 0 0 auto; width: auto; }
  /* v1.13.3: Die Positionsnummer bleibt RECHTS - auch hier.
     Vorher zog "order: 1" sie nach ganz vorn. Auf dem Handy sprang sie
     damit an eine andere Stelle als am Rechner, und man suchte sie jedes
     Mal neu. Sie steht jetzt an beiden Stellen rechts aussen. */
  .pos-badge-lg { order: 7; margin-left: 6px; }
  /* Die Kategorie behaelt ihre feste Breite (siehe v1.13.3 weiter unten) -
     nur so beginnen die Cover in allen Zeilen auf derselben Linie. */
}

/* ---- Alarm/Blinklicht (DJ-Anzeige) ----
   Bewusst NICHT als Vollbild-Flash (wuerde beim Screensharen/Streamen
   auffallen), sondern als Effekt, der komplett INNERHALB der eigenen
   Notiz-Zeile bleibt: ein pulsierender "Blaulichtbalken" links in der Zeile
   plus ein dezentes Strobe der ganzen Zeilenumrandung, dazu spuerbarer
   Abstand zu den Nachbarn ("Spacer"), damit die Zeile sofort auffaellt ohne
   den Rest der Anzeige zu ueberdecken. Laeuft per CSS-Animation "infinite" -
   wie lange sie tatsaechlich sichtbar bleibt, steuert einzig der Server/JS
   ueber die has-alarm-Klasse (siehe djviewNoteRow + scheduleAlarmExpiryCheck
   in app.js), nicht die Animation selbst. */
.djview-row-note.has-alarm {
  position: relative;
  margin: 16px 0;
  border-radius: 8px;
  border-left: 3px solid transparent;
  animation: alarm-row-strobe 1s steps(2, jump-none) infinite;
}
.alarm-lightbar {
  flex: 0 0 auto;
  width: 10px;
  align-self: stretch;
  border-radius: 5px;
  margin-right: 2px;
  animation: alarm-bar-strobe 0.6s linear infinite;
  box-shadow: 0 0 14px 3px currentColor;
}
/* v1.28.25 - Die Staerke steckt als Deckkraft in den Farben selbst, nicht
   als opacity am Element: sonst wuerde auch der TEXT in der Zeile blass,
   und genau der muss lesbar bleiben. --alarm-staerke kommt aus den
   Einstellungen (Ansichten -> "Stärke des Blinklichts"), Vorgabe 1 = wie
   bisher. */
@keyframes alarm-row-strobe {
  0%, 100% { border-left-color: rgba(239, 68, 68, var(--alarm-staerke, 1));
             background: rgba(239, 68, 68, calc(0.14 * var(--alarm-staerke, 1))); }
  50%      { border-left-color: rgba(59, 130, 246, var(--alarm-staerke, 1));
             background: rgba(59, 130, 246, calc(0.14 * var(--alarm-staerke, 1))); }
}
@keyframes alarm-bar-strobe {
  0%, 100% { background: rgba(239, 68, 68, var(--alarm-staerke, 1)); color: rgba(239, 68, 68, var(--alarm-staerke, 1)); }
  50%      { background: rgba(59, 130, 246, var(--alarm-staerke, 1)); color: rgba(59, 130, 246, var(--alarm-staerke, 1)); }
}
.alarm-active-tag {
  font-size: 12px;
  font-weight: 800;
  color: #fff;
  background: var(--danger);
  padding: 4px 9px;
  border-radius: 999px;
  letter-spacing: .02em;
  white-space: nowrap;
  animation: alarm-tag-strobe 1s steps(2, jump-none) infinite;
}
@keyframes alarm-tag-strobe {
  0%, 100% { background: rgba(239, 68, 68, var(--alarm-staerke, 1)); }
  50%      { background: rgba(59, 130, 246, var(--alarm-staerke, 1)); }
}
.icon-btn.alarm-trigger { color: var(--warn); }
.icon-btn.alarm-trigger:hover { background: rgba(245, 158, 11, .18); }

.alarm-picker-row {
  flex-basis: 100%;
  order: 10;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 6px;
  padding: 8px 10px;
  background: var(--bg-elevated);
  border: 1px solid var(--warn);
  border-radius: 8px;
}
.djview-row-note.has-picker { flex-wrap: wrap; }
.alarm-picker-label { font-size: 12px; font-weight: 700; color: var(--warn); flex: 0 0 auto; }
.alarm-picker-chips { display: flex; gap: 4px; flex-wrap: wrap; }
.alarm-picker-custom { display: flex; align-items: center; gap: 4px; }
.alarm-picker-custom input[type="number"] {
  width: 84px;
  padding: 5px 8px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 13px;
}
.alarm-picker-custom select {
  padding: 5px 6px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 13px;
}

@media (max-width: 560px) {
  .djview-row-note.has-alarm { flex-wrap: wrap; }
}

/* Derselbe Alarm-Zustand ist bewusst NICHT nur in der DJ-Anzeige sicht- und
   ausloesbar, sondern auch im Notizen-Tab (Moderationsuebersicht) und im
   Musikfeed - meist ist es das Mod-Team, das den Alarm ausloest, waehrend
   der DJ die DJ-Anzeige offen hat. Gleiche Strobe-Keyframes, nur an die
   jeweilige Zeilenform angepasst. */
.timeline-item.timeline-note.has-alarm {
  position: relative;
  margin-top: 12px;
  margin-bottom: 12px;
  border-color: transparent;
  animation: alarm-row-strobe 1s steps(2, jump-none) infinite;
}
.dash-note-row.has-alarm {
  position: relative;
  margin: 10px 0;
  border-color: transparent;
  animation: alarm-row-strobe 1s steps(2, jump-none) infinite;
}

/* ---- Drag & Drop (Pointer-Events, siehe app.js initDragHandles) ---- */
.drag-handle {
  flex: 0 0 auto;
  cursor: grab;
  color: var(--text-dim);
  font-size: 14px;
  padding: 2px 4px;
  touch-action: none; /* verhindert Scrollen auf Touch waehrend des Ziehens */
  user-select: none;
  -webkit-user-select: none;
}
.drag-handle:hover { color: var(--text); }
.reorder-col .drag-handle { font-size: 10px; text-align: center; padding: 0; }

.dragging-source { opacity: .35; }

.drag-over-top { box-shadow: inset 0 3px 0 0 var(--accent); }
.drag-over-bottom { box-shadow: inset 0 -3px 0 0 var(--accent); }

.drag-ghost {
  position: fixed;
  z-index: 1200;
  pointer-events: none;
  background: var(--accent);
  color: var(--accent-text);
  font-size: 13px;
  font-weight: 700;
  padding: 6px 12px;
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0,0,0,.5);
  max-width: 260px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---- Musik-Autocomplete ---- */
/* Nutzer-Feedback: das Dropdown war bisher exakt so breit wie das Titel-Feld
   selbst - bei langen Titeln/Interpreten (oder erst recht bei der
   kombinierten Titel+Interpret-Suche, wo ja ohnehin beide Infos pro Zeile
   angezeigt werden) wurde viel per Ellipsis abgeschnitten. "right:auto" +
   eine feste Mindestbreite statt "right:0" laesst das Dropdown ueber die
   Breite des Titel-Feldes hinaus wachsen (es haengt technisch weiterhin nur
   unter dem Titel-Feld, ueberlappt bei Bedarf aber sichtbar das
   Interpret-Feld daneben - das ist gewollt, das Dropdown liegt ja ohnehin
   schon "ueber" allem anderen). min()/vw sorgt dafuer, dass auf schmalen
   Bildschirmen (Handy, wo Felder ohnehin untereinanderstehen) nichts seitlich
   abgeschnitten wird. */
.autocomplete-dropdown {
  position: absolute;
  left: 0; right: auto; top: 100%;
  /* nie schmaler als das Feld selbst (max(100%, ...)), aber mindestens
     560px breit, falls das Feld schmaler ist - auf kleinen Bildschirmen
     durch 92vw gedeckelt. */
  width: max(100%, min(560px, 92vw));
  margin-top: 4px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 12px 30px rgba(0,0,0,.5);
  z-index: 1100;
  max-height: 280px;
  overflow-y: auto;
}
.autocomplete-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  min-height: 52px;
  cursor: pointer;
  border-bottom: 1px solid var(--border);
  border-left: 3px solid transparent;
}
.autocomplete-item:last-child { border-bottom: none; }
.autocomplete-item:hover { background: var(--bg-card); }

/* New layout with content on left, badge and cover on right */
.autocomplete-content {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1 1 auto;
  min-width: 0;
}

/* Exact match highlighting - BEST MATCH. Nutzt die tatsaechlich eingestellte
   Akzentfarbe (--accent-rgb wird in app.js/applyTheme() live aus --accent
   berechnet) statt einer fest verdrahteten Farbe - Fallback-Werte hier
   entsprechen nur dem Standard-Lila (#8b5cf6), falls JS noch nicht geladen hat. */
/* Bugfix/Nutzer-Feedback: vorher ging die farbige Tönung ueber die GANZE
   Zeile, auch hinter dem (bei "nativ" transparenten) Anbieter-Icon - dadurch
   wirkte z.B. die weisse Tidal-Welle auf farbigem Untergrund "matschig".
   Jetzt nur noch ein nach rechts ausblendender Farbverlauf, der spaetestens
   dort transparent wird, wo Icon/Cover anfangen (die liegen rechts) - der
   farbliche linke Rand (border-left) bleibt als klar erkennbarer "Balken"
   bestehen. */
.autocomplete-item-exact {
  border-left-color: var(--accent, #8b5cf6);
  border-left-width: 5px;
  background: linear-gradient(to right, rgba(var(--accent-rgb, 139,92,246), 0.32), rgba(var(--accent-rgb, 139,92,246), 0.1) 45%, transparent 68%);
}
.autocomplete-exact-text {
  font-weight: 700 !important;
  color: var(--accent, #8b5cf6);
}

/* Strict match highlighting - GOOD MATCH (z.B. Praefix-Treffer "fly" ->
   "Fly Like an Eagle", oder bei kombinierter Suche: Titel + Interpret
   passen beide). Nutzer-Feedback: vorher nur eine transparentere Version
   DERSELBEN Akzentfarbe wie beim Volltreffer - kaum zu unterscheiden ohne
   genau hinzuschauen. Jetzt ein leicht in Richtung Nachbarfarbton
   verschobener, aber klar verwandter Ton (--accent-strict-rgb, siehe
   app.js/applyTheme() - per HSL-Rotation aus der echten Akzentfarbe
   berechnet), damit "guter Treffer" auf den ersten Blick von "Volltreffer"
   unterscheidbar ist, ohne wie eine komplett andere/fremde Farbe zu wirken. */
.autocomplete-item-strict {
  border-left-color: rgb(var(--accent-strict-rgb, 124, 92, 220));
  background: linear-gradient(to right, rgba(var(--accent-strict-rgb, 124,92,220), 0.26), transparent 62%);
}
.autocomplete-strict-text {
  font-weight: 600;
  color: rgb(var(--accent-strict-rgb, 124, 92, 220));
}

/* Partial match highlighting - SCHWAECHERES MATCH (Eingabe steckt nur
   irgendwo mitten im Feld, z.B. "fly" in "Butterfly" - nicht am Anfang).
   Nochmal staerker in Richtung Nachbarfarbton verschoben + blasser als
   .autocomplete-item-strict (--accent-partial-rgb, siehe app.js), damit auf
   einen Blick klar ist "das ist nur ein schwacher Treffer" - aber immer noch
   sichtbar farbig statt komplett grau wie die Fallback-Stufe, weil hier ja
   zumindest textlich etwas uebereinstimmt. */
.autocomplete-item-partial {
  border-left-color: rgb(var(--accent-partial-rgb, 160, 140, 210));
  border-left-width: 3px;
  background: linear-gradient(to right, rgba(var(--accent-partial-rgb, 160,140,210), 0.2), transparent 55%);
}
.autocomplete-partial-text {
  color: rgb(var(--accent-partial-rgb, 160, 140, 210));
  font-weight: 500;
}

/* Fallback match highlighting - PARTIAL MATCH (artist didn't match, only title) */
.autocomplete-item-fallback {
  opacity: 0.7;
  border-left-color: #666;
}
.autocomplete-fallback-text {
  color: #999;
}
.fallback-hint {
  font-size: 10px;
  color: #666;
  font-style: italic;
  margin-left: 4px;
}

/* Echtes Anbieter-Logo (Tidal/Spotify/Apple Music, siehe public/icons/providers/
   + providerIconUrl() im Frontend) statt Text-Badge - 36px (nochmal
   vergroessert, Nutzer-Feedback: "die Icons koennten groesser sein, aehnlich
   der Albenbilder") + die Chip-Grafiken selbst wurden mit deutlich weniger
   Rand neu erzeugt, damit das eigentliche Symbol darin auch wirklich groesser
   wirkt statt nur die umgebende Box. Nur sichtbar, wenn
   settings.musicSearch.showLogos an ist. */
.autocomplete-logo {
  width: 36px; height: 36px;
  flex: 0 0 auto;
  object-fit: contain;
  display: block;
}

/* Album-/Songcover, ganz am Zeilenende (Nutzer-Wunsch: "kleiner, ans Ende
   statt vorne die Musiknote") - nur sichtbar, wenn showCovers an ist. */
.autocomplete-cover {
  width: 32px; height: 32px;
  border-radius: 6px;
  flex: 0 0 auto;
  object-fit: cover;
  margin-left: 4px;
  background: var(--bg-card);
}

.autocomplete-text {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.autocomplete-title {
  font-size: 13px;
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.autocomplete-artist {
  font-size: 12px;
  color: var(--text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---- Warnliste: eingeschraenkte Kuenstler (siehe lib/restrictedArtists.js) ----
   "exact" = sicherer Treffer -> kraeftiges Rot, kann nicht uebersehen werden.
   "fuzzy" = nur aehnlich, muss geprueft werden -> Bernstein/Orange, etwas
   zurueckhaltender, aber immer noch klar sichtbar. */
.restricted-badge {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 6px 10px;
  font-size: 13px;
  font-weight: 800;
  padding: 8px 10px;
  border-radius: 8px;
  margin: 8px 0;
  line-height: 1.3;
}
.restricted-badge-text { flex: 1 1 auto; }
.restricted-badge.restricted-exact {
  background: rgba(239, 68, 68, .22);
  color: #ffb4b4;
  border: 1px solid var(--danger);
}
.restricted-badge.restricted-fuzzy {
  background: rgba(245, 158, 11, .18);
  color: #ffd699;
  border: 1px solid var(--warn);
}
/* Nachdem ein Mod/DJ den Treffer als "nicht zutreffend" markiert hat: viel
   ruhiger/dezenter als die rote/gelbe Warnung, aber nicht komplett
   verschwunden - der "Wieder einblenden"-Button bleibt als Undo sichtbar. */
.restricted-badge.restricted-dismissed {
  background: var(--bg-elevated);
  color: var(--text-dim);
  border: 1px dashed var(--border);
  font-weight: 600;
}
.restricted-dismiss-btn {
  flex: 0 0 auto;
  font-size: 11px;
  font-weight: 800;
  padding: 4px 9px;
  border-radius: 999px;
  border: 1px solid currentColor;
  background: rgba(0, 0, 0, .15);
  color: inherit;
  cursor: pointer;
  white-space: nowrap;
}
.restricted-dismiss-btn:hover { background: rgba(0, 0, 0, .3); }
/* Platzsparende Variante fuer die DJ-Anzeige (ein Zeile, kein Umbruch -
   die Zeilenhoehe dort ist bewusst knapp, damit viel auf einen Blick
   passt; laesst sich per Hover/lange Kompakt-Ansicht trotzdem vollstaendig
   ueber "title" auslesen). */
.restricted-badge-compact {
  padding: 2px 8px;
  margin: 2px 0 0;
  font-size: 11px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  display: block;
}

.wish-card.restricted-exact,
.wish-row.restricted-exact,
.djview-row.restricted-exact {
  border-color: var(--danger) !important;
  box-shadow: 0 0 0 1px var(--danger), 0 0 16px rgba(239, 68, 68, .35);
}
.wish-card.restricted-fuzzy,
.wish-row.restricted-fuzzy,
.djview-row.restricted-fuzzy {
  border-color: var(--warn) !important;
  box-shadow: 0 0 0 1px var(--warn), 0 0 12px rgba(245, 158, 11, .25);
}

.row-restricted-badge {
  flex: 0 0 auto;
  font-size: 11px;
  font-weight: 800;
  padding: 2px 7px;
  border-radius: 999px;
  white-space: nowrap;
}
.row-restricted-badge.exact {
  background: rgba(239, 68, 68, .22);
  color: #ffb4b4;
  border: 1px solid var(--danger);
}
.row-restricted-badge.fuzzy {
  background: rgba(245, 158, 11, .18);
  color: #ffd699;
  border: 1px solid var(--warn);
}

/* Hinweis "Interpret wurde in dieser Session schon gespielt" (siehe
   attachCategoryInfo() in lib/models.js) - bewusst ein neutraler
   Info-Ton (Blau), nicht Rot/Gelb wie die Warnliste, da es sich nur um
   einen Hinweis handelt, keine Warnung. Erscheint NIE auf der
   oeffentlichen Liste (nur staff-Ansichten, siehe app.js
   alreadyPlayedBadge()). */
.already-played-badge {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  font-size: 11px;
  font-weight: 800;
  padding: 2px 7px;
  border-radius: 999px;
  white-space: nowrap;
  background: rgba(56, 189, 248, .16);
  color: #a9e3fb;
  border: 1px solid #38bdf8;
}
.already-played-badge.compact {
  padding: 1px 5px;
  font-size: 12px;
}

/* Chip-Liste in den Einstellungen: jeder Warnlisten-Eintrag einzeln mit
   eigenem Loesch-Button, damit man nicht immer das ganze Textfeld anfassen
   muss, um nur einen einzelnen Namen zu entfernen. */
.restricted-chip-list {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 10px;
}
.restricted-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  color: var(--text);
  font-size: 13px;
  padding: 4px 6px 4px 10px;
  border-radius: 999px;
}
.chip-remove {
  border: none;
  background: rgba(239, 68, 68, .18);
  color: #ffb4b4;
  width: 18px; height: 18px;
  border-radius: 50%;
  font-size: 11px;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
}
.chip-remove:hover { background: var(--danger); color: #fff; }

/* ---- Notizen/Kommentare: kombinierte Zeitleiste (Notizen-Tab) ----
   Songs und Notizen im selben "reihenfolge"-Zahlenraum, siehe app.js
   combinedTimelineHtml()/getTimelineItems() im Backend. Notizen sind leicht
   nach links, Songs leicht nach rechts eingerueckt ("zwei Lineale"), damit
   man beide Typen auf einen Blick auseinanderhaelt. */
.timeline-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.timeline-item {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  padding: 8px 10px;
  border-radius: 8px;
  border: 1px solid var(--border);
  font-size: 13px;
}
.timeline-item.timeline-wish {
  margin-left: 18px;
  background: var(--bg-elevated);
  border-left: 3px solid var(--cat-color, var(--border));
  opacity: .8;
  /* v1.28.21: Song-Zeilen platzsparender - Saschas Vorgabe 10.09.: "die
     chips koennen von der hoehe her etwas schmaler von den liedern".
     Bewusst NUR die Lieder - Notizen-Zeilen bleiben unveraendert hoch. */
  padding-block: 2px;
  gap: 6px;
  font-size: 12.5px;
}
.timeline-item.timeline-wish .badge { padding: 1px 7px; font-size: 10.5px; }
.timeline-item.timeline-wish .mini-icon { font-size: 12px; }
.timeline-item.timeline-note {
  margin-right: 18px;
  background: color-mix(in srgb, var(--accent) 6%, var(--bg-card));
  border-left: 3px solid var(--cat-color, #38bdf8);
}
.timeline-item-text { font-weight: 600; flex: 1 1 auto; min-width: 120px; }
.timeline-item.timeline-wish .timeline-item-text { font-weight: 500; }

.note-label-chip {
  display: inline-flex;
  align-items: center;
  font-size: 11px;
  font-weight: 800;
  padding: 2px 8px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--cat-color, #38bdf8) 20%, transparent);
  color: var(--cat-color, #38bdf8);
  border: 1px solid color-mix(in srgb, var(--cat-color, #38bdf8) 45%, transparent);
  flex: 0 0 auto;
}
/* Nur fuer die oeffentliche Liste (siehe publicNoteItem in app.js): zeigt
   dieselbe Kategorie-Farbe wie .note-label-chip, aber OHNE den Label-Text -
   Zuschauer muessen den internen Notiz-Kategorienamen nicht lesen koennen,
   ein Mod erkennt die Kategorie beim Druebersehen trotzdem an der Farbe. */
.note-color-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--cat-color, #38bdf8);
  border: 1px solid color-mix(in srgb, var(--cat-color, #38bdf8) 60%, black);
  flex: 0 0 auto;
}
.note-link-chip {
  font-size: 11px;
  color: var(--accent);
  background: rgba(139, 92, 246, .12);
  border: 1px solid rgba(139, 92, 246, .3);
  border-radius: 999px;
  padding: 2px 8px;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.note-von {
  font-size: 11px;
  color: var(--text-dim);
  flex: 0 0 auto;
}

.visibility-badge {
  font-size: 10px;
  font-weight: 800;
  padding: 2px 8px;
  border-radius: 999px;
  flex: 0 0 auto;
  white-space: nowrap;
}
.visibility-badge.private { background: var(--bg-elevated); color: var(--text-dim); border: 1px solid var(--border); }
.visibility-badge.public { background: rgba(34,197,94,.15); color: var(--success); border: 1px solid rgba(34,197,94,.3); }
.visibility-badge.locked { background: rgba(239,68,68,.15); color: var(--danger); border: 1px solid rgba(239,68,68,.3); }

.timeline-note-actions {
  display: flex;
  gap: 3px;
  flex: 0 0 auto;
  margin-left: auto;
}
.publish-confirm-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1 1 100%;
  background: rgba(239,68,68,.08);
  border: 1px dashed var(--danger);
  border-radius: 6px;
  padding: 6px 10px;
  margin-top: 4px;
}
.publish-confirm-row .checkbox-row { font-size: 12px; margin-bottom: 0; white-space: nowrap; }

/* ---- Notizen in der DJ-Anzeige (leicht nach links eingerueckt, eigene
   Akzentfarbe je Notiz statt Kategorie-Farbe) ---- */
.djview-row-wish { margin-left: 14px; }
.djview-row-note {
  margin-right: 14px;
  border-left-color: var(--cat-color, #38bdf8) !important;
  background: color-mix(in srgb, var(--accent) 5%, var(--bg-card));
}

/* ---- Icon-Gutter: feste, farbige Icon-Kachel ganz vorne in jeder
   DJ-Anzeige-Zeile (statt eines kleinen Icons mitten im Text) - macht Songs
   vs. Notizen auch bei sehr kompakten Zeilen auf den ersten Blick
   unterscheidbar, weil man nur die Spalte ganz links "runterscannen" muss.
   Notizen behalten dabei ihre eigene, frei waehlbare Farbe (--cat-color =
   n.farbe) - die Kachel ist nur eine weitere Stelle, an der diese Farbe
   auftaucht, keine neue Farbquelle. */
/* v1.2.4: von 28 auf 34 Pixel, damit das Albumbild darin erkennbar ist.
   Songs und Notizen behalten dieselbe Groesse, sonst stuenden ihre Zeilen
   nicht mehr buendig untereinander. */
.djview-icon-gutter {
  flex: 0 0 34px;
  width: 34px;
  height: 34px;
  border-radius: 7px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 15px;
  line-height: 1;
  overflow: hidden;
}
.djview-icon-wish {
  background: color-mix(in srgb, var(--cat-color, var(--accent)) 20%, transparent);
}
/* Steckt ein echtes Bild in der Kachel, stoert die eingefaerbte Flaeche
   dahinter nur - sie schimmert an den Raendern durch. */
.djview-icon-wish:has(img.wish-cover) {
  background: none;
}
.djview-icon-note {
  background: color-mix(in srgb, var(--cat-color, #38bdf8) 20%, transparent);
}
/* Bei aktivem Alarm wird die Icon-Kachel selbst zur "Blinklicht"-Flaeche
   (siehe djviewNoteRow) - ersetzt die vorher separate .alarm-lightbar-Leiste
   in dieser Ansicht, damit der Effekt sauber innerhalb der ohnehin
   vorhandenen Icon-Spalte bleibt statt eine zusaetzliche Leiste zu brauchen. */
.djview-icon-alarm {
  animation: alarm-bar-strobe 0.6s linear infinite;
  box-shadow: 0 0 14px 3px currentColor;
}
/* Wenn "Blinklicht in dieser Ansicht anzeigen" fuer die DJ-Anzeige in den
   Einstellungen deaktiviert ist (settings.alarmDisplay.djAnzeige): Alarm
   bleibt weiterhin erkennbar (rote Kachel, 🚨-Icon, Stop-Button), nur eben
   ohne Strobe-Animation/Leuchten. */
.djview-icon-alarm.icon-alarm-static {
  animation: none;
  box-shadow: none;
  background: rgba(239, 68, 68, .3);
  color: #ef4444;
}

/* ---- Oeffentliche Hinweise (nur wenn Einstellungen das erlauben UND eine
   Notiz explizit veroeffentlicht wurde) ---- */
.public-notes-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.public-note-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border-radius: 8px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--cat-color, #38bdf8);
  background: var(--bg-elevated);
  font-size: 14px;
}
/* Nur sichtbar, wenn settings.public.showAlarmToPublic an ist (siehe
   publicNoteItem in app.js) - der Server liefert das alarmActive-Feld sonst
   gar nicht erst mit. */
.public-note-item.has-alarm {
  position: relative;
  margin: 10px 0;
  border-color: transparent;
  animation: alarm-row-strobe 1s steps(2, jump-none) infinite;
}

/* ---- Notiz-Karten-Optik: unterscheidet Notizen STRUKTURELL (nicht nur
   farblich) von Songwunsch-Zeilen, ueberall dort wo genug Platz fuer eine
   eigene kleine Karte ist (Musikfeed, Notizen-Tab, oeffentliche Liste).
   "open" = normale Notiz, die laut Einstellungen ggf. veroeffentlicht werden
   KANN (neverPublish=false) -> gestrichelter "Post-it"-Rahmen in der eigenen,
   frei waehlbaren Farbe. "locked" = eine Notiz, die nie oeffentlich sichtbar
   werden kann ("gesperrt"/neverPublish, z.B. sensible interne Hinweise) ->
   auffaelliges Gefahren-Streifenmuster, ebenfalls in der eigenen Farbe (z.B.
   gelblich waehlen fuer "wichtig", rot fuer "sensibel" - die Farbe bleibt
   komplett frei waehlbar, nur das Streifenmuster ist fest an "gesperrt"
   gekoppelt). Ein aktiver Alarm (siehe .has-alarm oben) ueberlagert das
   durch die eigene Animation weiterhin wie gehabt. */
/* OPEN NOTES - Öffentlich veröffentlicht (gestrichelt, keine Schraffur) */
.dash-note-row.note-card-open,
.timeline-item.timeline-note.note-card-open,
.public-note-item.note-card-open {
  border: 1.5px dashed var(--cat-color, #38bdf8);
  border-radius: 4px 14px 4px 14px;
  background: var(--bg-elevated);
  position: relative;
}

.dash-note-row.note-card-open::before,
.timeline-item.timeline-note.note-card-open::before,
.public-note-item.note-card-open::before {
  content: '';
  position: absolute;
  inset: 0;
  /* "closest-side" statt (Default) "farthest-corner": Ohne die explizite
     Size-Keyword strecken sich sehr flache Zeilen (Musikfeed/DJ-Anzeige)
     und hoehere Karten (Liste/Notizen-Tab) beim gleichen %-Wert optisch
     unterschiedlich stark, weil "farthest-corner" die Ellipse an der
     Boxdiagonale ausrichtet. "closest-side" orientiert sich stattdessen an
     der jeweils naheren Kante (Breite/2 bzw. Hoehe/2), wodurch der
     Farbverlauf ueber verschiedene Zeilenformen hinweg deutlich
     vergleichbarer wirkt. */
  background: radial-gradient(
    ellipse closest-side at center,
    transparent 0%,
    transparent calc(var(--note-gradient-radius, 40) * 1%),
    var(--cat-color, #38bdf8) 100%
  );
  opacity: calc(var(--note-gradient-opacity, 12) / 100);
  pointer-events: none;
  border-radius: 4px 14px 4px 14px;
}

.dash-note-row.note-card-open > *,
.timeline-item.timeline-note.note-card-open > *,
.public-note-item.note-card-open > * {
  position: relative;
  z-index: 1;
}
.dash-note-row.note-card-locked,
.timeline-item.timeline-note.note-card-locked,
.public-note-item.note-card-locked {
  position: relative;
  border: 1.5px solid var(--cat-color, #ef4444);
  border-radius: 6px;
  background-color: var(--bg-elevated);
  overflow: hidden;
}
/* Nutzer-Feedback: die Schraffur ueberdeckte Titel/Kategorie und war
   dadurch schlecht lesbar. Die Streifen sitzen deshalb jetzt auf einem
   eigenen ::before-Layer HINTER dem eigentlichen Inhalt (nicht mehr als
   background-image direkt auf der Zeile) und werden per Maske nur an
   beiden Enden sichtbar gehalten (Icon+Kategorie links, Buttons/Position
   rechts, falls vorhanden) - dazwischen (Titel/Text) komplett klar. Wichtig:
   eine Maske auf der Zeile SELBST wuerde auch den Text mit ausblenden (Maske
   wirkt auf die komplette Box inkl. Kindelemente) - deshalb bewusst ein
   separates, rein dekoratives Pseudo-Element dafuer. */
.dash-note-row.note-card-locked::before,
.timeline-item.timeline-note.note-card-locked::before,
.public-note-item.note-card-locked::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: repeating-linear-gradient(
    135deg,
    color-mix(in srgb, var(--cat-color, #ef4444) 30%, transparent),
    color-mix(in srgb, var(--cat-color, #ef4444) 30%, transparent) 10px,
    transparent 10px,
    transparent 20px
  );
  -webkit-mask-image: linear-gradient(to right, black 0%, black 8%, transparent 16%, transparent 84%, black 92%, black 100%);
  mask-image: linear-gradient(to right, black 0%, black 8%, transparent 16%, transparent 84%, black 92%, black 100%);
}
/* DJ-Anzeige: gleiche "Post-it"/Gefahren-Karten-Optik wie in Notizen-Tab und
   Liste (siehe Nutzer-Feedback: die gestrichelte/gestreifte Umrandung soll
   auch hier als klar sichtbare "Trennlinie" rund um die Notiz-Zeile zu sehen
   sein, abgerundet wie dort). Ersetzt dafuer bewusst den duennen
   Tabellen-Rahmen (border-bottom/border-left) der Songzeilen komplett durch
   einen eigenen Rahmen rundum + etwas vertikalem Abstand zu den
   Song-Nachbarzeilen, damit die Karte sichtbar "heraussticht" statt buendig
   in der Tabelle zu verschwinden - genau wie es beim aktiven Alarm (siehe
   .has-alarm) schon gemacht wird, nur dezenter/kompakter. */
.djview-row-note.note-card-open {
  margin: 5px 6px;
  border: 1.5px dashed var(--cat-color, #38bdf8);
  border-radius: 4px 14px 4px 14px;
  background: var(--bg-elevated);
  position: relative;
}

.djview-row-note.note-card-open::before {
  content: '';
  position: absolute;
  inset: 0;
  /* "closest-side" statt (Default) "farthest-corner": Ohne die explizite
     Size-Keyword strecken sich sehr flache Zeilen (Musikfeed/DJ-Anzeige)
     und hoehere Karten (Liste/Notizen-Tab) beim gleichen %-Wert optisch
     unterschiedlich stark, weil "farthest-corner" die Ellipse an der
     Boxdiagonale ausrichtet. "closest-side" orientiert sich stattdessen an
     der jeweils naheren Kante (Breite/2 bzw. Hoehe/2), wodurch der
     Farbverlauf ueber verschiedene Zeilenformen hinweg deutlich
     vergleichbarer wirkt. */
  background: radial-gradient(
    ellipse closest-side at center,
    transparent 0%,
    transparent calc(var(--note-gradient-radius, 40) * 1%),
    var(--cat-color, #38bdf8) 100%
  );
  opacity: calc(var(--note-gradient-opacity, 12) / 100);
  pointer-events: none;
  border-radius: 4px 14px 4px 14px;
}

.djview-row-note.note-card-open > * {
  position: relative;
  z-index: 1;
}

/* Live-Vorschau fuer die "Farbgradient"-Regler in Einstellungen (siehe
   perPageGradientFieldHtml() in app.js) - Nutzer-Feedback: beim Ziehen der
   Regler passierte auf der Einstellungen-Seite selbst sichtbar NICHTS, weil
   dort nirgends eine echte Notiz-Karte gerendert wird, an der man den Effekt
   haette sehen koennen (das JS aktualisierte zwar korrekt die CSS-Variable,
   nur eben ohne jedes Element, das sie tatsaechlich benutzt). Diese kleine
   Box benutzt bewusst EXAKT dieselbe radial-gradient/opacity-Formel wie die
   echten Notiz-Karten oben (nur eigener Klassenname + eigene, lokal
   gescopte --note-gradient-opacity Variable statt der globalen auf
   documentElement), damit die Vorschau garantiert 1:1 zeigt, wie stark der
   Effekt bei einem bestimmten %-Wert wirklich ausfaellt. */
.gradient-preview-demo {
  position: relative;
  flex: 0 0 auto;
  width: 150px;
  height: 52px;
  border: 1.5px dashed var(--cat-color, #8b5cf6);
  border-radius: 4px 14px 4px 14px;
  background: var(--bg-elevated);
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 0 8px;
}
.gradient-preview-demo::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(
    ellipse closest-side at center,
    transparent 0%,
    transparent calc(var(--note-gradient-radius, 40) * 1%),
    var(--cat-color, #8b5cf6) 100%
  );
  opacity: calc(var(--note-gradient-opacity, 12) / 100);
  pointer-events: none;
}
.gradient-preview-demo span {
  position: relative;
  z-index: 1;
  font-size: 11px;
  color: var(--text-dim, #9ca3af);
}

/* Nutzer-Wunsch: zusaetzlich zur runden/ovalen Form (Standard, siehe oben)
   auch eine "Rechteck/Balken"-Form waehlbar machen - der Farbrand folgt
   dann den geraden Kanten (links/rechts/oben/unten) statt einer Ellipse.
   radial-gradient kennt kein natives Rechteck, deshalb hier stattdessen
   ZWEI uebereinandergelegte linear-gradients (einer horizontal, einer
   vertikal) - jeder ist in der Mitte komplett durchsichtig und wird erst
   zu den jeweiligen Kanten hin farbig. Weil beide dieselbe --cat-color
   benutzen, entsteht an den Ecken (wo sich beide ueberlappen) keine
   Farbmischung/kein sichtbarer Bruch, nur derselbe Farbton wie an den
   Kanten - sieht sauber aus, ohne dass eine echte Diagonal-Berechnung
   noetig waere. --note-gradient-radius hat hier dieselbe Bedeutung wie bei
   der Ellipse: 0 = kein farbfreier Bereich (komplett eingefaerbt), 100 =
   farbfreier Bereich so gross wie die ganze Karte (keine Farbe sichtbar).
   Nutzer-Beispiel: 10cm breiter Balken, Radius 50 -> die mittleren 5cm
   bleiben klar, je 2,5cm an den Seiten werden gefaerbt. */
html.note-gradient-rect .note-card-open::before {
  background:
    linear-gradient(
      to right,
      var(--cat-color, #38bdf8) 0%,
      transparent calc(50% - var(--note-gradient-radius, 40) * 0.5%),
      transparent calc(50% + var(--note-gradient-radius, 40) * 0.5%),
      var(--cat-color, #38bdf8) 100%
    ),
    linear-gradient(
      to bottom,
      var(--cat-color, #38bdf8) 0%,
      transparent calc(50% - var(--note-gradient-radius, 40) * 0.5%),
      transparent calc(50% + var(--note-gradient-radius, 40) * 0.5%),
      var(--cat-color, #38bdf8) 100%
    );
}
/* Gleiche Rechteck-Formel fuer die Live-Vorschau-Box in Einstellungen (siehe
   .gradient-preview-demo oben) - eigene, lokal gescopte Modifier-Klasse
   statt der globalen html.note-gradient-rect, damit mehrere Vorschau-Boxen
   mit unterschiedlicher Form gleichzeitig auf der Einstellungen-Seite
   nebeneinander korrekt angezeigt werden koennen. */
.gradient-preview-demo.gradient-preview-rect::before {
  background:
    linear-gradient(
      to right,
      var(--cat-color, #8b5cf6) 0%,
      transparent calc(50% - var(--note-gradient-radius, 40) * 0.5%),
      transparent calc(50% + var(--note-gradient-radius, 40) * 0.5%),
      var(--cat-color, #8b5cf6) 100%
    ),
    linear-gradient(
      to bottom,
      var(--cat-color, #8b5cf6) 0%,
      transparent calc(50% - var(--note-gradient-radius, 40) * 0.5%),
      transparent calc(50% + var(--note-gradient-radius, 40) * 0.5%),
      var(--cat-color, #8b5cf6) 100%
    );
}

.djview-row-note.note-card-locked {
  position: relative;
  margin: 6px 6px;
  border: 1.5px solid var(--cat-color, #ef4444);
  border-radius: 6px;
  background-color: var(--bg-elevated);
  overflow: hidden;
}
/* Gleicher "Streifen auf eigenem ::before-Layer, Warnrand an beiden Enden,
   Mitte klar"-Effekt wie oben bei Musikfeed/Notizen-Tab/oeffentlicher Liste
   (siehe ausfuehrlichen Kommentar dort). */
.djview-row-note.note-card-locked::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: repeating-linear-gradient(
    135deg,
    color-mix(in srgb, var(--cat-color, #ef4444) 22%, transparent),
    color-mix(in srgb, var(--cat-color, #ef4444) 22%, transparent) 8px,
    transparent 8px,
    transparent 16px
  );
  -webkit-mask-image: linear-gradient(to right, black 0%, black 8%, transparent 16%, transparent 84%, black 92%, black 100%);
  mask-image: linear-gradient(to right, black 0%, black 8%, transparent 16%, transparent 84%, black 92%, black 100%);
}
.djview-icon-gutter.djview-icon-note.icon-locked {
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--cat-color, #ef4444) 70%, transparent);
}

/* ---- Kompaktere Alarm-Auswahl speziell in der DJ-Anzeige (dort ist die
   Zeile ohnehin schon dicht befuellt - siehe Nutzer-Feedback "das koennte
   noch etwas kompakter sein"). Notizen-Tab/Musikfeed behalten die normal
   grossen Buttons, dort ist mehr Platz. ---- */
.djview-row-note .alarm-picker-row {
  padding: 4px 8px;
  gap: 5px;
  margin-top: 3px;
}
.djview-row-note .alarm-picker-label { font-size: 11px; }
.djview-row-note .alarm-picker-chips { gap: 3px; }
.djview-row-note .alarm-picker-chips .btn.small,
.djview-row-note .alarm-picker-custom .btn.small,
.djview-row-note > .alarm-picker-row .btn.small {
  padding: 3px 7px;
  font-size: 11px;
}
.djview-row-note .alarm-picker-custom { gap: 3px; }
.djview-row-note .alarm-picker-custom input[type="number"] {
  width: 62px;
  padding: 3px 6px;
  font-size: 11px;
}
.djview-row-note .alarm-picker-custom select {
  padding: 3px 4px;
  font-size: 11px;
}

/* ---- Design-Tab (Farbschema, siehe app.js viewDesign) ---- */
.design-layout {
  display: flex;
  gap: 24px;
  flex-wrap: wrap;
  margin-top: 14px;
}
.design-controls { flex: 1 1 260px; min-width: 240px; }
.design-preview { flex: 1 1 300px; min-width: 260px; }

.accent-swatches { display: flex; gap: 6px; margin-left: 10px; flex-wrap: wrap; }
.accent-swatches .swatch {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 2px solid var(--border);
  cursor: pointer;
  padding: 0;
}
.accent-swatches .swatch.active { border-color: var(--text); box-shadow: 0 0 0 2px var(--bg); }

.settings-advanced { margin-top: 14px; }
.settings-advanced summary {
  cursor: pointer;
  font-weight: 600;
  color: var(--text-dim);
  font-size: 13px;
}

/* ---- Pro-Seite-Einstellungen in viewEinstellungen: jede Seite (Liste,
   Musikfeed, Notizen-Tab, DJ-Anzeige) bekommt ihre eigene, einklappbare
   Kachel (siehe Nutzer-Wunsch "jede Seite soll eigene, detaillierte
   Einstellungen bekommen"). Bewusst <details>/<summary> statt eigenem
   JS-Toggle-State - bleibt so einfach und foldable-Zustand geht beim
   Re-Render nicht verloren (Browser haelt das selbst). ---- */
.settings-page-section {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-elevated);
  margin-bottom: 14px;
  overflow: hidden;
}
.settings-page-section summary {
  cursor: pointer;
  padding: 12px 16px;
  font-weight: 700;
  font-size: 14px;
  display: flex;
  align-items: center;
  gap: 8px;
  list-style: none;
}
.settings-page-section summary::-webkit-details-marker { display: none; }
.settings-page-section summary::before {
  content: '▸';
  color: var(--text-dim);
  font-size: 11px;
  transition: transform .15s ease;
}
.settings-page-section[open] summary::before { transform: rotate(90deg); }
.settings-page-section summary .settings-page-sub {
  font-weight: 400;
  font-size: 12px;
  color: var(--text-dim);
  margin-left: 2px;
}
.settings-page-section .settings-page-body {
  padding: 4px 16px 16px;
  border-top: 1px solid var(--border);
}

/* Vorschau-Box: eigene, in sich geschlossene "Mini-Instanz" der Palette -
   ueberschreibt --bg/--bg-card/... NUR innerhalb dieser Box (nicht global),
   damit man frei rumprobieren kann, ohne die echte Seite zu veraendern,
   bevor man auf "Speichern & anwenden" klickt (siehe viewDesign()). */
.theme-preview-pane {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
}
.theme-preview-pane[data-preview-mode="dark"] {
  --bg: #0f0f14;
  --bg-elevated: #1a1a24;
  --bg-card: #201f2b;
  --border: #322f42;
  --text: #f2f1f7;
  --text-dim: #a8a5b8;
  background: var(--bg);
  color: var(--text);
}
.theme-preview-pane[data-preview-mode="light"] {
  --bg: #f4f3f9;
  --bg-elevated: #ffffff;
  --bg-card: #ffffff;
  --border: #ddd8ea;
  --text: #1c1a24;
  --text-dim: #6b6780;
  background: var(--bg);
  color: var(--text);
}

/* ---------------- Rollen & Rechteverwaltung (Team & API-Tab) ---------------- */
/* Checkbox-Raster fuer die "pro Einzelaktion" vergebbaren Rechte von
   supporter/gast (siehe permissionGridHtml() in app.js) - eine Gruppe pro
   Seite (Musikfeed/Notizen/DJ-Anzeige). */
.perm-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 12px;
  margin: 10px 0 14px;
}
.perm-group {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px 12px 4px;
  margin: 0;
}
.perm-group legend {
  font-size: 12px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--text-dim);
  padding: 0 6px;
}
.perm-group .checkbox-row { font-size: 13px; margin-bottom: 8px; }

/* Inline-Panel zum Bearbeiten von Rolle/Rechten eines bestehenden Nutzers
   (siehe userRowHtml()/viewTeam() in app.js) - optisch leicht abgesetzt
   direkt unter der jeweiligen Nutzer-Zeile. */
.user-edit-panel {
  border: 1px dashed var(--border);
  border-radius: 10px;
  padding: 12px 14px;
  margin: -4px 0 14px;
  background: var(--bg-elevated);
}
.user-row .badge + .badge { margin-left: 4px; }

/* ---------------- Persoenliches Ansichts-Theme (oeffentliche Liste, kein Login) ---------------- */
/* "Farblos"-Modus (siehe applyEffectiveTheme() in app.js) - ein simpler
   Graustufen-Filter auf dem gesamten Dokument reicht aus, ohne dass jede
   einzelne Kategorie-/Notizfarbe separat entsaettigt werden muss. Rein
   lokal je Browser (localStorage) - betrifft nie das globale Theme. */
html.public-mono-theme {
  filter: grayscale(1);
}
.public-theme-picker .btn.active { background: var(--accent); color: var(--accent-text); }
.public-theme-picker p.hint { margin-top: 4px; }

/* ===== DJ-Anzeige: Hybrid Layout ===== */
.djview-hybrid-mode {
  display: grid;
  grid-template-columns: 1fr 320px;
  gap: 12px;
  height: calc(100vh - 100px);
  overflow: hidden;
}

.djview-songs-column {
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-width: thin;
  scrollbar-color: #ec4899 rgba(236, 72, 153, 0.1);
}

.djview-songs-column::-webkit-scrollbar {
  width: 4px;
}

.djview-songs-column::-webkit-scrollbar-track {
  background: rgba(236, 72, 153, 0.1);
}

.djview-songs-column::-webkit-scrollbar-thumb {
  background: rgba(236, 72, 153, 0.4);
  border-radius: 2px;
}

.djview-songs-column::-webkit-scrollbar-thumb:hover {
  background: #ec4899;
}

.djview-hybrid-note-indicator {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #ec4899;
  margin-left: 8px;
  vertical-align: middle;
  box-shadow: 0 0 6px rgba(236, 72, 153, 0.8);
  animation: pulse-indicator 2s infinite;
}

@keyframes pulse-indicator {
  0%, 100% { box-shadow: 0 0 6px rgba(236, 72, 153, 0.8); }
  50% { box-shadow: 0 0 12px rgba(236, 72, 153, 1); }
}

.djview-notes-sidebar {
  overflow-y: auto;
  overflow-x: hidden;
  border-left: 1px solid rgba(236, 72, 153, 0.2);
  padding-left: 12px;
  scrollbar-width: thin;
  scrollbar-color: #ec4899 rgba(236, 72, 153, 0.1);
}

.djview-notes-sidebar::-webkit-scrollbar {
  width: 4px;
}

.djview-notes-sidebar::-webkit-scrollbar-track {
  background: transparent;
}

.djview-notes-sidebar::-webkit-scrollbar-thumb {
  background: rgba(236, 72, 153, 0.4);
  border-radius: 2px;
}

.djview-notes-sidebar::-webkit-scrollbar-thumb:hover {
  background: #ec4899;
}

.djview-hybrid-note-item {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-left: 4px solid #64b5f6;
  border-radius: 6px;
  padding: 8px;
  margin-bottom: 8px;
  font-size: 0.875rem;
  position: relative;
}

.djview-hybrid-note-item.note-sensibel {
  border-left-color: #ef4444;
}

.djview-hybrid-note-item.note-standard {
  border-left-color: #38bdf8;
}

.djview-hybrid-note-item.note-hinweis {
  border-left-color: #ec4899;
}

.djview-hybrid-note-item.note-gruen {
  border-left-color: #10b981;
}

.djview-hybrid-note-item.has-alarm {
  border-color: #ef4444;
  border-left-color: #ef4444;
  box-shadow: 0 0 8px rgba(239, 68, 68, 0.3);
}

.djview-hybrid-note-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 6px;
  margin-bottom: 6px;
  font-size: 0.8rem;
}

.djview-hybrid-note-from {
  font-size: 0.75rem;
  opacity: 0.6;
}

.djview-hybrid-note-text {
  margin: 6px 0;
  line-height: 1.4;
  word-wrap: break-word;
  font-size: 0.85rem;
}

.djview-hybrid-note-link {
  font-size: 0.75rem;
  opacity: 0.75;
  margin: 4px 0;
  padding: 4px;
  background: rgba(236, 72, 153, 0.08);
  border-radius: 3px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.djview-hybrid-alarm-badge {
  margin: 6px 0;
  font-size: 0.8rem;
}

.djview-hybrid-note-actions {
  display: flex;
  gap: 4px;
  margin-top: 6px;
}

.djview-hybrid-note-actions .icon-btn,
.djview-hybrid-note-actions .btn {
  padding: 2px 4px;
  font-size: 0.75rem;
}

.djview-hybrid-empty {
  text-align: center;
  padding: 20px 8px;
  opacity: 0.5;
  font-size: 0.85rem;
}

.djview-layout-toggle {
  display: flex;
  gap: 4px;
  align-items: center;
}

.djview-layout-toggle .btn {
  padding: 6px 10px;
  font-size: 0.8rem;
  white-space: nowrap;
  background: rgba(255, 255, 255, 0.1);
  color: inherit;
  border: 1px solid rgba(255, 255, 255, 0.2);
  cursor: pointer;
  border-radius: 4px;
  transition: all 0.2s ease;
}

.djview-layout-toggle .btn:hover {
  background: rgba(255, 255, 255, 0.15);
}

.djview-layout-toggle .btn.active {
  background: #ec4899;
  color: white;
  border-color: #ec4899;
}

/* Responsive: Bei schmalen Bildschirmen */
@media (max-width: 1024px) {
  .djview-hybrid-mode {
    grid-template-columns: 1fr;
  }

  .djview-notes-sidebar {
    display: none;
  }
}
/* ==========================================================================
   Twitch-Kanalpunkte (v1.1.0)
   Prüfen-Tab, Prio-Zeichen und Twitch-Abzeichen.
   ========================================================================== */

/* --- Prio-Zeichen: reine Anzeige, sortiert nichts um --- */
.prio-mark{
  font-family: ui-monospace, Consolas, monospace; font-weight: 900;
  letter-spacing: -2px; padding: 1px 7px; border-radius: 6px; font-size: 15px;
  line-height: 1; display: inline-flex; align-items: center; cursor: help;
  margin-right: 7px; vertical-align: middle;
}
.prio-1{ color:#8b5cf6; background:rgba(139,92,246,.16); border:1px solid rgba(139,92,246,.45); }
.prio-2{ color:#fb923c; background:rgba(251,146,60,.16); border:1px solid rgba(251,146,60,.5); }
.prio-3{ color:#fff; background:#f43f5e; border:1px solid #fb7185;
  animation: prioBlink 1s ease-in-out infinite; }
@keyframes prioBlink{ 50%{ opacity:.35 } }

/* --- Twitch-Abzeichen in der Liste (v1.2.2) ---
   Feste 19x19 sind raus: das Kaestchen war fuer den Text "tv" gebaut und
   schnitt alles Groessere ab. Jetzt bestimmt der Inhalt die Breite, der
   Hintergrund waechst mit - egal ob dort ein Bild, ein Zeichen oder Text
   steht. */
.twitch-badge{
  display:inline-flex; align-items:center; justify-content:center;
  padding:2px; white-space:nowrap; line-height:0;
  border-radius:6px; background:rgba(145,70,255,.18); border:1px solid rgba(145,70,255,.5);
  color:#c9a5ff; font-size:10px; font-weight:700; cursor:help;
  margin-left:8px; vertical-align:middle;
}

/* Starres Twitch-Zeichen. display:inline-block verhindert, dass die
   Zeilenhoehe der Liste durch das Bild auseinandergeht. */
.twitch-icon{
  display:inline-block; vertical-align:middle;
  object-fit:contain; flex:0 0 auto;
}

/* Animiertes Zeichen in der Kopfzeile. */
.twitch-icon-anim{ display:inline-flex; line-height:0; }
.twitch-icon-anim img{ display:block; object-fit:contain; }

/* Gruene RX-Kugel - "Abruf laeuft". */
.rx-kugel{ display:inline-block; vertical-align:middle; flex:0 0 auto; }

/* Kopfzeilen-Gruppe: animiertes Zeichen + Kugel gehoeren optisch zusammen. */
.twitch-live{
  display:inline-flex; align-items:center; gap:5px;
  padding:3px 7px 3px 5px; margin-right:4px;
  border-radius:999px;
  background:rgba(145,70,255,.14); border:1px solid rgba(145,70,255,.38);
}

/* In der Statuszeile des Pruefen-Tabs sitzt die Kugel direkt vor dem Text. */
.tw-status.ok .rx-kugel{ margin-right:5px; vertical-align:-3px; }

/* Wer Bewegung im Betriebssystem abgeschaltet hat, soll sie hier auch nicht
   bekommen - das animierte Zeichen faellt dann auf das Standbild zurueck. */
@media (prefers-reduced-motion: reduce){
  .twitch-icon-anim img{ content:url('/icons/twitch/twitch.png'); }
}

/* --- Hinweisleiste --- */
.tw-notice{
  margin:18px 0 14px; padding:13px 16px; border-radius:11px;
  background:linear-gradient(90deg, rgba(145,70,255,.13), transparent);
  border:1px solid rgba(145,70,255,.3); font-size:13.5px;
  display:flex; gap:11px; align-items:flex-start; line-height:1.6;
}
.tw-lock{ font-size:17px; flex:none }

.tw-status{ margin-bottom:16px; padding:9px 14px; border-radius:9px; font-size:13px; }
.tw-status.ok{ background:rgba(52,211,153,.1); border:1px solid rgba(52,211,153,.3); }
.tw-status.aus{ background:rgba(251,191,36,.1); border:1px solid rgba(251,191,36,.3); }
.tw-status.fehler{ background:rgba(248,113,113,.1); border:1px solid rgba(248,113,113,.35); }

.tw-h{ font-size:15px; margin:22px 0 12px; font-weight:600; letter-spacing:.3px; }
.tw-h-sub{ font-weight:400; opacity:.65 }

/* --- Karte --- */
.tw-card{
  background:var(--bg-card, #201f2b); border:1px solid var(--border, #322f42);
  border-radius:14px; margin-bottom:16px; overflow:hidden;
}
.tw-card.gesperrt{ border-color:rgba(248,113,113,.45) }
.tw-top{
  padding:13px 16px; display:flex; align-items:center; gap:12px;
  border-bottom:1px solid var(--border, #322f42); flex-wrap:wrap;
}
.tw-av{
  width:34px; height:34px; border-radius:50%; flex:none; color:#fff;
  background:linear-gradient(135deg,#9146ff,#c084fc);
  display:grid; place-items:center; font-weight:700; font-size:14px;
}
.tw-user{ font-weight:600; font-size:14.5px; display:flex; align-items:center; gap:2px }
.tw-meta{ font-size:12.5px; opacity:.65 }
.tw-rw{
  background:rgba(145,70,255,.15); color:#c9a5ff; border:1px solid rgba(145,70,255,.3);
  padding:1px 8px; border-radius:6px; font-size:11.5px; font-weight:600;
}
.tw-cost{
  margin-left:auto; background:rgba(145,70,255,.18); color:#c9a5ff;
  border:1px solid rgba(145,70,255,.35); padding:4px 11px; border-radius:8px;
  font-size:12.5px; font-weight:600; white-space:nowrap;
}

.tw-raw{ padding:14px 16px 4px }
.tw-raw-lbl{ font-size:11.5px; opacity:.55; text-transform:uppercase; letter-spacing:.6px; margin-bottom:6px }
.tw-raw-txt{
  font-size:16px; font-family: ui-monospace, Consolas, monospace;
  background:var(--bg, #0f0f14); border:1px dashed var(--border, #322f42);
  border-radius:9px; padding:10px 13px;
}

.tw-warn{
  margin:12px 16px 0; padding:11px 14px; border-radius:10px;
  background:rgba(248,113,113,.12); border:1px solid rgba(248,113,113,.4);
  font-size:13.5px; display:flex; gap:10px; align-items:flex-start; line-height:1.6;
}

.tw-score-row{ padding:12px 16px 4px; display:flex; align-items:center; gap:10px; flex-wrap:wrap }
.tw-pill{ padding:3px 11px; border-radius:99px; font-size:12px; font-weight:700 }
.tw-pill.ok{ background:rgba(52,211,153,.12); color:#34d399; border:1px solid rgba(52,211,153,.35) }
.tw-pill.warn{ background:rgba(251,191,36,.12); color:#fbbf24; border:1px solid rgba(251,191,36,.35) }
.tw-pill.bad{ background:rgba(248,113,113,.12); color:#f87171; border:1px solid rgba(248,113,113,.35) }
.tw-hint{ font-size:12.5px; opacity:.6 }

/* --- Kandidaten --- */
.tw-cands{ padding:10px 16px 4px; display:flex; flex-direction:column; gap:8px }
.tw-cand{
  display:flex; align-items:center; gap:12px; padding:9px 11px;
  border:1.5px solid var(--border, #322f42); border-radius:11px; cursor:pointer;
  background:var(--bg-elevated, #1a1a24); transition:.15s;
}
.tw-cand:hover{ border-color:#4a4560 }
.tw-cand.sel{
  border-color:var(--accent, #8b5cf6); background:rgba(139,92,246,.15);
  box-shadow:0 0 0 3px rgba(139,92,246,.12);
}
.tw-cover{ width:44px; height:44px; border-radius:7px; flex:none; object-fit:cover }
.tw-cover.leer{ background:linear-gradient(135deg,#3b3550,#524a70); display:grid; place-items:center; font-size:18px }
.tw-cand-main{ min-width:0; flex:1 }
.tw-cand-t{ font-weight:600; font-size:14.5px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
.tw-cand-a{ font-size:13px; opacity:.7; overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
.tw-prov{ font-size:10.5px; font-weight:700; padding:2.5px 8px; border-radius:6px; flex:none;
  background:rgba(255,255,255,.06); border:1px solid var(--border, #322f42) }
.tw-score{ font-size:12.5px; font-weight:700; flex:none; min-width:44px; text-align:right }
.tw-score.ok{ color:#34d399 } .tw-score.warn{ color:#fbbf24 } .tw-score.bad{ color:#f87171 }
.tw-check{
  width:20px; height:20px; border-radius:50%; border:2px solid var(--border, #322f42);
  flex:none; display:grid; place-items:center; font-size:12px; color:transparent;
}
.tw-cand.sel .tw-check{ border-color:var(--accent,#8b5cf6); background:var(--accent,#8b5cf6); color:#fff }
.tw-leer{ margin:10px 16px 0; padding:12px 14px; border-radius:10px;
  background:rgba(251,191,36,.1); border:1px solid rgba(251,191,36,.3); font-size:13px }

/* --- Live-Suche --- */
.tw-own{ padding:8px 16px 0 }
.tw-own-btn{
  width:100%; padding:10px; border-radius:10px; cursor:pointer; font-size:13.5px;
  border:1.5px dashed var(--border, #322f42); background:transparent;
  color:inherit; opacity:.75;
}
.tw-own-btn:hover{ border-color:var(--accent,#8b5cf6); opacity:1 }
.tw-own-box{ margin-top:10px; padding:13px; border:1.5px solid var(--accent,#8b5cf6);
  border-radius:11px; background:rgba(139,92,246,.06) }
.tw-own-box input{
  width:100%; padding:10px 12px; border-radius:9px; font-size:14.5px;
  border:1px solid var(--border,#322f42); background:var(--bg,#0f0f14); color:inherit;
}
.tw-own-box .tw-cands{ padding:11px 0 0 }
.tw-keep{
  margin-top:10px; padding:10px 12px; border-radius:9px; font-size:12.5px; line-height:1.7;
  background:rgba(52,211,153,.12); border:1px solid rgba(52,211,153,.3);
}

/* --- Felder aendern --- */
.tw-edit{ margin:10px 16px 0; padding:14px; border:1.5px solid var(--accent,#8b5cf6);
  border-radius:11px; background:rgba(139,92,246,.06) }
.tw-edit-t{ font-size:13px; font-weight:600; margin-bottom:11px }
.tw-flds{ display:flex; gap:10px; flex-wrap:wrap }
.tw-fld{ flex:1; min-width:190px }
.tw-fld label{ display:block; font-size:11.5px; opacity:.55; text-transform:uppercase;
  letter-spacing:.5px; margin-bottom:5px }
.tw-fld input{ width:100%; padding:10px 12px; border-radius:9px; font-size:14.5px;
  border:1px solid var(--border,#322f42); background:var(--bg,#0f0f14); color:inherit }
.tw-edit-acts{ display:flex; gap:8px; margin-top:12px; flex-wrap:wrap }
.tw-edit-note{ margin-top:11px; padding:10px 12px; border-radius:9px; font-size:12.5px;
  line-height:1.7; background:rgba(251,191,36,.12); border:1px solid rgba(251,191,36,.3) }

/* --- Was uebernommen wird --- */
.tw-carry{ margin:13px 16px 0; padding:10px 13px; border-radius:9px; font-size:12.5px;
  line-height:1.7; background:rgba(52,211,153,.12); border:1px solid rgba(52,211,153,.28) }
.tw-carry.duenn{ background:rgba(251,191,36,.12); border-color:rgba(251,191,36,.3) }

/* --- Aktionen --- */
.tw-acts{ padding:13px 16px 15px; display:flex; gap:8px; flex-wrap:wrap;
  border-top:1px solid var(--border,#322f42); margin-top:13px }

/* --- Ablehnen --- */
.tw-rej{ margin:0 16px 15px; padding:13px; border-radius:11px;
  border:1.5px solid rgba(248,113,113,.45); background:rgba(248,113,113,.06) }
.tw-rej-t{ font-size:13px; font-weight:600; margin-bottom:9px }
.tw-chips{ display:flex; gap:7px; flex-wrap:wrap; margin-bottom:10px }
.tw-chip{ padding:6px 12px; border-radius:99px; font-size:12.5px; cursor:pointer;
  border:1px solid var(--border,#322f42); background:var(--bg-elevated,#1a1a24) }
.tw-chip:hover{ border-color:#4a4560 }
.tw-chip.on{ background:rgba(248,113,113,.12); border-color:rgba(248,113,113,.55) }
.tw-rej input{ width:100%; padding:10px 12px; border-radius:9px; font-size:13.5px;
  border:1px solid var(--border,#322f42); background:var(--bg,#0f0f14); color:inherit }
.tw-rej-note{ margin-top:9px; font-size:12px; opacity:.6 }

/* --- Leer --- */
.tw-empty{ text-align:center; padding:55px 20px; opacity:.6 }
.tw-empty-icon{ font-size:40px; margin-bottom:12px }
.tw-empty-sub{ font-size:13px; margin-top:6px; opacity:.8 }
.tw-setup-link{ margin-bottom:16px; font-size:13.5px }
/* v1.29.1: Aus dem Link wurde ein Knopf (er springt jetzt in den Bereich,
   statt eine eigene Seite zu oeffnen) - er soll aber weiter wie ein Link
   aussehen, nicht wie ein Bedienknopf. */
.tw-setup-link a,
.tw-setup-link button{
  color:var(--accent,#8b5cf6); text-decoration:none;
  background:none; border:none; padding:0; font:inherit; cursor:pointer;
}
.tw-setup-link a:hover,
.tw-setup-link button:hover{ text-decoration:underline }

/* ==========================================================================
   v1.29.1 - BEREICH "TWITCH EINRICHTEN"

   Die sechs Schritte kommen von der alten Seite /twitch-setup.html. Dort
   hatten sie ein eigenes Aussehen; hier benutzen sie die Karten und Felder
   der App. Eigen bleibt nur, was die Schritt-fuer-Schritt-Fuehrung braucht:
   die nummerierte Ueberschrift, der grosse Anmeldecode und die
   Belohnungstabelle.
   ========================================================================== */
.tw-schritt h2{ display:flex; align-items:center; gap:10px; }
.tw-nr{
  flex:0 0 auto; width:24px; height:24px; border-radius:50%;
  display:inline-grid; place-items:center;
  background:var(--accent,#8b5cf6); color:#fff;
  font-size:13px; font-weight:700; font-variant-numeric:tabular-nums;
}
.tw-knopfreihe{ display:flex; flex-wrap:wrap; gap:8px; align-items:center; margin-top:10px; }

/* Der Geraetecode. Er wird von Hand auf einer Twitch-Seite abgetippt -
   deshalb gross, weit gesperrt und in gleichbreiten Ziffern: 0 und O, 1 und
   l duerfen sich nicht aehnlich sehen. */
.tw-code{
  margin:10px 0; padding:14px 12px; border-radius:10px;
  background:var(--bg-elevated); border:1px dashed var(--border);
  font-family:ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size:26px; font-weight:700; letter-spacing:.18em; text-align:center;
  font-variant-numeric:tabular-nums; user-select:all;
}

/* Rechte-Chips: was das Token darf (gruen) und was ihm fehlt (rot). */
.tw-rechte{ margin-top:14px; }
.tw-rechte h4{ margin:0 0 8px; font-size:14px; }
.tw-scopes{ display:flex; flex-wrap:wrap; gap:6px; }
.tw-scope{
  display:inline-block; padding:2px 8px; border-radius:999px;
  font-size:11.5px; font-family:ui-monospace, Menlo, Consolas, monospace;
  border:1px solid var(--border); background:var(--bg-elevated);
}
.tw-scope.ok{ color:var(--success,#34d399); border-color:rgba(52,211,153,.42); background:rgba(52,211,153,.10) }
.tw-scope.no{ color:#f87171; border-color:rgba(248,113,113,.42); background:rgba(248,113,113,.10) }

/* Belohnungstabelle - Kopf und Spalten muessen exakt uebereinanderstehen
   (dieselbe Regel wie bei den Belohnungs-Sets in v1.28.26). */
.tw-rw-tabelle{ margin-top:14px; overflow-x:auto; }
.tw-rw-kopf,
.tw-rw-zeile{
  display:grid; grid-template-columns:minmax(160px,2fr) 90px 70px minmax(150px,1fr);
  gap:10px; align-items:center; padding:7px 4px;
}
.tw-rw-kopf{
  font-size:11.5px; text-transform:uppercase; letter-spacing:.04em;
  color:var(--text-dim); border-bottom:1px solid var(--border);
}
.tw-rw-zeile{ border-bottom:1px solid var(--border-weak,rgba(255,255,255,.06)); font-size:13.5px; }
.tw-rw-zeile .num, .tw-rw-kopf .num{ text-align:right; font-variant-numeric:tabular-nums; }
.tw-rw-neu{ align-items:end; }

.tw-anbieter{ display:flex; flex-wrap:wrap; gap:14px; }

/* Aenderungsverlauf der Einrichtung - wer hat wann was umgestellt. */
.tw-verlauf{ display:flex; flex-direction:column; gap:4px; font-size:12.5px; }
.tw-verlauf-zeile{ display:flex; flex-wrap:wrap; gap:8px; align-items:baseline; }
.tw-verlauf-zeit{ color:var(--text-dim); font-variant-numeric:tabular-nums; flex:0 0 auto; }
.tw-verlauf-wer{ color:var(--text-dim); }

/* Schmale Ansichten: die Tabelle darf scrollen, der Rest stapelt sich.
   Saschas Dauervorgabe - "IMMER so halten, dass es einmal fuer volle breite
   breit normal schmal und handy/mobilansicht usw geht". */
@media (max-width: 700px){
  .tw-rw-kopf, .tw-rw-zeile{ grid-template-columns:minmax(130px,2fr) 70px 56px minmax(120px,1fr); }
  .tw-code{ font-size:21px; letter-spacing:.12em; }
}


/* --- Testmodus (v1.1.1) --- */
.tw-test{ margin-bottom:18px; background:var(--bg-card,#201f2b);
  border:1px dashed rgba(251,191,36,.45); border-radius:13px; overflow:hidden }
.tw-test summary{ padding:12px 16px; cursor:pointer; font-size:13.5px; font-weight:600;
  color:#ffe0a3; list-style:none }
.tw-test summary::-webkit-details-marker{ display:none }
.tw-test summary:hover{ background:rgba(251,191,36,.06) }
.tw-test[open] summary{ border-bottom:1px solid var(--border,#322f42) }
.tw-test-in{ padding:15px 16px 17px }
.tw-test-p{ margin:0 0 13px; font-size:13px; opacity:.75; line-height:1.7 }
.tw-test-l{ display:block; font-size:11.5px; opacity:.55; text-transform:uppercase;
  letter-spacing:.5px; margin:0 0 5px }
.tw-test-in input, .tw-test-in select{ width:100%; padding:9px 12px; border-radius:9px;
  border:1px solid var(--border,#322f42); background:var(--bg,#0f0f14); color:inherit;
  font-size:14px }
.tw-test-bsp{ display:flex; gap:7px; flex-wrap:wrap; margin:10px 0 14px }
.tw-test-row{ display:flex; gap:10px; flex-wrap:wrap }
.tw-test-row>div{ flex:1; min-width:130px }
.tw-test-acts{ margin-top:14px }
.tw-testtag{ background:rgba(251,191,36,.18); color:#fbbf24;
  border:1px solid rgba(251,191,36,.45); padding:1px 7px; border-radius:5px;
  font-size:10.5px; font-weight:700; letter-spacing:.5px }


/* --- Prüfliste: Feinschliff v1.1.3 --- */

/* Vorschlagsliste scrollbar, damit die Karte nicht ausufert.
   Fünf Zeilen sind sichtbar, der Rest wird gescrollt. */
.tw-cands.scrollbar{
  max-height: 296px; overflow-y: auto;
  /* v1.27.3: "contain" -> "auto". Saschas Befund: "wenn man EIGENTLICH die
     ganze seite scrollen wollte zu einem anderen weiteren pruefsong gehts
     aber nicht weiter". "contain" (seit v1.1.3) hiess woertlich: Ist die
     Liste am Anschlag, gib das Radscrollen NICHT an die Seite weiter.
     "auto" macht genau das Gewuenschte: erst durch die Trefferliste, und
     wenn die zu Ende ist, scrollt die ganze Seite weiter. */
  overscroll-behavior: auto;
  padding-right: 6px;
}
.tw-cands.scrollbar::-webkit-scrollbar{ width: 9px }
.tw-cands.scrollbar::-webkit-scrollbar-track{ background: transparent }
.tw-cands.scrollbar::-webkit-scrollbar-thumb{
  background: var(--border, #322f42); border-radius: 99px;
}
.tw-cands.scrollbar::-webkit-scrollbar-thumb:hover{ background: #4a4560 }

/* Anbieter-Kennzeichnung: Logo + Text nebeneinander */
.tw-prov{
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 9px 3px 6px;
}
.tw-prov-icon{
  width: 16px; height: 16px; border-radius: 4px; object-fit: contain;
  display: block; flex: none;
}

/* Cover: echtes Bild, kein Platzhalterrahmen mehr */
.tw-cover{ background: var(--bg-elevated, #1a1a24); }

/* Gesperrter Vorschlag - die Warnung steht jetzt AM Vorschlag,
   nicht mehr pauschal über der ganzen Karte. */
.tw-cand.gesperrt{
  border-color: rgba(248,113,113,.5);
  background: linear-gradient(90deg, rgba(248,113,113,.10), var(--bg-elevated,#1a1a24));
}
.tw-cand.gesperrt.sel{
  border-color: #f87171;
  box-shadow: 0 0 0 3px rgba(248,113,113,.15);
}
.tw-cand-warn{
  font-size: 11.5px; color: #ffc2c2; margin-top: 4px; line-height: 1.4;
}

/* Hinweis (keine Sperre) - deutlich ruhiger als die echte Sperrmeldung */
.tw-warn.hinweis{
  background: rgba(251,191,36,.10);
  border-color: rgba(251,191,36,.35);
  color: #ffe0a3;
}

/* ---------------------------------------------------------------------------
   v1.3.14: Warnhinweis in Einstellungsbereichen.

   Fuer Lagen, die kein Fehler sind, aber Aufmerksamkeit brauchen: die
   Rollenliste konnte nicht geladen werden, oder es gibt nur einen einzigen
   Zugang mit voller Kontrolle. Bewusst gelb und nicht rot - kaputt ist
   nichts davon, wissen sollte man beides.
   --------------------------------------------------------------------------- */
.hinweis-warnung{
  padding: 10px 13px;
  border-radius: 9px;
  font-size: 13px;
  line-height: 1.5;
  background: rgba(251,191,36,.10);
  border: 1px solid rgba(251,191,36,.35);
  color: #ffe0a3;
}
.hinweis-warnung strong{ font-weight: 650; }

/* --- Anbieter-Gruppen in der Vorschlagsliste (v1.1.4) --- */
.tw-gruppe{
  display:flex; align-items:center; gap:9px; padding:9px 2px 3px;
  position:sticky; top:0; z-index:2;
  background:linear-gradient(180deg, var(--bg-card,#201f2b) 70%, transparent);
}
.tw-gruppe .tw-prov{ font-size:11px }
.tw-gruppe-n{ font-size:11.5px; opacity:.5 }
.tw-gruppe + .tw-cand{ margin-top:0 }

/* --- Sperrlisten-Fehlalarm freigeben (v1.1.6) --- */
.tw-cand-warn{
  display:flex; align-items:center; gap:9px; flex-wrap:wrap;
  font-size:11.5px; color:#ffc2c2; margin-top:5px; line-height:1.45;
}
.tw-cand-warn b{ color:#fff }
.tw-frei{
  padding:3px 10px; border-radius:99px; font-size:11px; font-weight:600;
  border:1px solid rgba(248,113,113,.5); background:transparent; color:#ffc2c2;
  cursor:pointer; white-space:nowrap; flex:none;
}
.tw-frei:hover{ background:rgba(248,113,113,.18); color:#fff }
.tw-cand.freigegeben{
  border-color:rgba(52,211,153,.4);
  background:linear-gradient(90deg, rgba(52,211,153,.08), var(--bg-elevated,#1a1a24));
}
.tw-cand-frei{ font-size:11.5px; color:#9ff0cd; margin-top:5px; line-height:1.45 }

/* --- Ausnahme dauerhaft merken (v1.2.0) --- */
.tw-frei.dauer{
  border-color: rgba(52,211,153,.5); color: #9ff0cd;
}
.tw-frei.dauer:hover{ background: rgba(52,211,153,.16); color: #fff }


/* ==========================================================================
   Liste: persoenliche Kompaktansicht (v1.2.1)
   Rein lokal pro Browser - aendert nie etwas fuer andere Besucher.
   ========================================================================== */
.liste-view-switch{
  display:flex; gap:5px; justify-content:flex-end;
  margin: 0 0 8px; padding: 0 2px;
}
.liste-view-switch .btn.mini{ padding:4px 11px; font-size:12px }

.wish-list.kompakt{ display:flex; flex-direction:column; gap:4px }

.liste-row{
  display:flex; align-items:center; gap:10px;
  padding:8px 12px;
  background:var(--bg-card); border:1px solid var(--border);
  border-radius:9px; font-size:14px;
  border-left:3px solid var(--cat-color, var(--border));
}
.liste-row-status{
  width:8px; height:8px; border-radius:50%; flex:none;
  background:var(--text-faint, #6e6a85);
}
.liste-row-status.status-offen{ background:#34d399 }
.liste-row-status.status-gespielt{ background:#6e6a85 }
.liste-row-status.status-abgelehnt{ background:#f87171 }

.liste-row-main{ flex:1; min-width:0; display:flex; align-items:baseline; gap:8px; flex-wrap:wrap }
.liste-row-t{ font-weight:600; overflow:hidden; text-overflow:ellipsis }
.liste-row-a{ opacity:.7; font-size:13px; overflow:hidden; text-overflow:ellipsis }
.liste-row-cat{
  font-size:10.5px; font-weight:700; letter-spacing:.4px; flex:none;
  padding:2px 7px; border-radius:5px;
  background:color-mix(in srgb, var(--cat-color, #8b5cf6) 22%, transparent);
  color:var(--cat-color, #8b5cf6);
}
.liste-row-von{ font-size:12px; opacity:.55; flex:none; max-width:120px;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
.liste-row-flags{ font-size:12px; flex:none; opacity:.75 }

/* Warnlisten-Treffer bleiben auch kompakt deutlich sichtbar */
.liste-row.restricted-exact{ border-color:rgba(248,113,113,.55);
  background:linear-gradient(90deg, rgba(248,113,113,.10), var(--bg-card)) }
.liste-row.restricted-fuzzy{ border-color:rgba(251,191,36,.5);
  background:linear-gradient(90deg, rgba(251,191,36,.08), var(--bg-card)) }

@media (max-width: 520px){
  .liste-row{ padding:7px 10px; font-size:13.5px }
  .liste-row-von{ display:none }
}

/* ============================================================================
   Zugangsdaten der Suchanbieter (v1.2.4)
   ========================================================================== */
.zd-anbieter { margin-top: 10px; }
.zd-anbieter > summary { display: flex; align-items: center; gap: 10px; }

/* Zeigt auf einen Blick, ob dieser Anbieter wirklich benutzt wird. */
.zd-status {
  font-size: 11px; font-weight: 700; letter-spacing: .3px;
  padding: 2px 8px; border-radius: 999px; text-transform: uppercase;
}
.zd-status.an  { background: rgba(34,197,94,.18);  color: #86efac; border: 1px solid rgba(34,197,94,.45); }
.zd-status.aus { background: rgba(148,163,184,.15); color: #cbd5e1; border: 1px solid rgba(148,163,184,.35); }

/* Woher der gerade geltende Wert stammt - wichtig, weil die Startdatei
   Vorrang vor dem hier Gespeicherten hat. */
.zd-quelle {
  font-size: 10.5px; font-weight: 600; margin-left: 6px;
  padding: 1px 7px; border-radius: 999px; vertical-align: middle;
}
.zd-quelle.umgebung    { background: rgba(56,189,248,.16); color: #7dd3fc; border: 1px solid rgba(56,189,248,.4); }
.zd-quelle.gespeichert { background: rgba(139,92,246,.16); color: #c4b5fd; border: 1px solid rgba(139,92,246,.4); }
.zd-quelle.fehlt       { background: rgba(148,163,184,.12); color: #94a3b8; border: 1px solid rgba(148,163,184,.3); }

.zd-aktionen { display: flex; gap: 8px; margin-top: 14px; flex-wrap: wrap; }

/* ============================================================================
   "Gewünscht von"-Feld in der DJ-Anzeige (v1.2.4)
   Farben, Groesse und Rundung kommen als style-Attribut aus
   wunschVonChipHtml() - hier steht nur, was sich NICHT einstellen laesst.
   ========================================================================== */
.wunschvon-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 9px;
  font-weight: 600;
  line-height: 1.5;
  white-space: nowrap;
  max-width: 220px;
  vertical-align: middle;
}
.wunschvon-name {
  overflow: hidden;
  text-overflow: ellipsis;
}
.wunschvon-chip .twitch-icon { flex: 0 0 auto; }

/* Vorschau in den Einstellungen */
.wvc-vorschau-box {
  margin: 12px 0 16px;
  border: 1px solid var(--border, rgba(148,163,184,.3));
  border-radius: 10px;
  overflow: hidden;
}
.wvc-vorschau-label {
  font-size: 11px; font-weight: 700; letter-spacing: .5px; text-transform: uppercase;
  padding: 6px 12px;
  background: rgba(148,163,184,.12);
  color: var(--text-dim, #94a3b8);
}
.wvc-vorschau { padding: 10px 12px; background: var(--bg, #0f0f14); }
.wvc-vorschau-zeile {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 6px 0;
}
.wvc-vorschau-zeile + .wvc-vorschau-zeile { border-top: 1px solid rgba(148,163,184,.14); }
.wvc-vorschau-song { font-size: 14px; font-weight: 600; }
.wvc-vorschau-dim { color: var(--text-dim, #94a3b8); font-weight: 400; }

/* Zahl neben dem Regler */
.wvc-wert {
  font-weight: 700; font-size: 11.5px;
  color: var(--accent, #8b5cf6);
  margin-left: 6px;
}

/* ============================================================================
   Raid-Meldung (v1.2.5)
   Farben und Groesse kommen als style-Attribut aus raidStil() - hier steht
   nur, was sich nicht einstellen laesst.
   ========================================================================== */
.raid-box {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: calc(14px * var(--raid-skala, 1)) calc(16px * var(--raid-skala, 1));
  border: 1px solid transparent;
  border-radius: 14px;
  font-size: calc(14px * var(--raid-skala, 1));
  box-shadow: 0 8px 30px rgba(0,0,0,.45);
  position: relative;
}

/* Popup: schwebt ueber allem. max-width, damit es auf dem Handy nicht
   ueber den Rand laeuft. */
.raid-box.raid-popup {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  z-index: 9000;
  width: min(560px, calc(100vw - 24px));
}
.raid-box.raid-popup.raid-pos-oben  { top: 16px; }
.raid-box.raid-popup.raid-pos-unten { bottom: 16px; }

/* Ereignis: haengt in der Ansicht und schiebt den Inhalt weiter. */
.raid-box.raid-ereignis { margin: 0 0 14px; }
.raid-box.raid-ereignis.raid-pos-unten { margin: 14px 0 0; }

@keyframes raidEin    { from { opacity: 0; } to { opacity: 1; } }
@keyframes raidSchieb { from { opacity: 0; transform: translateY(-14px); } to { opacity: 1; transform: translateY(0); } }
@keyframes raidPuls   { 0%,100% { box-shadow: 0 8px 30px rgba(0,0,0,.45); } 50% { box-shadow: 0 8px 44px rgba(145,70,255,.75); } }

.raid-anim-einblenden { animation: raidEin .35s ease-out; }
.raid-anim-schieben   { animation: raidSchieb .35s ease-out; }
.raid-anim-pulsieren  { animation: raidPuls 1.6s ease-in-out infinite; }
/* Popup wird ueber transform zentriert - eine Animation mit eigenem
   transform wuerde das ueberschreiben und den Kasten nach links springen
   lassen. Deshalb dort nur die Deckkraft animieren. */
.raid-popup.raid-anim-schieben { animation: raidEin .35s ease-out; }
@media (prefers-reduced-motion: reduce) {
  .raid-box { animation: none !important; }
}

.raid-bild {
  width: calc(56px * var(--raid-skala, 1));
  height: calc(56px * var(--raid-skala, 1));
  border-radius: 50%;
  object-fit: cover;
  flex: 0 0 auto;
  background: rgba(255,255,255,.15);
}
.raid-bild.leer {
  display: flex; align-items: center; justify-content: center;
  font-size: calc(24px * var(--raid-skala, 1)); font-weight: 800;
}
.raid-mini-bild {
  width: 20px; height: 20px; border-radius: 50%;
  object-fit: cover; vertical-align: -5px; margin-right: 7px;
}

.raid-text { flex: 1 1 auto; min-width: 0; }
.raid-kopf {
  font-size: calc(12px * var(--raid-skala, 1));
  font-weight: 800; letter-spacing: 1.2px; text-transform: uppercase;
  opacity: .9; margin-bottom: 3px;
}
.raid-testmarke {
  font-size: 10px; padding: 1px 6px; border-radius: 999px;
  background: rgba(0,0,0,.28); letter-spacing: .5px;
}
.raid-haupt { font-size: calc(16px * var(--raid-skala, 1)); line-height: 1.45; }
.raid-zeilen { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 7px; }
.raid-kategorie, .raid-streamtitel, .raid-fehlt {
  font-size: calc(11.5px * var(--raid-skala, 1));
  padding: 2px 8px; border-radius: 999px;
  background: rgba(0,0,0,.22);
}
.raid-fehlt { opacity: .75; font-style: italic; }
.raid-fehlt-klein { opacity: .6; font-style: italic; font-size: 12px; }
.raid-gruss { margin-top: 8px; font-style: italic; opacity: .95; }
.raid-link {
  display: inline-block; margin-top: 8px;
  color: inherit; text-decoration: underline; opacity: .9;
  font-size: calc(13px * var(--raid-skala, 1));
}
.raid-knoepfe { display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap; }

/* --- v1.3.10: mehrere Raids in EINEM Popup --- */
.raid-box.raid-multi .raid-text { width: 100%; }
.raid-teil {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 4px; border-top: 1px solid rgba(255,255,255,.18);
}
.raid-teil:first-of-type { border-top: none; }
.raid-teil-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.raid-teil .raid-zu { position: static; margin-left: 4px; }
.raid-bild.klein { width: 34px; height: 34px; min-width: 34px; font-size: 15px; }
/* Bei genau ZWEI Raids ("gesplittet") sind die Zeilen grosszuegiger. */
.raid-box.raid-zwei .raid-teil { padding: 14px 4px; font-size: 15px; }
.raid-box.raid-zwei .raid-bild.klein { width: 44px; height: 44px; min-width: 44px; }

/* --- v1.3.10: minimalistische Raid-Leiste in der DJ-Anzeige --- */
.dj-raid-leiste { display: flex; flex-direction: column; gap: 6px; padding: 8px 14px 0; }
.dj-raid-zeile {
  display: flex; align-items: center; gap: 14px;
  padding: 8px 14px; border-radius: 9px;
  background: rgba(145,70,255,.16); border: 1px solid rgba(145,70,255,.45);
  font-size: 14.5px;
}
.dj-raid-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Bewusst groesser als ein Mini-Icon: gut treffbar waehrend des Auflegens. */
.dj-raid-ausblenden { padding: 9px 26px; font-size: 13.5px; }

/* --- v1.3.11: Uhr in der Kopfzeile --- */
.kopf-uhr {
  font-variant-numeric: tabular-nums;
  font-size: 13px; opacity: .85; white-space: nowrap;
  padding: 2px 8px; border-radius: 6px;
  background: rgba(148,163,184,.12);
}
.raid-zu {
  position: absolute; top: 8px; right: 10px;
  background: none; border: none; cursor: pointer;
  color: inherit; opacity: .6; font-size: 16px; line-height: 1;
}
.raid-zu:hover { opacity: 1; }

/* --- Einstellungen --- */
.raid-hinweis {
  padding: 11px 14px; border-radius: 10px; margin-bottom: 14px;
  font-size: 13.5px; line-height: 1.55;
}
.raid-hinweis.ok   { background: rgba(34,197,94,.13);  border: 1px solid rgba(34,197,94,.4); }
.raid-hinweis.warn { background: rgba(250,204,21,.12); border: 1px solid rgba(250,204,21,.4); }
.raid-hinweis.stop { background: rgba(239,68,68,.12);  border: 1px solid rgba(239,68,68,.45); }
.raid-hinweis-sub  { margin-top: 6px; opacity: .8; font-size: 12.5px; }

.raid-ansicht-zeile {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 7px 0;
}
.raid-ansicht-zeile + .raid-ansicht-zeile { border-top: 1px solid rgba(148,163,184,.15); }
.raid-ansicht-name { font-weight: 600; }
.raid-oeff {
  font-size: 10px; padding: 1px 7px; border-radius: 999px;
  background: rgba(250,204,21,.18); color: #fde68a;
  border: 1px solid rgba(250,204,21,.4); font-weight: 700;
}

.raid-ph {
  padding: 1px 6px; border-radius: 5px; cursor: help;
  background: rgba(139,92,246,.18); border: 1px solid rgba(139,92,246,.4);
}
.raid-vorschau {
  margin-top: 6px; font-size: 12.5px; opacity: .85;
  font-style: italic; min-height: 1.2em; word-break: break-word;
}
.hint-inline { font-weight: 400; opacity: .7; font-size: 12px; }

.raid-tabelle { width: 100%; border-collapse: collapse; margin: 12px 0; font-size: 13px; }
.raid-tabelle th, .raid-tabelle td {
  padding: 7px 9px; text-align: left;
  border-bottom: 1px solid rgba(148,163,184,.16);
}
.raid-tabelle th { font-size: 11px; text-transform: uppercase; letter-spacing: .4px; opacity: .7; }
.raid-tabelle .num { text-align: right; }

/* Zustand der Startdatei (v1.3.0) - der Fall "stillgelegt, aber die Datei
   liegt noch da" muss deutlich sichtbar sein, sonst gibt jemand die App
   weiter und die Schluessel stehen weiterhin im Klartext darin. */
.zd-warnung {
  padding: 10px 13px; border-radius: 9px; margin: 4px 0 14px;
  font-size: 13px; line-height: 1.55;
}
.zd-warnung.aktiv       { background: rgba(56,189,248,.12); border: 1px solid rgba(56,189,248,.38); }
.zd-warnung.stillgelegt { background: rgba(250,204,21,.12); border: 1px solid rgba(250,204,21,.42); }
.zd-warnung code { background: rgba(0,0,0,.22); padding: 1px 5px; border-radius: 4px; }
.zd-warnung .btn { margin-top: 9px; }

/* Chat-Bot (v1.3.1) */
.bot-anleitung { margin: 0 0 12px 18px; padding: 0; line-height: 1.7; font-size: 13.5px; }
.bot-anleitung li { margin-bottom: 8px; }
.bot-anleitung code, .bot-scopes code {
  background: rgba(139,92,246,.18); border: 1px solid rgba(139,92,246,.4);
  padding: 1px 6px; border-radius: 5px; font-size: 12px;
}
.bot-scopes { margin-top: 5px; display: flex; gap: 6px; flex-wrap: wrap; }

/* ============================================================================
   v1.3.2 – Ablehn-Archiv, Chat-Befehle, Belohnungen, Raid-Feed
   ========================================================================== */

/* --- Ablehnen: zweiter Knopf und Nachrichtenfeld --- */
.tw-rej-msg { margin: 12px 0; }
.tw-rej-msg textarea {
  width: 100%; resize: vertical; margin-top: 6px;
  font-family: inherit; font-size: 13.5px; line-height: 1.5;
}
.tw-rej-note.warn {
  background: rgba(250,204,21,.12); border: 1px solid rgba(250,204,21,.4);
  padding: 9px 12px; border-radius: 8px; line-height: 1.55;
}
.btn.ohne-erstattung { display: inline-flex; flex-direction: column; line-height: 1.25; }
.btn.ohne-erstattung .tw-sub { font-size: 10px; opacity: .8; font-weight: 400; }

/* --- Archiv unter der Prüfliste --- */
.tw-archiv { margin-top: 22px; border-top: 1px solid rgba(148,163,184,.2); padding-top: 12px; }
.tw-archiv > summary { cursor: pointer; font-weight: 700; padding: 6px 0; }
.tw-archiv-body { padding-top: 10px; }
/* v1.3.9: erste Spalte ist die Auswahl-Checkbox (Bulk); ohne Recht wird sie
   nicht gerendert, dann rueckt der Status per auto-Spalte einfach nach. */
.abg-kopf, .abg-zeile {
  display: grid;
  grid-template-columns: auto 160px 130px 1fr 62px 1fr 132px;
  gap: 8px; align-items: center;
  padding: 5px 8px; font-size: 12.5px;
}
.abg-kopf {
  font-size: 10.5px; text-transform: uppercase; letter-spacing: .4px;
  opacity: .6; font-weight: 700;
}
.abg-check { width: 15px; height: 15px; accent-color: #8b5cf6; cursor: pointer; }
.abg-zeile { border-radius: 5px; }
.abg-zeile:nth-child(even) { background: rgba(148,163,184,.06); }
.abg-zeile.offen { background: rgba(239,68,68,.1); }
.abg-was, .abg-grund, .abg-user { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.abg-punkte, .abg-zeit { opacity: .75; font-size: 11.5px; }
.abg-status {
  font-size: 10px; font-weight: 700; text-transform: uppercase;
  padding: 2px 7px; border-radius: 999px; text-align: center;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* v1.3.9: Zustandsfarben - rot=offen (braucht Entscheidung), gelb=laeuft,
   gruen/grau=final. */
.abg-status.offen   { background: rgba(239,68,68,.18);  color: #fca5a5; }
.abg-status.wartet  { background: rgba(250,204,21,.2);  color: #fde68a; }
.abg-status.fertig  { background: rgba(34,197,94,.18);  color: #86efac; }
.abg-status.grau    { background: rgba(148,163,184,.18); color: #cbd5e1; }
.abg-warnzahl { color: #fca5a5; font-weight: 700; }
.abg-bulk {
  display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
  padding: 8px; margin-bottom: 8px; border-radius: 8px;
  background: rgba(139,92,246,.1); border: 1px solid rgba(139,92,246,.3);
}
.abg-bulk-n { font-size: 12px; font-weight: 700; margin-right: 4px; }
@media (max-width: 820px) {
  .abg-kopf { display: none; }
  .abg-zeile { grid-template-columns: auto 130px 1fr 70px; grid-auto-rows: min-content; }
  .abg-grund, .abg-zeit { display: none; }
}

/* --- v1.3.9: Offene-Refunds-Kasten --- */
.refund-kasten {
  border: 1px solid rgba(239,68,68,.45); border-radius: 10px;
  background: rgba(239,68,68,.07);
  padding: 12px 14px; margin: 14px 0;
}
.refund-kopf { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-bottom: 4px; }
.refund-kopf > b { font-size: 14.5px; }
.refund-summe { font-size: 12px; opacity: .85; margin-right: auto; }
.refund-zeile {
  display: grid;
  grid-template-columns: 130px 1fr 62px 160px max-content max-content;
  gap: 8px; align-items: center;
  padding: 5px 6px; font-size: 12.5px; border-radius: 5px;
}
.refund-zeile:nth-child(even) { background: rgba(148,163,184,.06); }
.refund-user, .refund-song { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.refund-punkte { opacity: .8; font-size: 11.5px; }
.refund-warnzeile {
  display: flex; align-items: center; gap: 8px;
  border: 1px solid rgba(239,68,68,.45); border-radius: 8px;
  background: rgba(239,68,68,.08); color: inherit;
  padding: 8px 12px; margin-bottom: 12px; font-size: 13px;
  cursor: pointer; width: 100%; text-align: left;
}
.refund-warnzeile:hover { background: rgba(239,68,68,.14); }
@media (max-width: 820px) {
  .refund-zeile { grid-template-columns: 1fr 60px max-content; }
  .refund-song, .refund-zeile .abg-status { display: none; }
}

/* --- Chat-Befehle --- */
.bef-zeile {
  display: flex; gap: 14px; align-items: flex-start;
  padding: 10px 0; border-bottom: 1px solid rgba(148,163,184,.14);
}
.bef-links { flex: 1 1 auto; min-width: 0; }
.bef-eingabe { flex: 0 0 150px; font-family: ui-monospace, Consolas, monospace; }
.bef-zeile code {
  background: rgba(139,92,246,.16); border: 1px solid rgba(139,92,246,.35);
  padding: 1px 6px; border-radius: 4px; font-size: 11.5px;
}

/* --- Belohnungen --- */
.bel-tabelle td { vertical-align: middle; }
.bel-tabelle tr.bel-drin { background: rgba(139,92,246,.07); }
.bel-akt { white-space: nowrap; text-align: right; }
.bel-ja, .bel-nein, .bel-aus, .bel-warn, .bel-status {
  font-size: 10.5px; font-weight: 700; padding: 2px 8px; border-radius: 999px;
  display: inline-block; cursor: help;
}
.bel-ja     { background: rgba(34,197,94,.16);   color: #86efac; }
.bel-nein   { background: rgba(239,68,68,.15);   color: #fca5a5; }
.bel-aus    { background: rgba(148,163,184,.16); color: #cbd5e1; margin-left: 6px; }
.bel-warn   { background: rgba(250,204,21,.16);  color: #fde68a; margin-left: 6px; }
.bel-status.an  { background: rgba(34,197,94,.16);   color: #86efac; }
.bel-status.aus { background: rgba(148,163,184,.16); color: #cbd5e1; }
.bel-status.neu { background: rgba(148,163,184,.1);  color: #94a3b8; font-weight: 400; }

/* --- Raid-Ansicht --- */
.raid-kennzahlen { display: flex; gap: 14px; flex-wrap: wrap; margin: 14px 0 20px; }
.raid-kennzahl {
  flex: 1 1 130px; padding: 14px 16px; border-radius: 11px;
  background: rgba(145,70,255,.1); border: 1px solid rgba(145,70,255,.28);
  display: flex; flex-direction: column; gap: 3px;
}
.raid-kennzahl .zahl { font-size: 26px; font-weight: 800; line-height: 1; }
.raid-kennzahl .text { font-size: 11.5px; opacity: .75; }

.raid-feed { display: flex; flex-direction: column; }
.raid-feed-zeile {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 4px; border-bottom: 1px solid rgba(148,163,184,.14);
}
.raid-feed-initial {
  width: 34px; height: 34px; border-radius: 50%; flex: 0 0 auto;
  display: flex; align-items: center; justify-content: center;
  background: rgba(145,70,255,.22); font-weight: 800;
}
.raid-feed-zeile .raid-mini-bild { width: 34px; height: 34px; margin: 0; }
.raid-feed-haupt { flex: 1 1 auto; min-width: 0; }
.raid-feed-oben { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.raid-feed-zahl {
  font-size: 11.5px; padding: 1px 8px; border-radius: 999px;
  background: rgba(145,70,255,.18);
}
.raid-feed-unten { display: flex; gap: 8px; margin-top: 3px; flex-wrap: wrap; }
.raid-feed-titel { font-size: 12px; opacity: .7; }
.raid-feed-zeit { font-size: 11.5px; opacity: .6; white-space: nowrap; }
.tw-ersatztag {
  font-size: 9.5px; font-weight: 800; letter-spacing: .5px;
  padding: 1px 6px; border-radius: 999px; margin-left: 5px;
  background: rgba(56,189,248,.2); color: #7dd3fc;
  border: 1px solid rgba(56,189,248,.45); cursor: help;
}

/* ---------------------------------------------------------------------------
   v1.31.23 - Der Doppel-Hinweis in der Prüfliste.

   DREI Lautstärken, nicht eine. Saschas Vorgabe war ausdrücklich abgestuft:
   "wenn der Interpret da ist ... ein Hinweis und wenn der Song'titel' der
   gleiche ist ein Hinweis und wenn es exakt das gleiche ist dann der
   deutliche Hinweis."

     hart    = Duplikat. Rot, fett - das ist die Ansage.
     mittel  = andere Version. Orange.
     leise   = nur Titel oder nur Interpret. Gedämpft, ohne Rahmen -
               benennt, stört aber nicht. Zweimal derselbe Interpret an
               einem Abend ist schließlich der Normalfall.

   Grundregel des Hauses: hell ist, was benennt oder schaltet; gedämpft ist,
   was erklärt. Deshalb trägt die Stufe die Farbe und der Ort daneben nicht.
   --------------------------------------------------------------------------- */
.tw-dup {
  font-size: 9.5px; font-weight: 800; letter-spacing: .4px;
  padding: 1px 6px; border-radius: 999px; margin-left: 5px;
  cursor: help; white-space: nowrap;
}
.tw-dup-hart {
  background: rgba(239,68,68,.2); color: #fca5a5;
  border: 1px solid rgba(239,68,68,.5);
}
.tw-dup-mittel {
  background: rgba(245,158,11,.18); color: #fbbf24;
  border: 1px solid rgba(245,158,11,.45);
}
.tw-dup-leise {
  background: rgba(255,255,255,.07); color: var(--muted, #9aa0aa);
  border: 1px solid rgba(255,255,255,.14); font-weight: 600;
}
/* Wer war schneller? Steht nur bei einem Treffer in der Prüfliste selbst. */
.tw-dup-zeit {
  font-size: 9.5px; margin-left: 4px; opacity: .65;
  font-variant-numeric: tabular-nums;
}

/* An der einzelnen Vorschlagszeile - eine Zeile, kein Kasten. */
.tw-cand-dup {
  margin-top: 2px; font-size: 10.5px; line-height: 1.35;
  display: flex; flex-wrap: wrap; gap: 5px;
}
.tw-cand-dup > span { cursor: help; }
/* Eigene Klassennamen (cd-…), NICHT die des Abzeichens oben. Im ersten
   Entwurf hiessen beide tw-dup-hart - die Vorschlagszeile erbte damit
   Hintergrund und Rahmen des Abzeichens und wurde zum breiten roten
   Balken. Im Screenshot sofort zu sehen, im Quelltext nicht. */
.tw-cand-dup.cd-hart   { color: #fca5a5; font-weight: 600; }
.tw-cand-dup.cd-mittel { color: #fbbf24; }
.tw-cand-dup.cd-leise  { color: var(--muted, #9aa0aa); }
.tw-cand-dup-wo { opacity: .75; font-weight: 400; }

/* v1.3.3 – Erstattung an übernommenen Wünschen */
.erstattet-tag, .erstattung-nein {
  font-size: 10.5px; font-weight: 700; padding: 2px 8px;
  border-radius: 999px; cursor: help; white-space: nowrap;
}
.erstattet-tag   { background: rgba(34,197,94,.16);   color: #86efac; }
.erstattung-nein { background: rgba(148,163,184,.14); color: #94a3b8; font-weight: 400; }
/* Der "Liste löschen"-Knopf soll klein und unauffällig sein – gefährlich,
   aber nicht dauernd im Blick. */
.btn.mini.leise { opacity: .55; padding: 3px 7px; font-size: 12px; }
.btn.mini.leise:hover { opacity: 1; }

/* ===========================================================================
   v1.3.3 – kompakter Raid-Tab und Sprachumschalter
   ===========================================================================
   Der Raid-Tab war in v1.3.2 sehr groß: vier Kacheln über die volle Breite,
   darunter erst die eigentliche Liste. Die Kennzahlen wandern deshalb in
   eine schmale Zeile neben die Überschrift – man sieht sie weiter, sie
   kosten aber keinen halben Bildschirm mehr.
   =========================================================================== */

.raid-kopfzeile {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 14px; flex-wrap: wrap;
}
.raid-kopfzeile h2 { margin: 0; }

.raid-zahlen-schmal {
  display: flex; gap: 16px; flex-wrap: wrap;
  font-size: 12.5px; opacity: .8;
}
.raid-zahlen-schmal b { font-size: 15px; font-weight: 800; opacity: 1; }

/* Shoutout-Zustand je Raid. Grün = erledigt, grau = offen. Beide Zustände
   sind gleich breit gebaut, damit die Zeilen nicht springen. */
.raid-so-ja, .raid-so-nein {
  font-size: 11px; padding: 2px 8px; border-radius: 999px;
  white-space: nowrap; flex: 0 0 auto;
}
.raid-so-ja   { background: rgba(34,197,94,.16); color: #86efac; font-weight: 700; cursor: help; }
.raid-so-nein { background: rgba(148,163,184,.12); color: #94a3b8; }

.raid-feed-zeile .btn.mini { flex: 0 0 auto; }

@media (max-width: 620px) {
  /* Auf dem Handy bricht die Zeile um, statt die Zeit abzuschneiden. */
  .raid-feed-zeile { flex-wrap: wrap; }
  .raid-feed-haupt { flex: 1 1 100%; order: 1; }
  .raid-feed-zeile .raid-mini-bild,
  .raid-feed-initial { order: 0; }
  .raid-so-ja, .raid-so-nein, .raid-feed-zeile .btn.mini, .raid-feed-zeit { order: 2; }
}

/* Sprachumschalter in der Kopfzeile. Bewusst winzig – er wird einmal
   benutzt und danach nie wieder.

   v1.7.3: ein Auswahlfeld statt zweier Knoepfe. Zwei Knoepfe nebeneinander
   sahen aus wie ein Schalter, der immer halb "aus" ist, und kosteten die
   doppelte Breite. Jetzt steht nur die aktuelle Sprache da; auf dem Telefon
   klappt das Betriebssystem seine eigene Auswahl auf.

   Kuerzel DE/EN statt Flaggen (seit v1.3.4): Flaggen stehen fuer Laender,
   nicht fuer Sprachen - Englisch ist nicht "britisch". */
/* v1.7.4: eigenes Auswahlfeld statt des nativen <select>.

   v1.7.3 hatte ein <select> - und das war ein Rueckschritt. Im
   Dunkelmodus uebernimmt es die Farben des Betriebssystems: beim
   Aufklappen stand hellgraue Schrift auf hellgrauem Grund, die zweite
   Sprache war praktisch unlesbar. Daran laesst sich per CSS nichts
   aendern, das Aufklappmenue gehoert dem Browser.

   Jetzt: ein Knopf mit der aktuellen Sprache, und beim Klick legt sich
   eine eigene Leiste ueber die Seite. */
.sprach-knopf {
  display: inline-flex; align-items: center; gap: 3px;
  border: 0; border-radius: 999px; cursor: pointer;
  padding: 3px 8px 3px 9px;
  background: rgba(148,163,184,.14); color: inherit;
  font-size: 11.5px; font-weight: 700; letter-spacing: .4px;
}
.sprach-knopf:hover { background: rgba(148,163,184,.26); }
.sprach-pfeil { font-size: 9px; opacity: .7; line-height: 1; }

/* Der Schleier dimmt alles dahinter ab, damit die Auswahl unuebersehbar
   ist. blur nur, wo der Browser es kann - sonst reicht die Abdunklung. */
.sprach-schleier {
  position: fixed; inset: 0; z-index: 2000;
  display: flex; align-items: flex-start; justify-content: center;
  padding-top: 14vh;
  background: rgba(6,8,14,.62);
  backdrop-filter: blur(3px);
  animation: sprachAuf .13s ease-out;
}
@keyframes sprachAuf { from { opacity: 0; } to { opacity: 1; } }

.sprach-leiste {
  display: flex; flex-direction: column; gap: 2px;
  min-width: 210px; max-width: calc(100vw - 32px);
  padding: 8px;
  border-radius: 14px;
  background: #1c2029;
  border: 1px solid rgba(255,255,255,.09);
  box-shadow: 0 18px 44px rgba(0,0,0,.5);
}
:root[data-theme="light"] .sprach-leiste {
  background: #f6f7fa; border-color: rgba(0,0,0,.1);
  box-shadow: 0 18px 44px rgba(0,0,0,.18);
}
.sprach-leiste-titel {
  font-size: 10px; text-transform: uppercase; letter-spacing: .07em;
  opacity: .55; padding: 2px 8px 6px;
}
.sprach-eintrag {
  display: flex; align-items: center; gap: 10px;
  width: 100%; text-align: left;
  padding: 9px 10px; border: 0; border-radius: 9px;
  background: transparent; color: inherit; cursor: pointer;
  font-size: 14px;
}
.sprach-eintrag:hover { background: rgba(148,163,184,.16); }
.sprach-eintrag b { min-width: 2.2em; font-weight: 800; letter-spacing: .5px; }
.sprach-eintrag span { opacity: .72; font-size: 13px; }
.sprach-eintrag.aktiv { background: rgba(var(--accent-rgb, 139,92,246), .18); }
.sprach-haken { margin-left: auto; opacity: .85; }

/* ===========================================================================
   v1.3.4 – Wunschverlauf-Link und blasser Löschknopf
   =========================================================================== */

/* Link zum Wunschverlauf, ganz unten im Musikfeed. Bewusst ruhig: man braucht
   ihn selten, aber dann soll er auffindbar sein. */
.wv-link-karte {
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
  padding: 11px 15px; margin-top: 10px;
  border: 1px dashed var(--border); border-radius: var(--radius);
  background: transparent;
}
.wv-link-karte a { font-weight: 700; font-size: 13.5px; text-decoration: none; color: var(--accent); }
.wv-link-karte a:hover { text-decoration: underline; }
.wv-link-karte .hint { margin: 0; font-size: 12px; flex: 1 1 260px; }

/* Der zweite Löschknopf ("ohne Erstattung"). Er MUSS erreichbar sein, soll
   aber nicht wie der gemeinte Weg aussehen - deshalb blass, ohne Warnfarbe,
   und erst beim Darüberfahren deutlich. Wer ihn drückt, tut es absichtlich. */
.btn.leise, .icon-btn.leise {
  opacity: .5;
  border-color: var(--border);
  background: transparent;
  font-weight: 400;
}
.btn.leise:hover, .icon-btn.leise:hover {
  opacity: 1;
  border-color: var(--danger);
  color: var(--danger);
}

/* ===========================================================================
   v1.3.4 – Raid-Tab: Twitch-Zeichen und Rundumleuchte
   =========================================================================== */

/* Das Twitch-Zeichen im Tab nimmt die Schriftfarbe an (fill="currentColor"
   im eingebetteten SVG). Grau im ruhenden Tab, hell im aktiven - ohne dass
   dafür eine zweite Bilddatei gepflegt werden muss. */
.twitch-glyph {
  vertical-align: -2px;
  margin-right: 5px;
  opacity: .75;
}
.tab-btn.active .twitch-glyph { opacity: 1; }

/* Rundumleuchte für einen noch nicht bestätigten Raid. Dieselbe Rot/Blau-
   Taktung wie beim Notiz-Alarm (alarm-tag-strobe), damit im Betrieb sofort
   klar ist, was gemeint ist - nur klein und rund statt als Balken. */
.raid-alarm-leuchte {
  display: inline-block;
  width: 9px; height: 9px;
  border-radius: 50%;
  margin-right: 6px;
  vertical-align: 0;
  background: #ef4444;
  box-shadow: 0 0 8px 2px rgba(239,68,68,.75);
  animation: raid-alarm-strobe 1s steps(2, jump-none) infinite;
}
@keyframes raid-alarm-strobe {
  0%, 100% { background: #ef4444; box-shadow: 0 0 8px 2px rgba(239,68,68,.75); }
  50%      { background: #3b82f6; box-shadow: 0 0 8px 2px rgba(59,130,246,.75); }
}

/* Wer Bewegung im Bild nicht verträgt, bekommt die Leuchte still - die Farbe
   allein sagt dasselbe. Betrifft auch den Notiz-Alarm weiter unten nicht,
   der hat seine eigene Regel. */
@media (prefers-reduced-motion: reduce) {
  .raid-alarm-leuchte { animation: none; }
}

/* Raid-Übersicht: die letzten Raids direkt in der Karte, aufgeklappt. */
.raid-letzte {
  display: flex; flex-direction: column; gap: 6px;
  margin-top: 10px;
}
.raid-letzte-zeile {
  display: flex; align-items: center; gap: 9px;
  padding: 7px 10px; border-radius: 9px;
  background: rgba(148,163,184,.07);
  font-size: 13px;
}
.raid-letzte-zeile.offen {
  background: rgba(239,68,68,.1);
  border: 1px solid rgba(239,68,68,.32);
}
.raid-letzte-zeile .name { font-weight: 700; }
.raid-letzte-zeile .wann { margin-left: auto; font-size: 11.5px; opacity: .62; white-space: nowrap; }
.raid-letzte-leer { font-size: 12.5px; opacity: .65; padding: 6px 2px; }

/* ===========================================================================
   v1.3.4 – Einstellungen mit Seitenleiste
   ===========================================================================
   Am Rechner zwei Spalten: links die Bereiche, rechts einer davon.
   Auf dem Handy zweistufig – erst die Liste, nach dem Antippen der Bereich
   im Vollbild mit Zurück-Knopf. Umgeschaltet wird über die Klassen
   .liste-offen / .bereich-offen am Container (siehe viewEinstellungenNeu).
   =========================================================================== */

/* ===========================================================================
   v1.28.24 - DAS GERUEST (Saschas Umbau, Schritt 1)
   ---------------------------------------------------------------------------
   Woertlich: "am besten so, das nur die 'rechte seite' dann scrollbar ist so
   das die Menuepunkte oben und links immer an der gleichen stelle sind quasi
   wie bei excel wenn man die oberste zeile und links die erste spalte
   fixiert". Genau das macht dieses Raster: Die Seite selbst scrollt nicht
   mehr, sondern der Inhaltsbereich rechts - Baum und Speicherleiste stehen
   still. Auf dem Handy bleibt es beim bisherigen Zweistufen-Verhalten (dort
   waere eine feste Spalte nur im Weg).
   ========================================================================= */
.einst-layout {
  display: grid;
  grid-template-columns: 214px 1fr;
  gap: 12px;
  align-items: stretch;
  max-width: 1180px;
  margin: 0 auto;
  padding: 12px 14px 14px;
  /* Die Hoehe, die unter der Kopfzeile uebrig bleibt. */
  height: calc(100vh - var(--einst-kopf, 96px));
  min-height: 420px;
  overflow: hidden;
}
.einst-layout > .einst-inhalt { overflow-y: auto; overflow-x: hidden; min-height: 0; padding-right: 6px; }
.einst-seitenleiste {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0;
  position: static;
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
}
/* v1.28.24: Saschas Vorgabe "nicht so extrem weit weg vom Rand" - der Baum
   rueckt dichter an seine Kanten, ohne dass etwas ueberlappt. */
.sl-liste { overflow-y: auto; min-height: 0; flex: 1 1 auto; padding: 5px 0 9px; }
.sl-gruppe {
  font-size: 10px; text-transform: uppercase; letter-spacing: .7px;
  color: var(--text-dim); padding: 9px 8px 3px; opacity: .75; font-weight: 700;
}
.sl-trenner { height: 1px; background: var(--border); margin: 7px 9px; }
.sl-punkt {
  display: flex; align-items: center; gap: 9px; width: 100%;
  border: 0; background: transparent; color: var(--text-dim);
  padding: 8px 11px; border-radius: 9px; cursor: pointer;
  font-size: 13.5px; font-family: inherit; text-align: left;
}
.sl-punkt:hover { background: rgba(148,163,184,.1); color: var(--text); }
.sl-punkt.aktiv { background: var(--accent); color: var(--accent-text); font-weight: 700; }
.sl-punkt .sym { width: 18px; text-align: center; flex: 0 0 auto; }
.sl-punkt .txt { flex: 1 1 auto; min-width: 0; }
/* Der Pfeil zeigt auf dem Handy, dass hier eine zweite Ebene wartet.
   Am Rechner wäre er irreführend – dort steht der Inhalt schon daneben. */
.sl-punkt .pfeil { display: none; opacity: .5; flex: 0 0 auto; }

.einst-inhalt { min-width: 0; }
.einst-inhalt-kopf { display: flex; align-items: center; gap: 11px; margin-bottom: 12px; }
.einst-inhalt-kopf h2 { margin: 0; font-size: 18px; }
/* Der Zurück-Knopf gehört zur Handy-Ansicht und stört am Rechner nur. */
.einst-zurueck { display: none; }

/* ---- Handy: zweistufig -------------------------------------------------- */
@media (max-width: 820px) {
  /* v1.28.24: Auf dem Handy KEINE feste Hoehe - dort blaettert die Seite
     wie gewohnt, und immer nur eine der beiden Ebenen ist sichtbar. */
  .einst-layout { grid-template-columns: 1fr; padding: 10px 12px 40px;
                  height: auto; overflow: visible; }
  .einst-layout > .einst-inhalt { overflow: visible; padding-right: 0; }
  .einst-seitenleiste { position: static; overflow: visible; }
  .sl-liste { overflow: visible; }

  .sl-punkt { padding: 12px 12px; font-size: 15px; }   /* größere Tippfläche */
  .sl-punkt .pfeil { display: block; }
  .sl-punkt.aktiv { background: rgba(148,163,184,.14); color: var(--text); }

  .einst-zurueck { display: inline-flex; }

  /* Immer nur eine der beiden Ebenen zeigen. */
  .einst-layout.liste-offen .einst-inhalt { display: none; }
  .einst-layout.bereich-offen .einst-seitenleiste { display: none; }
}


/* v1.3.4.1 - eine Reihe der Werkzeugleiste: innerhalb der Reihe darf auf
   schmalen Fenstern weiter umbrochen werden, die Gruppierung bleibt fest. */
.tb-zeile {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

/* "Zuletzt"-Chips sind jetzt Sprungmarken - Zeigefinger und ein leichtes
   Anheben beim Darueberfahren, damit man ihnen die Klickbarkeit ansieht. */
.recent-added-item[data-spring-zu] { cursor: pointer; }
.recent-added-item[data-spring-zu]:hover {
  background: rgba(148,163,184,.18);
  transform: translateY(-1px);
}

/* ===========================================================================
   v1.3.6 – Stream-Übersicht im Raid-Tab
   =========================================================================== */
.raid-stream-block {
  margin: 12px 0 4px;
  padding: 11px 14px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: rgba(148,163,184,.05);
}
.raid-stream-block h3 { margin: 0 0 8px; font-size: 13.5px; }
.raid-stream-kanaele { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 8px; }
/* v1.28.7: Hinweis "Stream vorbei - Ansicht leert sich beim naechsten
   Live-Start". Bewusst ruhig (kein Alarm): es ist eine Vormerkung, kein
   Problem. */
.raid-fenster-vorbei {
  font-size: 12.5px;
  margin: 0 0 8px;
  padding: 6px 10px;
  border-radius: 8px;
  border: 1px dashed rgba(148,163,184,.45);
  opacity: .8;
}

/* =============================================================================
   v1.28.8 - Raid-Tab: Stream-Titel, Nachzuegler-Marke, Historie als Kanal-Log
   ============================================================================= */
.raid-feed-titel {
  font-size: 12px;
  opacity: .7;
  font-style: italic;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 340px;
}
.raid-nachzuegler {
  font-size: 11.5px;
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(234,179,8,.14);
  border: 1px solid rgba(234,179,8,.35);
  cursor: help;
}
.raid-hist-leiste {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 8px;
}
.raid-hist-sort { font-size: 12.5px; display: inline-flex; gap: 6px; align-items: center; }
.raid-hist-sort .btn.aktiv { outline: 1px solid rgba(139,92,246,.55); }
.raid-hist-gruppe {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 4px 8px;
  margin-bottom: 8px;
}
.raid-hist-gruppe .raid-hist-kopf { border-bottom: 0; }
.raid-hist-altliste { margin: 0 0 4px 44px; }
/* Bei vielen Raids eines Kanals scrollt die aufgeklappte Liste in sich,
   statt die Ansicht zu sprengen (Saschas Vorgabe). */
.raid-hist-scroll { max-height: 240px; overflow-y: auto; }
.raid-hist-alt {
  display: flex;
  gap: 10px;
  align-items: center;
  font-size: 12.5px;
  opacity: .75;
  padding: 2px 0;
}
.raid-hist-alt .raid-feed-titel { max-width: 260px; }
.raid-hist-alt-zeit { font-variant-numeric: tabular-nums; }
.raid-hist-mehr { margin: 0 0 6px 44px; }

/* =============================================================================
   v1.28.9 - "Moegliche Raidziele" (DJ-Anzeige) + Medaillen in der Raidkarte
   Saschas Wahl: Mischung aus Variante B (Farbkante links, Nummer und Stimmen
   in der Medaillenfarbe) und Variante C (zart schimmernder Rahmen).
   ============================================================================= */

/* --- Raidkarte: zweite Chip-Zeile mit dem Stream-Titel --- */
.rv-zeile { flex-wrap: wrap; }
.rv-titelzeile {
  position: relative; z-index: 1;
  flex-basis: 100%;
  font-size: 12px; opacity: .65; font-style: italic;
  padding-left: 34px; margin-top: 1px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* --- Medaillen (Raidkarte, dezent) --- */
.rv-zeile.rv-gold { border-left: 4px solid #eab308; border-color: rgba(234,179,8,.5); box-shadow: 0 0 0 1px rgba(234,179,8,.18), inset 0 0 12px rgba(234,179,8,.06); }
.rv-zeile.rv-silber { border-left: 4px solid #b7bdc9; border-color: rgba(183,189,201,.5); box-shadow: 0 0 0 1px rgba(183,189,201,.16), inset 0 0 12px rgba(183,189,201,.05); }
.rv-zeile.rv-bronze { border-left: 4px solid #cd7f32; border-color: rgba(205,127,50,.5); box-shadow: 0 0 0 1px rgba(205,127,50,.18), inset 0 0 12px rgba(205,127,50,.06); }
.rv-zeile.rv-gold .rv-nummer, .rv-zeile.rv-gold .rv-zahl { color: #eab308; }
.rv-zeile.rv-silber .rv-nummer, .rv-zeile.rv-silber .rv-zahl { color: #b7bdc9; }
.rv-zeile.rv-bronze .rv-nummer, .rv-zeile.rv-bronze .rv-zahl { color: #cd7f32; }

/* --- DJ-Anzeige: Block "Moegliche Raidziele" (immer unten, kompakt) --- */
.djrz-block {
  margin-top: 14px;
  border: 1px solid var(--border);
  /* v1.28.11: Rand dezent in der global eingestellten Akzentfarbe -
     color-mix mischt sie leise in den normalen Rahmen; Browser ohne
     color-mix behalten die Zeile darueber. */
  border-color: color-mix(in srgb, var(--accent) 32%, var(--border));
  border-radius: 12px;
  padding: 8px 10px 4px;
  background: rgba(148,163,184,.04);
}
/* v1.28.11: Ueberschrift fett und heller (Saschas Wunsch: "in Fett und
   etwas heller also weisser"). */
.djrz-kopf { font-size: 12px; font-weight: 700; letter-spacing: .4px; text-transform: uppercase; color: var(--text); opacity: .95; margin: 0 0 6px 2px; }
.djrz-fest { text-transform: none; letter-spacing: 0; }
.djrz-ziel {
  display: flex; gap: 10px; align-items: stretch;
  padding: 5px 8px; border-radius: 9px;
  border: 1px solid var(--border); background: rgba(148,163,184,.05);
  margin-bottom: 6px;
}
.djrz-nr {
  align-self: center; min-width: 30px; height: 30px; border-radius: 8px;
  display: flex; align-items: center; justify-content: center;
  font-weight: 800; font-size: 15px;
  background: rgba(148,163,184,.12); border: 1px solid var(--border);
}
.djrz-mitte { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.djrz-z1 { display: flex; align-items: baseline; gap: 10px; }
/* v1.28.11: Kanalname und Streamtitel groesser (waren "etwas zu klein"). */
.djrz-kanal { font-weight: 700; font-size: 15.5px; }
.djrz-komm {
  margin-left: auto; font-size: 12px;
  background: rgba(139,92,246,.12); border: 1px solid rgba(139,92,246,.3);
  border-radius: 999px; padding: 1px 9px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 48%;
}
.djrz-z2 { display: flex; align-items: center; gap: 8px; }
.djrz-titel { font-size: 13px; opacity: .65; font-style: italic; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 1; }
.djrz-stimmen { font-size: 14px; font-weight: 800; margin-left: auto; white-space: nowrap; }
.djrz-stimmen small { font-weight: 400; opacity: .7; font-size: 10.5px; margin-left: 3px; }
.djrz-rakete { border: 1px solid var(--border); background: rgba(148,163,184,.1); color: inherit; border-radius: 7px; font-size: 11.5px; padding: 2px 8px; cursor: pointer; }

/* Medaillen im DJ-Block (B+C-Mix, gleiche Sprache wie in der Raidkarte) */
.djrz-ziel.djrz-gold { border-left: 4px solid #eab308; border-color: rgba(234,179,8,.5); box-shadow: 0 0 0 1px rgba(234,179,8,.2), inset 0 0 12px rgba(234,179,8,.07); }
.djrz-ziel.djrz-silber { border-left: 4px solid #b7bdc9; border-color: rgba(183,189,201,.5); box-shadow: 0 0 0 1px rgba(183,189,201,.18), inset 0 0 12px rgba(183,189,201,.06); }
.djrz-ziel.djrz-bronze { border-left: 4px solid #cd7f32; border-color: rgba(205,127,50,.5); box-shadow: 0 0 0 1px rgba(205,127,50,.2), inset 0 0 12px rgba(205,127,50,.07); }
.djrz-gold .djrz-nr, .djrz-gold .djrz-stimmen { color: #eab308; border-color: rgba(234,179,8,.5); }
.djrz-silber .djrz-nr, .djrz-silber .djrz-stimmen { color: #b7bdc9; border-color: rgba(183,189,201,.5); }
.djrz-bronze .djrz-nr, .djrz-bronze .djrz-stimmen { color: #cd7f32; border-color: rgba(205,127,50,.5); }

/* Helles Design: Silber/Gold brauchen dunklere Toene, sonst unlesbar. */
html[data-theme="light"] .rv-zeile.rv-gold .rv-nummer, html[data-theme="light"] .rv-zeile.rv-gold .rv-zahl,
html[data-theme="light"] .djrz-gold .djrz-nr, html[data-theme="light"] .djrz-gold .djrz-stimmen { color: #a16207; }
html[data-theme="light"] .rv-zeile.rv-silber .rv-nummer, html[data-theme="light"] .rv-zeile.rv-silber .rv-zahl,
html[data-theme="light"] .djrz-silber .djrz-nr, html[data-theme="light"] .djrz-silber .djrz-stimmen { color: #64748b; }
html[data-theme="light"] .rv-zeile.rv-bronze .rv-nummer, html[data-theme="light"] .rv-zeile.rv-bronze .rv-zahl,
html[data-theme="light"] .djrz-bronze .djrz-nr, html[data-theme="light"] .djrz-bronze .djrz-stimmen { color: #92400e; }
@media (prefers-color-scheme: light) {
  html:not([data-theme]) .rv-zeile.rv-gold .rv-nummer, html:not([data-theme]) .rv-zeile.rv-gold .rv-zahl,
  html:not([data-theme]) .djrz-gold .djrz-nr, html:not([data-theme]) .djrz-gold .djrz-stimmen { color: #a16207; }
  html:not([data-theme]) .rv-zeile.rv-silber .rv-nummer, html:not([data-theme]) .rv-zeile.rv-silber .rv-zahl,
  html:not([data-theme]) .djrz-silber .djrz-nr, html:not([data-theme]) .djrz-silber .djrz-stimmen { color: #64748b; }
  html:not([data-theme]) .rv-zeile.rv-bronze .rv-nummer, html:not([data-theme]) .rv-zeile.rv-bronze .rv-zahl,
  html:not([data-theme]) .djrz-bronze .djrz-nr, html:not([data-theme]) .djrz-bronze .djrz-stimmen { color: #92400e; }
}
.raid-stream-chip {
  font-size: 12.5px; padding: 4px 11px; border-radius: 999px;
  background: rgba(139,92,246,.14); border: 1px solid rgba(139,92,246,.3);
}

/* ===========================================================================
   v1.3.7 – Belohnungs-Sets
   =========================================================================== */

/* Schaltleiste im Prüfen-Tab: ein Klick wendet ein Set an. */
.set-leiste {
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
  padding: 9px 13px; margin: 10px 0;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg-elevated);
}
.set-leiste-titel {
  font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .5px; opacity: .65; margin-right: 3px;
}
.set-leiste-info { font-size: 11.5px; opacity: .6; margin-left: auto; cursor: help; }

/* Verwaltung in den Einstellungen */
.set-verwaltung-koepfe { display: flex; gap: 10px; flex-wrap: wrap; margin: 10px 0; }
.set-kopf {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 5px 7px 5px 12px; border-radius: 9px;
  background: rgba(139,92,246,.12); border: 1px solid rgba(139,92,246,.3);
  font-size: 13px;
}
.set-zuordnung { display: flex; flex-direction: column; gap: 7px; margin: 12px 0; }
.set-zeile {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 8px 11px; border: 1px solid var(--border); border-radius: 9px;
}
.set-zeile.ausgenommen { opacity: .55; }
.set-zeile-name { flex: 0 0 190px; }
.set-zeile-chips { display: flex; gap: 6px; flex-wrap: wrap; flex: 1 1 auto; }
/* v1.3.11: deutlich als Schalter erkennbar (☐/☑ im Text, kraeftigere
   Zustaende) - vorher wirkten die Chips wie blosse Beschriftungen und die
   Zuordnung wurde uebersehen. */
.set-chip {
  border: 1px solid var(--border); background: transparent; color: var(--text-dim);
  border-radius: 8px; padding: 5px 12px; font-size: 12.5px; cursor: pointer;
  font-family: inherit;
}
.set-chip:hover:not(:disabled) { border-color: var(--accent); color: var(--text); }
.set-chip.drin {
  background: var(--accent); border-color: var(--accent);
  color: var(--accent-text); font-weight: 700;
}
.set-chip:disabled { cursor: not-allowed; opacity: .45; }
.set-ausnahme { margin: 0; flex: 0 0 auto; font-size: 12px; }
.set-anlegen { display: flex; gap: 8px; margin-top: 12px; }
.set-anlegen input { flex: 1 1 220px; max-width: 320px; }

/* ===========================================================================
   v1.3.8 – Raid-Alarm in der Kopfzeile
   ===========================================================================
   Sitzt zwischen App-Namen und Sprachumschalter und ist damit in JEDEM
   Bereich sichtbar (auch in der DJ-Anzeige, eigene Topbar). Dieselbe
   Rot/Blau-Rundumleuchte wie beim Notiz-Alarm, damit die Bedeutung im
   Betrieb sofort klar ist. */
.raid-alarm-chip {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 13px; border-radius: 999px;
  background: rgba(239,68,68,.13);
  border: 1px solid rgba(239,68,68,.5);
  color: var(--text); font-size: 13px; font-family: inherit;
  white-space: nowrap;
  animation: raid-chip-rand 1s steps(2, jump-none) infinite;
}
button.raid-alarm-chip { cursor: pointer; }
button.raid-alarm-chip:hover { background: rgba(239,68,68,.22); }
.raid-alarm-chip .raid-alarm-leuchte { margin-right: 0; }
.raid-alarm-mehr { font-size: 11px; opacity: .75; }
@keyframes raid-chip-rand {
  0%, 100% { border-color: rgba(239,68,68,.55); }
  50%      { border-color: rgba(59,130,246,.55); }
}
@media (prefers-reduced-motion: reduce) {
  .raid-alarm-chip { animation: none; }
}
/* In der DJ-Topbar etwas kompakter, die Leiste ist schmal. */
.djview-topbar .raid-alarm-chip { padding: 3px 11px; font-size: 12.5px; }

/* ---------------------------------------------------------------------------
   v1.3.14: Aufforderungs-Chip der Raid-Abstimmung.

   Gleiche Stelle und Bauart wie der Raid-Alarm, aber violett und ruhiger -
   es brennt nichts an, es fehlt nur die eigene Stimme. Pulsiert dezent;
   bei prefers-reduced-motion steht er still.
   --------------------------------------------------------------------------- */
.rv-chip {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 5px 13px; border-radius: 999px;
  background: rgba(169,112,255,.14);
  border: 1px solid rgba(169,112,255,.5);
  color: var(--text); font-size: 13px; font-family: inherit;
  white-space: nowrap; cursor: pointer;
  animation: rv-chip-puls 2s ease-in-out infinite;
}
.rv-chip:hover { background: rgba(169,112,255,.24); }
.rv-chip:focus-visible { outline: 2px solid #a970ff; outline-offset: 2px; }
.rv-chip-punkt {
  width: 7px; height: 7px; border-radius: 50%;
  background: #a970ff; flex: none;
}
@keyframes rv-chip-puls {
  0%, 100% { box-shadow: 0 0 0 0 rgba(169,112,255,.45); }
  50%      { box-shadow: 0 0 0 6px rgba(169,112,255,0); }
}
@media (prefers-reduced-motion: reduce) {
  .rv-chip { animation: none; }
}
.djview-topbar .rv-chip { padding: 3px 11px; font-size: 12.5px; }


/* === v1.3.13: Raid-Abstimmung ============================================ */
.rv-karte { margin-top: 16px; }
.rv-aus {
  font-size: 11px; font-weight: 700; text-transform: uppercase;
  padding: 2px 9px; border-radius: 999px;
  background: rgba(148,163,184,.18); color: #cbd5e1;
}
.rv-ergebnis {
  margin: 4px 0 12px; padding: 9px 13px; border-radius: 9px; font-size: 13.5px;
  background: rgba(34,197,94,.1); border: 1px solid rgba(34,197,94,.3);
}
.rv-ergebnis.gleich { background: rgba(250,204,21,.1); border-color: rgba(250,204,21,.35); }
.rv-ergebnis.leer { background: rgba(148,163,184,.08); border-color: rgba(148,163,184,.22); color: var(--text-dim); }
.rv-liste { display: flex; flex-direction: column; gap: 6px; }
.rv-zeile {
  position: relative; display: flex; align-items: center; gap: 10px;
  padding: 9px 12px; border-radius: 9px; overflow: hidden;
  border: 1px solid var(--border); background: rgba(148,163,184,.05);
}
.rv-zeile.gewaehlt { border-color: var(--accent); }
/* Der Balken liegt HINTER dem Text - deshalb eigener Layer statt Hintergrund,
   sonst müsste die Schrift auf wechselnder Deckkraft lesbar bleiben. */
.rv-balken {
  position: absolute; left: 0; top: 0; bottom: 0;
  background: rgba(139,92,246,.16); pointer-events: none;
  transition: width .25s ease;
}
.rv-name, .rv-zahl, .rv-zeile button { position: relative; z-index: 1; }
.rv-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rv-name a { color: var(--text); text-decoration: none; font-weight: 600; }
.rv-name a:hover { color: var(--accent); }
.rv-von { font-size: 11px; color: var(--text-dim); margin-left: 7px; }
.rv-zahl { font-size: 12.5px; opacity: .85; font-variant-numeric: tabular-nums; white-space: nowrap; }
.rv-neu { display: flex; gap: 8px; margin-top: 14px; flex-wrap: wrap; }
.rv-neu input { flex: 1 1 220px; max-width: 340px; }
.rv-verwaltung { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; margin-top: 10px; }
.rv-rechte { margin-top: 10px; }

/* v1.3.14: Loeschknopf je Kanal - vorher ein blasses Symbol ohne Flaeche,
   das man auf dem Touchscreen kaum traf. Jetzt 30x30 mit Rahmen; rot wird
   er erst beim Ueberfahren, damit die Liste nicht nach Gefahr schreit. */
.rv-weg {
  display: grid; place-items: center;
  width: 30px; height: 30px; flex: none;
  background: rgba(255,255,255,.05);
  border: 1px solid var(--border, rgba(255,255,255,.16));
  border-radius: 8px;
  color: var(--dim, #9b93ad);
  font-size: 14px; line-height: 1; cursor: pointer;
  transition: background .12s, border-color .12s, color .12s;
}
.rv-weg:hover, .rv-weg:focus-visible {
  background: rgba(229,72,77,.16);
  border-color: rgba(229,72,77,.45);
  color: #ff6b6f;
}
.rv-weg:focus-visible { outline: 2px solid var(--acc, #a970ff); outline-offset: 2px; }

/* v1.8.1: "diesen Kanal nehmen" direkt in der Zeile. Gleiche Groesse wie
   der Papierkorb daneben, aber in der Akzentfarbe - der eine Knopf raeumt
   weg, der andere entscheidet die Runde. */
.rv-nehmen {
  display: grid; place-items: center;
  width: 30px; height: 30px; flex: none;
  background: rgba(255,255,255,.05);
  border: 1px solid var(--border, rgba(255,255,255,.16));
  border-radius: 8px;
  color: var(--dim, #9b93ad);
  font-size: 14px; line-height: 1; cursor: pointer;
  transition: background .12s, border-color .12s, color .12s;
}
.rv-nehmen:hover, .rv-nehmen:focus-visible {
  background: rgba(169,112,255,.18);
  border-color: rgba(169,112,255,.5);
  color: #c9a6ff;
}
.rv-nehmen:focus-visible { outline: 2px solid var(--acc, #a970ff); outline-offset: 2px; }

/* v1.3.14: Abschluss der Abstimmung */
.rv-fest {
  color: #f5b942;
  border-color: rgba(245,158,11,.4);
  background: rgba(245,158,11,.1);
}
.rv-vorbereitet { margin-top: 12px; display: flex; flex-direction: column; gap: 10px; }
.rv-befehl {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 9px 12px;
  border: 1px dashed var(--border, rgba(255,255,255,.2));
  border-radius: 9px;
}
.rv-befehl code {
  font-size: 13.5px; padding: 3px 9px; border-radius: 6px;
  background: rgba(169,112,255,.16); color: var(--acc, #c9a4ff);
}
.rv-befehl-marke {
  font-size: 10.5px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--dim, #9b93ad);
}
.rv-abschlussleiste { border-top: 1px solid var(--border, rgba(255,255,255,.1)); padding-top: 12px; }
.rv-zuletzt { margin: 8px 0 0; }

@media (max-width: 720px) {
  .rv-von { display: none; }
}

/* ---------------------------------------------------------------------------
   v1.3.13: Eigene Befehle / Aktionen.

   Eine Zeile je Befehl. Der Name steht fest links, Aktion und Antworttext
   teilen sich den Platz, und die beiden Schalter (an/aus, loeschen) sitzen
   rechts - sonst rutschen sie untereinander und die Zeile faellt auseinander.
   --------------------------------------------------------------------------- */
.eb-karte { margin-top: 16px; }
.eb-liste { display: flex; flex-direction: column; gap: 8px; margin: 12px 0 4px; }

/* v1.3.20 (Nutzer-Feedback): jede Befehlszeile ist eine kleine Karte -
   Befehlsname als Ueberschrift oben links, darunter beschriftete Felder.
   Die alte 7-Spalten-Zeile fiel auf schmalen Fenstern auseinander und das
   Antworttext-Feld war ohne Beschriftung nicht als solches erkennbar. */
.eb-zeile {
  padding: 10px 12px;
  border: 1px solid var(--border, rgba(255,255,255,.12));
  border-radius: 10px;
  background: rgba(255,255,255,.03);
}
.eb-kopf {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-bottom: 8px;
}
.eb-kopf .eb-name { font-size: 14px; padding: 4px 10px; }
.eb-kopf-rechts {
  display: flex; align-items: center; gap: 8px; margin-left: auto;
}
.eb-koerper {
  display: flex; gap: 12px; flex-wrap: wrap; align-items: flex-end;
}
.eb-koerper .field { margin: 0; flex: 1 1 200px; }
.eb-koerper .eb-text-feld { flex: 2 1 280px; }
.eb-koerper .field label { font-size: 11.5px; opacity: .75; }
/* Ausgeschaltete Befehle blass, aber lesbar - sie sind nicht geloescht. */
.eb-zeile.aus { opacity: .55; }
.eb-zeile.aus .eb-name { text-decoration: line-through; }

.eb-name {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 13px;
  padding: 3px 7px;
  border-radius: 6px;
  background: rgba(139,92,246,.16);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.eb-zeile select, .eb-zeile .eb-text { min-width: 0; width: 100%; }
/* Bei Aktionen ist das Textfeld gesperrt - das soll man sehen. */
.eb-zeile .eb-text:disabled { opacity: .45; cursor: not-allowed; }
.eb-zeile .checkbox-row { margin: 0; justify-self: center; }
.eb-von { font-size: 11.5px; opacity: .6; white-space: nowrap; }

.eb-neu {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  align-items: center;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--border, rgba(255,255,255,.1));
}
.eb-neu input[name="befehl"] { flex: 0 1 180px; }
.eb-neu select { flex: 0 1 220px; }
.eb-neu input[name="text"] { flex: 1 1 240px; min-width: 0; }

/* Schmale Fenster: die Zeile wird zum Block, sonst wird jede Spalte unlesbar
   schmal. Die Schalter bleiben zusammen in einer Reihe. */
@media (max-width: 860px) {
  .eb-zeile { grid-template-columns: 1fr auto auto; }
  .eb-zeile select, .eb-zeile .eb-text { grid-column: 1 / -1; }
  .eb-von { grid-column: 1 / -1; }
}

/* ---------------------------------------------------------------------------
   v1.3.14: Steuerungs-Whitelist (Einstellungen -> Chat-Befehle).

   Eine kleine Haken-Tabelle: Zeile = Twitch-Konto, Spalte = Steuerbefehl.
   Sie waechst mit dem Katalog - neue Steuerbefehle brauchen hier nichts.
   --------------------------------------------------------------------------- */
.tabelle-scroll { overflow-x: auto; }
.steuer-tabelle {
  border-collapse: collapse;
  font-size: 13px;
  min-width: 320px;
}
.steuer-tabelle th {
  text-align: left;
  font-size: 11px; font-weight: 700;
  letter-spacing: .05em; text-transform: uppercase;
  color: var(--dim, #9b93ad);
  padding: 6px 10px;
  border-bottom: 1px solid var(--border, rgba(255,255,255,.14));
  white-space: nowrap;
}
.steuer-tabelle td {
  padding: 7px 10px;
  border-bottom: 1px solid var(--border, rgba(255,255,255,.07));
}
.steuer-tabelle code { font-size: 12.5px; }

/* ---------------------------------------------------------------------------
   v1.3.14: Befehl-Tester (Einstellungen -> Chat-Befehle).

   Eine Chat-Zeile: Befehl waehlen, Empfaenger tippen (Namen werden
   vervollstaendigt), senden. Die Trefferliste liegt UEBER dem Inhalt
   darunter, deshalb die absolute Position samt z-index.
   --------------------------------------------------------------------------- */
.ebt {
  margin: 14px 0 4px;
  padding: 12px;
  border: 1px solid var(--border, rgba(255,255,255,.12));
  border-radius: 10px;
  background: rgba(255,255,255,.03);
}
.ebt-zeile { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.ebt-zeile select { flex: 0 1 160px; }
.ebt-eingabe-huelle { position: relative; flex: 1 1 220px; min-width: 0; }
.ebt-eingabe-huelle input { width: 100%; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 13px; }
.ebt-liste {
  position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 30;
  background: var(--bg-card, #201f2b);
  border: 1px solid var(--border, rgba(255,255,255,.2));
  border-radius: 9px;
  box-shadow: 0 8px 24px rgba(0,0,0,.45);
  overflow: hidden;
}
.ebt-eintrag {
  display: flex; align-items: center; gap: 9px;
  width: 100%; padding: 7px 11px;
  background: none; border: none; text-align: left; cursor: pointer;
  color: var(--text, #ece9f2); font: inherit; font-size: 12.5px;
}
.ebt-eintrag:hover, .ebt-eintrag.aktiv { background: rgba(169,112,255,.18); }
.ebt-name { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
.ebt-quelle {
  margin-left: auto; font-size: 10px; font-weight: 700;
  letter-spacing: .05em; text-transform: uppercase;
  color: var(--dim, #9b93ad);
}
.ebt-vorschau {
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
  margin-top: 9px; padding: 8px 11px;
  border: 1px dashed var(--border, rgba(255,255,255,.18));
  border-radius: 8px;
}
.ebt-vorschau code { font-size: 13px; background: rgba(169,112,255,.14); padding: 2px 8px; border-radius: 6px; }
.ebt-marke {
  font-size: 10.5px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--dim, #9b93ad);
}

/* ============================================================================
   v1.3.16: Medienwiedergabe / Medienerkennung
   "Laeuft gerade"-Leiste (Musikfeed + DJ-Anzeige), Ampel, Quellen-Verwaltung
   ============================================================================ */
.medien-jetzt {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 0 0 12px; padding: 9px 14px; border-radius: 10px;
  background: var(--bg-elevated); border: 1px solid var(--border);
  font-size: 13.5px;
}
/* v1.3.21 (Nutzer-Feedback): DJ-Anzeige braucht jeden Pixel - kompakte
   Variante mit minimalem Rand, Musikfeed-Variante etwas schmaler. */
.medien-jetzt { padding: 6px 12px; margin-bottom: 10px; }
.medien-jetzt.kompakt {
  margin: 3px 8px; padding: 3px 9px; font-size: 12.5px; gap: 7px;
  border-radius: 7px;
}
.medien-jetzt.kompakt .mj-treffer { font-size: 11.5px; padding: 1px 7px; }
.medien-jetzt .mj-text b { font-weight: 700; }
.medien-jetzt .mj-app { opacity: .6; font-size: 12px; }
.mj-treffer { font-size: 12.5px; padding: 2px 9px; border-radius: 999px; white-space: nowrap; }
.mj-treffer.sicher   { background: rgba(34,197,94,.16);  color: #86efac; }
.mj-treffer.unsicher { background: rgba(245,158,11,.16); color: #fcd34d; }
.mj-treffer.keiner   { background: rgba(148,163,184,.14); color: #cbd5e1; }

/* Die Ampel: gruen = sicher, gelb/orange = Restzweifel, rot = nichts erkannt
   oder Erkennung nicht aktiv. Grenzen einstellbar (Einstellungen ->
   Medienwiedergabe). */
.medien-ampel {
  display: inline-block; width: 12px; height: 12px; border-radius: 50%;
  flex: 0 0 auto; box-shadow: 0 0 0 2px rgba(255,255,255,.06);
}
.medien-ampel.gross { width: 15px; height: 15px; }
.medien-ampel.gruen  { background: #22c55e; box-shadow: 0 0 8px rgba(34,197,94,.7); }
.medien-ampel.gelb   { background: #eab308; box-shadow: 0 0 8px rgba(234,179,8,.6); }
.medien-ampel.orange { background: #f97316; box-shadow: 0 0 8px rgba(249,115,22,.6); }
.medien-ampel.rot    { background: #ef4444; box-shadow: 0 0 6px rgba(239,68,68,.5); }

/* Chip an automatisch vorgemerkten Wuenschen */
.badge.medien-auto { background: rgba(34,197,94,.14); color: #86efac; }
.badge.medien-auto.unsicher { background: rgba(245,158,11,.16); color: #fcd34d; }

/* Quellen-Verwaltung in den Einstellungen */
.medien-quelle {
  border: 1px solid var(--border); border-radius: 10px;
  padding: 10px 12px; margin: 8px 0;
}
.medien-quelle.inaktiv { opacity: .55; }
.medien-quelle .mq-kopf { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.medien-quelle .mq-kopf b { flex: 1 1 auto; }
.medien-quelle .mq-key {
  display: flex; align-items: center; gap: 8px; margin: 7px 0 4px; flex-wrap: wrap;
}
.medien-quelle .mq-key code {
  font-size: 12px; background: rgba(169,112,255,.12); padding: 3px 8px;
  border-radius: 6px; word-break: break-all;
}
.mq-neu { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
.mq-neu input { flex: 1 1 200px; }
.medien-warnung {
  margin: 8px 0; padding: 9px 13px; border-radius: 9px; font-size: 12.5px;
  background: rgba(245,158,11,.12); border: 1px solid rgba(245,158,11,.4);
}
.medien-anleitung { margin: 4px 0 8px 18px; padding: 0; line-height: 1.6; font-size: 13px; }
.medien-anleitung li { margin-bottom: 6px; }

/* v1.3.19: deutliche Warnung, solange niemand !addcommand nutzen darf */
.eb-leer-warnung {
  margin-top: 5px; padding: 7px 11px; border-radius: 8px;
  background: rgba(245,158,11,.12); border: 1px solid rgba(245,158,11,.4);
  color: #fcd34d; opacity: 1;
}
\n/* v1.3.20: Mindest-Spielzeit laeuft noch */\n.mj-treffer.wartend { background: rgba(56,189,248,.14); color: #7dd3fc; }\n
/* v1.3.21: Auto-Kennzeichnung im Erledigt-Archiv des Musikfeeds */
.played-auto {
  font-size: 10.5px; font-weight: 700; padding: 1px 7px; border-radius: 999px;
  background: rgba(245,158,11,.16); color: #fcd34d; white-space: nowrap;
  border: 1px solid rgba(245,158,11,.35); margin-left: 6px; flex: 0 0 auto;
}
.played-auto.unsicher { border-style: dashed; }

/* ============================================================================
   v1.4.0: Der AUSGEHENDE Raid - wir raiden jemanden
   Eigene Leiste, weil sie etwas hat, das die eingehende Raid-Karte nie
   braucht: eine laufende Frist und einen Abbrechen-Knopf.
   ============================================================================ */
.raidaus {
  position: fixed; top: 0; left: 0; right: 0; z-index: 900;
  background: linear-gradient(90deg, rgba(145,70,255,.96), rgba(109,40,217,.96));
  color: #fff;
  box-shadow: 0 6px 24px -10px rgba(0,0,0,.7);
  font-size: 14px;
}
.raidaus-balken { height: 3px; background: rgba(255,255,255,.2); overflow: hidden; }
.raidaus-balken span {
  display: block; height: 100%; background: #fff;
  transition: width 1s linear;
}
.raidaus-inhalt {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 9px 16px;
}
.raidaus-marke { font-weight: 700; white-space: nowrap; }
.raidaus-ziel {
  font-weight: 800; letter-spacing: .01em;
  background: rgba(255,255,255,.16); padding: 2px 10px; border-radius: 999px;
}
.raidaus-zeit {
  font-variant-numeric: tabular-nums; font-weight: 800; font-size: 15px;
  min-width: 3.2em; text-align: right;
}
.raidaus-von, .raidaus-hinweis { font-size: 12.5px; opacity: .82; }
.raidaus-hinweis { flex: 1 1 auto; }
.raidaus-trocken {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
  border: 1px dashed rgba(255,255,255,.65); padding: 1px 7px; border-radius: 999px;
}
.raidaus-knopf {
  margin-left: auto; flex: 0 0 auto;
  background: rgba(0,0,0,.28); color: #fff; border: 1px solid rgba(255,255,255,.45);
  border-radius: 8px; padding: 5px 13px; font-weight: 700; font-size: 13px; cursor: pointer;
}
.raidaus-knopf:hover { background: rgba(0,0,0,.45); }
.raidaus-knopf[disabled] { opacity: .55; cursor: wait; }

/* Abgeschlossene Zustaende: ruhiger, sie sind nur noch Nachricht. */
.raidaus-gelaufen { background: linear-gradient(90deg, rgba(34,197,94,.95), rgba(21,128,61,.95)); }
.raidaus-abgebrochen { background: linear-gradient(90deg, rgba(100,116,139,.96), rgba(51,65,85,.96)); }
.raidaus-zu_spaet { background: linear-gradient(90deg, rgba(245,158,11,.96), rgba(180,83,9,.96)); }

/* DJ-Anzeige: jeder Pixel zaehlt - nur das Noetigste, halbe Hoehe. */
.raidaus.kompakt { font-size: 12.5px; }
.raidaus.kompakt .raidaus-inhalt { padding: 4px 10px; gap: 8px; }
.raidaus.kompakt .raidaus-ziel { padding: 1px 8px; }
.raidaus.kompakt .raidaus-knopf { padding: 2px 9px; font-size: 12px; }
.raidaus.kompakt .raidaus-zeit { font-size: 13px; }

@media (prefers-reduced-motion: reduce) {
  .raidaus-balken span { transition: none; }
}

/* ==========================================================================
   v1.5.0 - Abstimmungsnummern, Empfaenger-Uebersicht, Fluestern
   ========================================================================== */

/* Die feste Abstimmungsnummer vor dem Kanalnamen. Bewusst als Marke und
   nicht bloss als Text: Sie ist das, was im Chat und in der
   Fluesternachricht getippt wird - sie muss sofort ins Auge fallen. */
.rv-nummer {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 1.55em; height: 1.55em; padding: 0 .3em;
  margin-right: .45em;
  border-radius: .4em;
  background: var(--flaeche-2, rgba(255,255,255,.08));
  border: 1px solid var(--rand, rgba(255,255,255,.15));
  font-variant-numeric: tabular-nums;
  font-weight: 700; font-size: .82em; line-height: 1;
  color: var(--text-schwach, #a9b1c3);
}
.rv-zeile.gewaehlt .rv-nummer {
  background: var(--akzent, #8b5cf6); border-color: var(--akzent, #8b5cf6); color: #fff;
}

.rv-fluester-zeile {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  margin-top: 8px; padding-top: 8px;
  border-top: 1px solid var(--rand, rgba(255,255,255,.1));
}

/* Anwesenheitspunkt: gruen = da, hohl = weg, grau = unbekannt.
   Farbe UND Form unterscheiden sich - wer Farben schlecht trennt, sieht
   den Unterschied trotzdem. */
.steuer-punkt { margin-right: .4em; font-size: .9em; line-height: 1; }
.steuer-punkt.an { color: #22c55e; }
.steuer-punkt.aus { color: var(--text-schwach, #8b93a7); }
.steuer-punkt.unbekannt { color: var(--text-schwach, #6b7280); }

.steuer-person { display: inline-flex; align-items: center; gap: .15em; }
.steuer-rolle {
  margin-left: .5em; padding: .1em .45em;
  border-radius: .35em; font-size: .75em;
  background: var(--flaeche-2, rgba(255,255,255,.07));
  color: var(--text-schwach, #a9b1c3);
}
.steuer-rolle.extern { font-style: italic; }
.steuer-login { display: block; margin-top: .2em; font-size: .8em; opacity: .75; }
.steuer-fehlt { display: block; margin: .2em 0 0; font-size: .78em; font-style: italic; }

/* Gruppentrenner in der Steuer-Tabelle: Team-Konten oben, andere darunter. */
.steuer-trenner td {
  padding-top: 12px; padding-bottom: 4px;
  font-size: .78em; text-transform: uppercase; letter-spacing: .05em;
  color: var(--text-schwach, #8b93a7);
  border-bottom: 1px solid var(--rand, rgba(255,255,255,.1));
}
.steuer-trenner .hint { display: inline; margin-left: .6em; text-transform: none; letter-spacing: 0; }
.steuer-gruppe + .steuer-gruppe .steuer-trenner td { padding-top: 18px; }
.steuer-app.ohne-name { opacity: .72; }

.user-twitch { margin-left: .5em; font-size: .82em; opacity: .8; }
.user-twitch-fehlt { display: inline; margin-left: .5em; font-size: .78em; font-style: italic; }

/* Benachrichtigungs-Uebersicht */
.bn-lage {
  margin: 8px 0; padding: 8px 10px; border-radius: 6px;
  font-size: .88em; line-height: 1.45;
  border-left: 3px solid var(--rand, rgba(255,255,255,.2));
  background: var(--flaeche-2, rgba(255,255,255,.05));
}
.bn-lage.warn { border-left-color: #f59e0b; }
.bn-lage.aus { border-left-color: #6b7280; }
.bn-lage.info { border-left-color: #3b82f6; }

.bn-zahlen {
  display: flex; flex-wrap: wrap; align-items: center; gap: 14px;
  margin: 12px 0 8px; font-size: .9em;
}
.bn-zahlen b { font-size: 1.15em; }
.bn-warn { color: #f59e0b; }
.bn-ja { color: #22c55e; }
.bn-nein { color: var(--text-schwach, #8b93a7); font-size: .88em; }
.bn-tabelle tr.bn-nicht { opacity: .68; }
.checkbox-row.mini { font-size: .82em; margin: 0; }

/* ==========================================================================
   v1.6.0 - Fluester-Zustand je Person
   ========================================================================== */

.edit-fluester {
  margin-top: 10px; padding-top: 8px;
  border-top: 1px solid var(--rand, rgba(255,255,255,.1));
}

/* Der Zustandssatz. Gruen heisst "Antworten kommen bei dir an", gelb
   heisst "sie landen woanders" - das ist der Unterschied, auf den es
   hier ankommt, deshalb steht er als Satz da und nicht als Symbol. */
.fl-lage {
  display: block; margin: 2px 0;
  padding: 6px 8px; border-radius: 5px;
  font-size: .85em; line-height: 1.4;
  border-left: 3px solid var(--rand, rgba(255,255,255,.2));
  background: var(--flaeche-2, rgba(255,255,255,.05));
}
.fl-lage.gut { border-left-color: #22c55e; }
.fl-lage.warn { border-left-color: #f59e0b; }

/* ==========================================================================
   v1.6.1 - Historie der ausgehenden Raids
   ========================================================================== */

.raid-historie { display: flex; flex-direction: column; gap: 2px; }

.rh-zeile {
  display: flex; align-items: center; gap: 10px;
  padding: 6px 8px; border-radius: 6px;
}
.rh-zeile:nth-child(odd) { background: var(--flaeche-2, rgba(255,255,255,.035)); }

/* Der Zaehler steht vorn und ist die eigentliche Aussage der Zeile -
   "wie oft" beantwortet die Frage schneller als jedes Datum. */
.rh-anzahl {
  min-width: 2.6em; text-align: right;
  font-variant-numeric: tabular-nums; font-weight: 700;
  color: var(--akzent, #8b5cf6);
}
.rh-name { font-weight: 600; }
.rh-zeit { margin-left: auto; font-size: .84em; color: var(--text-schwach, #8b93a7); }
.rh-zeile .rv-weg { margin-left: 6px; }

@media (max-width: 560px) {
  .rh-zeit { margin-left: 0; width: 100%; }
  .rh-zeile { flex-wrap: wrap; }
}

/* ==========================================================================
   v1.6.1 - Anmeldung per Twitch-Code (Device Flow)
   ========================================================================== */

.bot-anmeldung {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  margin: 10px 0; padding: 10px 12px;
  border-radius: 8px;
  background: var(--flaeche-2, rgba(255,255,255,.05));
  border: 1px solid var(--rand, rgba(255,255,255,.12));
}
.bot-anmeldung .hint { margin: 0; }

/* Laeuft die Anmeldung, ist der Code das Einzige, was zaehlt - deshalb
   untereinander statt nebeneinander, und der Code gross. */
.bot-anmeldung.laeuft { flex-direction: column; align-items: stretch; gap: 6px; }
.ba-schritt { font-size: .92em; }

.ba-code {
  align-self: center;
  margin: 4px 0; padding: 10px 20px;
  border-radius: 8px;
  background: var(--flaeche, rgba(0,0,0,.25));
  border: 2px dashed var(--akzent, #8b5cf6);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 1.9em; font-weight: 700; letter-spacing: .16em;
  cursor: pointer; user-select: all;
}
.ba-warte { font-size: .88em; color: var(--text-schwach, #8b93a7); text-align: center; }

/* ==========================================================================
   v1.6.3 - Meine Chat-Texte
   ========================================================================== */

/* Der graue Platzhalter IST der allgemeine Text - er muss lesbar bleiben,
   sonst weiss man nicht, was gilt, wenn man das Feld leer laesst. */
.mt-zeile { position: relative; }
.mt-zeile label {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .82em; opacity: .8;
}
.mt-zeile input::placeholder { opacity: .55; font-style: italic; }
.mt-zeile .rv-weg {
  position: absolute; right: 6px; bottom: 6px;
  opacity: .7;
}
.mt-zeile .rv-weg:hover { opacity: 1; }

/* ==========================================================================
   v1.6.4 - Einordnung: Kategorie und Prioritaet
   ========================================================================== */

/* Im Pruefen-Tab: eine ruhige Zeile ueber den Aktionsknoepfen. Sie soll
   NICHT nach Pflichtfeld aussehen - in aller Regel stimmt, was aus der
   Belohnung kommt, und man geht daran vorbei. */
.tw-einordnung {
  display: flex; flex-wrap: wrap; align-items: center; gap: 12px;
  margin: 10px 0 0; padding: 8px 10px;
  border-radius: 6px;
  background: var(--flaeche-2, rgba(255,255,255,.04));
}
.tw-einordnung .tw-eo-marke {
  font-size: .78em; text-transform: uppercase; letter-spacing: .04em;
  color: var(--text-schwach, #8b93a7);
}
.tw-einordnung label {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: .86em; margin: 0;
}
/* v1.6.5: Aufklappmenues sind so breit wie ihr laengster Eintrag - nicht
   breiter. Die Prio-Stufe ist "^^ Super", das braucht keine 8 Zeichen
   Reserve. Die Langfassung steht im aufgeklappten Menue und im Mouseover. */
.tw-einordnung select { width: auto; max-width: 12em; }
.tw-einordnung select[data-tw-prio] { max-width: 8.5em; }

/* In der Belohnungs-Tabelle: die Spalten sollen den Blick nicht
   auseinanderziehen. Gerade so breit, dass der GEWAEHLTE Wert lesbar
   bleibt - ein abgeschnittenes "Balla…" beantwortet die Frage nicht,
   die man hier stellt -, aber keinen Millimeter mehr. */
.bel-zuordnung select {
  width: auto; min-width: 0; max-width: 11em;
  font-size: .84em; padding: 2px 1.6em 2px 6px;
}
.bel-zuordnung .hint { margin: 0; font-size: .78em; }

/* Prio kommt mit deutlich weniger aus als die Kategorie: der laengste
   Eintrag ist "^^^ Sofort". */
.bel-zuordnung-eng select { max-width: 8.5em; }
.bel-tabelle .bel-zuordnung { white-space: nowrap; }

@media (max-width: 700px) {
  .tw-einordnung { gap: 8px; }
  .tw-einordnung label { width: 100%; }
  .tw-einordnung select { flex: 1; }
}

/* ==========================================================================
   v1.7.0 - Event Feed
   ==========================================================================
   Grundregel fuer diesen ganzen Block: kein Element breiter als sein Inhalt
   braucht. Eingabefelder bekommen eine Obergrenze in em, keine 100%-Breite -
   ein Feld fuer eine zweistellige Zahl sieht sonst aus wie ein Textfeld fuer
   einen Aufsatz.
   ========================================================================== */

.ev-karte { --ev-farbe: #f59e0b; }
.ev-karte h2 { border-left: 4px solid var(--ev-farbe); padding-left: 10px; }

.event-set-leiste { border-left: 3px solid var(--ev-farbe, #f59e0b); }
.set-leiste-info.warn { color: #fbbf24; }

/* --- Formular zum Eintragen --- */
.ev-form { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin: 10px 0; }
.ev-form input[name="titel"] { flex: 1 1 18em; min-width: 12em; max-width: 32em; }
.ev-form input[name="wer"] { flex: 0 0 auto; width: 10em; }

/* --- Die Liste --- */
.ev-liste { display: flex; flex-direction: column; gap: 4px; margin-top: 10px; }

.ev-zeile {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 10px; border-radius: 6px;
  background: var(--flaeche-2, rgba(255,255,255,.04));
  border-left: 3px solid var(--ev-farbe, #f59e0b);
}
.ev-zeile.ev-fertig { opacity: .55; border-left-color: var(--text-schwach, #8b93a7); }
.ev-zeile .drag-platz { width: 14px; flex: 0 0 auto; }
.ev-zeichen { font-size: 1.1em; flex: 0 0 auto; }
.ev-text { flex: 1 1 auto; min-width: 0; display: flex; flex-wrap: wrap; gap: 8px; align-items: baseline; }
.ev-eingabe { font-style: italic; color: var(--text-schwach, #8b93a7); }
.ev-notiz { font-size: .82em; color: var(--text-schwach, #8b93a7); }
.ev-wer { flex: 0 0 auto; font-size: .86em; opacity: .85; }
.ev-kosten {
  flex: 0 0 auto; font-size: .8em; padding: 1px 7px; border-radius: 10px;
  background: rgba(var(--accent-rgb, 139,92,246), .18);
}
.ev-zeit { flex: 0 0 auto; font-size: .8em; color: var(--text-schwach, #8b93a7); }
.ev-status { flex: 0 0 auto; font-size: .78em; padding: 1px 7px; border-radius: 10px; }
.ev-status.erledigt { background: rgba(34,197,94,.16); color: #4ade80; }
.ev-status.abgebrochen { background: rgba(239,68,68,.16); color: #f87171; }
.ev-akt { flex: 0 0 auto; display: flex; gap: 5px; }

.ev-fertig-block { margin-top: 12px; }

/* --- Die Rueckfrage nach dem Abbruch ---
   Auffaellig, aber nicht schreiend: hier geht es um echte Punkte, das soll
   man lesen, bevor man klickt. */
.ev-frage {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 10px 0; padding: 10px 12px; border-radius: 6px;
  background: rgba(251,191,36,.12); border: 1px solid rgba(251,191,36,.35);
}

/* --- Mini-Zeilen in Musikfeed und Notizen ---
   Absichtlich winzig: sie sollen daran erinnern, dass etwas da ist, nicht
   mit den Songs um Aufmerksamkeit streiten. */
.ev-mini-liste {
  display: flex; flex-wrap: wrap; gap: 5px;
  margin: 0 0 10px; padding: 6px 8px; border-radius: 6px;
  background: color-mix(in srgb, var(--ev-farbe, #f59e0b) 8%, transparent);
}
.ev-mini {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: .72em; line-height: 1.4;
  padding: 2px 7px; border-radius: 10px;
  border: 1px solid color-mix(in srgb, var(--ev-farbe, #f59e0b) 45%, transparent);
  color: var(--ev-farbe, #f59e0b);
  white-space: nowrap;
}
.ev-mini-zeit { opacity: .75; }
.ev-mini-wer { font-weight: 600; }

/* --- DJ-Anzeige --- */
.djview-events {
  margin-bottom: 12px; padding: 8px 10px; border-radius: 8px;
  border: 2px solid var(--ev-farbe, #f59e0b);
  background: color-mix(in srgb, var(--ev-farbe, #f59e0b) 10%, transparent);
}
.djview-events-kopf {
  font-size: .8em; text-transform: uppercase; letter-spacing: .06em;
  color: var(--ev-farbe, #f59e0b); margin-bottom: 6px;
}
.djview-event-row {
  /* v1.8.3: enger - sechs Meldungen fuellten vorher den halben Bildschirm */
  display: flex; align-items: center; gap: 8px; padding: 2px 0;
}
.djview-ev-zeichen { font-size: 1.3em; }
.djview-ev-text { flex: 1 1 auto; font-weight: 600; }
.djview-ev-eingabe { font-weight: 400; font-style: italic; opacity: .8; }
.djview-ev-wer, .djview-ev-zeit { opacity: .8; font-size: .9em; }

/* --- Hype Train --- */
.hype-chip { border-color: rgba(251,191,36,.5) !important; }
.hype-lok { font-size: 1.05em; }
.hype-stufe { opacity: .9; }
.hype-mini-balken {
  display: inline-block; width: 46px; height: 5px; border-radius: 3px;
  background: rgba(255,255,255,.18); overflow: hidden; vertical-align: middle;
}
.hype-mini-balken > span { display: block; height: 100%; background: #fbbf24; }

.hype-karte.laeuft h2 { color: #fbbf24; }
.hype-balken {
  height: 10px; border-radius: 5px; margin: 8px 0;
  background: rgba(255,255,255,.12); overflow: hidden;
}
.hype-balken > span { display: block; height: 100%; background: linear-gradient(90deg,#f59e0b,#fbbf24); }
.hype-zahlen { display: flex; gap: 16px; flex-wrap: wrap; font-size: .9em; }
.hype-spitze { margin-top: 6px; font-size: .82em; color: var(--text-schwach, #8b93a7); }

/* --- Einstellungen --- */
.ev-grund { display: flex; gap: 14px; flex-wrap: wrap; margin: 12px 0; }
.ev-feld { display: flex; flex-direction: column; gap: 4px; font-size: .86em; }
.ev-feld input { width: 16em; max-width: 100%; }
/* Ein Zeichen und eine zweistellige Sekundenzahl - mehr Feld waere
   nur Dekoration. */
.ev-feld.schmal input { width: 6em; }
#evSymbol { width: 4em; text-align: center; }
.ev-feld input[type="color"] { width: 3.5em; padding: 2px; }

.ev-schwellen { display: flex; gap: 18px; flex-wrap: wrap; margin: 8px 0 4px; }
.ev-schwelle { display: flex; flex-direction: column; gap: 3px; font-size: .86em; }
/* Zweistellige Zahl - mehr Breite waere reine Dekoration. */
.ev-schwelle input { width: 6em; }
.ev-schwelle .hint { font-size: .78em; margin: 0; }

.ev-textblock {
  margin: 10px 0; padding: 8px 10px; border-radius: 6px;
  background: var(--flaeche-2, rgba(255,255,255,.04));
}
.ev-textblock.aus { opacity: .55; }
.ev-textblock .hint { margin: 2px 0 6px; }
.ev-textblock input.ev-text { display: block; width: 100%; max-width: 46em; margin-bottom: 5px; }

.ev-set {
  margin: 8px 0; padding: 8px 10px; border-radius: 6px;
  background: var(--flaeche-2, rgba(255,255,255,.04));
}
.ev-set-kopf { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
/* flex-basis statt width: im Flex-Container wuerde die Breite sonst
   aufgezogen, bis das Feld quer ueber die Karte reicht. */
.ev-set-name { flex: 0 0 16em; width: 16em; font-weight: 600; }
.ev-set-aktiv {
  font-size: .74em; text-transform: uppercase; letter-spacing: .05em;
  padding: 1px 7px; border-radius: 10px;
  background: rgba(34,197,94,.18); color: #4ade80;
}
.ev-set-zeile { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 4px 0; }
.ev-set-marke {
  font-size: .74em; text-transform: uppercase; letter-spacing: .05em;
  color: var(--text-schwach, #8b93a7); min-width: 7em;
}
.checkbox-row.mini { font-size: .84em; margin: 0; gap: 4px; }
.ev-set-neu { display: flex; gap: 8px; margin-top: 10px; }
.ev-set-neu input { flex: 0 0 18em; width: 18em; }

.ev-fehlend { margin: 6px 0 0; padding-left: 20px; font-size: .88em; }
.ev-fehlend li { margin-bottom: 4px; }

@media (max-width: 700px) {
  .ev-zeile { flex-wrap: wrap; }
  .ev-text { flex: 1 1 100%; }
  .ev-set-marke { min-width: 100%; }
}

/* v1.7.1 - Auswahl der Seitenbreite (Einstellungen -> Ansichten) */
.breite-wahl { display: flex; gap: 6px; flex-wrap: wrap; }

/* v1.7.2 - App-Name in den Einstellungen */
.appname-zeile { display: flex; gap: 8px; align-items: center; }
.appname-zeile input { flex: 0 0 18em; width: 18em; }

/* v1.7.2 - Testalarm sieht bewusst anders aus als ein echter.
   Eine geprobte Alarmierung, die wie eine echte aussieht, waere schlimmer
   als gar keine. */
.raid-alarm-chip.ist-test {
  border-style: dashed !important;
  opacity: .9;
}
.raid-alarm-chip.ist-test .raid-alarm-leuchte { animation: none; opacity: .6; }

/* ==========================================================================
   v1.7.4 - Verbindungszustand der Medienerkennung
   ==========================================================================
   Ein Punkt je hinterlegtem Play-Rechner: gruen = meldet sich im Takt,
   grau = still. Damit sieht man bei mehreren Rechnern auf einen Blick,
   WELCHER das Problem ist - vorher stand da nur ein Sammelname.
   ========================================================================== */
.medien-jetzt.getrennt .mj-text { opacity: .8; }

.mj-quellen { display: inline-flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.mj-quelle {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: .74em; padding: 1px 8px 1px 6px;
  border-radius: 999px; border: 1px solid var(--border);
  white-space: nowrap;
}
.mj-punkt {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--text-schwach, #8b93a7); flex: 0 0 auto;
}
.mj-quelle.an { border-color: rgba(34,197,94,.45); }
.mj-quelle.an .mj-punkt {
  background: #22c55e;
  /* Ruhiges Pulsieren - es meldet sich ja gerade etwas. */
  animation: mjPuls 2.4s ease-in-out infinite;
}
.mj-quelle.aus { opacity: .65; }

@keyframes mjPuls {
  0%, 100% { box-shadow: 0 0 0 0 rgba(34,197,94,.5); }
  50%      { box-shadow: 0 0 0 4px rgba(34,197,94,0); }
}

/* v1.7.10 - Testzeile im Event-Feed-Einstellungsbereich.
   Mengenfelder so schmal wie ihr Inhalt: drei- bis fuenfstellige Zahl. */
.ev-testzeile { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ev-testzeile input { width: 6em; }

/* ==========================================================================
   v1.8.0 - Support-Verlauf, Chips, DJ-Laufband
   ==========================================================================
   Grundregel fuer die Chips: das Symbol fuellt sie fast aus. Links nur 2 px
   Rand, damit das Zeichen gross wirkt, ohne die ZEILE hoeher zu machen -
   genau das war der Wunsch.
   ========================================================================== */

.sup-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 2px 9px 2px 2px;
  border-radius: 999px; flex: 0 0 auto;
  font-weight: 700; font-variant-numeric: tabular-nums;
  font-size: .9em; line-height: 1;
  border: 1px solid currentColor;
  background: color-mix(in srgb, currentColor 13%, transparent);
}
.sup-chip .sup-sym {
  width: 21px; height: 21px; border-radius: 999px;
  display: grid; place-items: center; flex: 0 0 auto;
}
.sup-chip .sup-sym svg,
.sup-chip .sup-sym img { width: 19px; height: 19px; display: block; object-fit: contain; }
.sup-chip .sup-wert { color: var(--text, #e7e9ef); }

.sup-chip.sup-gross { font-size: 1.02em; padding: 3px 11px 3px 3px; }
.sup-chip.sup-gross .sup-sym { width: 26px; height: 26px; }
.sup-chip.sup-gross .sup-sym svg,
.sup-chip.sup-gross .sup-sym img { width: 24px; height: 24px; }
.sup-chip.sup-klein { font-size: .76em; padding: 1px 7px 1px 1px; }
.sup-chip.sup-klein .sup-sym { width: 16px; height: 16px; }
.sup-chip.sup-klein .sup-sym svg,
.sup-chip.sup-klein .sup-sym img { width: 14px; height: 14px; }

/* Farben je Art - ueber Variablen, damit sie im Design-Bereich
   umstellbar sind (global oder je Ansicht wie die uebrigen Farben). */
:root {
  --sup-gift: #b47cff;
  --sup-sub: #e0566b;   /* v1.8.8: eigene Abos roetlich - per Farbwaehler weiter frei einstellbar */
  --sup-bits: #29d3f0;
  --sup-donation: #4ade80;
}
.sup-gift, .sup-art-gift .sup-mini-sym, .sup-art-gift .dj-sup-menge { color: var(--sup-gift); }
.sup-sub, .sup-art-sub .sup-mini-sym, .sup-art-sub .dj-sup-menge { color: var(--sup-sub); }
.sup-bits, .sup-art-bits .sup-mini-sym, .sup-art-bits .dj-sup-menge { color: var(--sup-bits); }
.sup-donation, .sup-art-donation .sup-mini-sym, .sup-art-donation .dj-sup-menge { color: var(--sup-donation); }

/* --- Verlaufszeilen --- */
.sup-liste { display: flex; flex-direction: column; gap: 4px; margin-top: 8px; }
.sup-zeile {
  display: flex; align-items: center; gap: 9px;
  padding: 6px 10px; border-radius: 6px;
  background: var(--flaeche-2, rgba(255,255,255,.04));
  border-left: 3px solid var(--border);
}
.sup-zeile[data-verlauf-auf] { cursor: pointer; }
.sup-zeile[data-verlauf-auf]:hover { background: rgba(255,255,255,.07); }
.sup-art-gift { border-left-color: var(--sup-gift); }
.sup-art-sub { border-left-color: var(--sup-sub); }
.sup-art-bits { border-left-color: var(--sup-bits); }
.sup-art-donation { border-left-color: var(--sup-donation); }
.sup-alt { opacity: .45; }

.sup-pfeil { color: var(--text-schwach, #8b93a7); flex: 0 0 auto; width: 10px; font-size: .8em; }
.sup-name { font-weight: 700; flex: 0 0 auto; }
.sup-davor, .sup-gesamt {
  flex: 0 0 auto; font-size: .78em; color: var(--text-schwach, #8b93a7);
  font-variant-numeric: tabular-nums;
}
.sup-davor { padding: 1px 8px; border-radius: 999px; border: 1px solid var(--border); }
.sup-davor b { color: var(--text-dim, #a8b0c2); font-weight: 600; }

.sup-auf { margin: 4px 0 4px 36px; border-left: 2px solid var(--border); padding-left: 12px; }
.sup-detail { display: flex; align-items: center; gap: 8px; padding: 2px 0; font-size: .84em; color: var(--text-schwach, #8b93a7); }
.sup-hype svg { width: 15px; height: 15px; vertical-align: -3px; color: var(--ev-farbe, #f59e0b); }

.sup-trenner {
  display: flex; align-items: center; gap: 10px; margin: 14px 0 4px;
  font-size: .7em; text-transform: uppercase; letter-spacing: .09em;
  color: var(--text-schwach, #8b93a7);
}
.sup-trenner::after { content: ''; flex: 1 1 auto; height: 1px; background: var(--border); }

/* --- Mini-Zeilen mit Menge --- */
.sup-mini { border-color: currentColor !important; }
.sup-mini-sym { display: inline-flex; width: 15px; height: 15px; flex: 0 0 auto; }
.sup-mini-sym svg, .sup-mini-sym img { width: 100%; height: 100%; }
.sup-mini-menge { font-weight: 700; font-variant-numeric: tabular-nums; }
.ev-mini-aufgabe { font-weight: 600; }

/* --- DJ-Ansicht --- */
.djview-support {
  margin-bottom: 8px; padding: 6px 10px; border-radius: 8px;
  border: 2px solid var(--ev-farbe, #f59e0b);
  background: color-mix(in srgb, var(--ev-farbe, #f59e0b) 9%, transparent);
}
.dj-band { overflow: hidden; }
.dj-bahn { display: flex; gap: 8px; }
.dj-band.rollt .dj-bahn { animation: djRollen 26s linear infinite; }
.dj-band.rollt:hover .dj-bahn { animation-play-state: paused; }
@keyframes djRollen { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) { .dj-band.rollt .dj-bahn { animation: none; } }

.dj-sup {
  display: inline-flex; align-items: center; gap: 6px; white-space: nowrap;
  padding: 3px 11px 3px 3px; border-radius: 999px; flex: 0 0 auto;
  border: 1px solid currentColor;
  background: color-mix(in srgb, currentColor 11%, transparent);
  font-size: .92em;
}
.dj-sup .sup-mini-sym { width: 19px; height: 19px; }
.dj-sup-name { font-weight: 700; color: var(--text); }
.dj-sup-menge { font-weight: 800; font-variant-numeric: tabular-nums; }
.dj-sup-mehr { font-size: .78em; color: var(--text-schwach, #8b93a7); }

.djview-aufgaben { margin-top: 4px; }
.djview-aufgaben .djview-event-row { padding: 2px 0; }

/* --- v1.8.5: Zickzack-Zeitleiste in der DJ-Anzeige ---
   Jede Kachel hat ihre EIGENE Zeile: Abos ruecken nach links, Bits und
   Donations nach rechts, oben nach unten ist die Ankunft. Der Versatz
   selbst sagt, was es war - der alte Zweispalten-Raster liess stattdessen
   Loecher, wo die andere Seite dran war (Vorlage: Saschas Skizze). Die
   blasse Mittellinie gibt dem Zickzack den Halt. */
.djev-zeitleiste {
  display: flex; flex-direction: column; gap: 2px; margin-top: 4px;
  position: relative;
}
.djev-zeitleiste::before {
  content: ''; position: absolute; left: 50%; top: 2px; bottom: 2px; width: 1px;
  background: color-mix(in srgb, var(--ev-farbe, #f59e0b) 22%, transparent);
}
.djev-kachel { width: calc(50% - 10px); }
.djev-kachel.links { align-self: flex-start; }
.djev-kachel.rechts { align-self: flex-end; }
@media (max-width: 700px) {
  .djev-zeitleiste::before { display: none; }
  .djev-kachel { width: 100%; }
}
.djev-kachel {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px; min-width: 0;
  padding: 2px 8px 2px 4px; border-radius: 8px;
  background: rgba(255,255,255,.04);
  border-left: 3px solid transparent;   /* Farbe setzt sup-art-* */
  line-height: 1.35;
}
.djev-sym { flex: 0 0 auto; display: grid; place-items: center; width: 21px; height: 21px; }
.djev-sym svg, .djev-sym img { width: 19px; height: 19px; display: block; object-fit: contain; }
.djev-oben { flex: 1 1 auto; min-width: 0; font-size: .95em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.djev-oben b { font-weight: 700; }
.djev-zeit { flex: 0 0 auto; font-size: .74em; opacity: .65; font-variant-numeric: tabular-nums; }
.djev-zusatz { font-size: .8em; opacity: .75; }

/* Gruppen-Kacheln - aufklappbar, mit Danke-Haken */
.djev-kachel.hat-mehr { cursor: pointer; }
.djev-anzahl { font-size: .8em; font-weight: 700; opacity: .7; }
.djev-caret { flex: 0 0 auto; font-size: .78em; opacity: .45; }
.djev-historie { flex-basis: 100%; display: flex; flex-direction: column; gap: 1px; margin: 1px 0 2px 27px;
  border-left: 2px solid color-mix(in srgb, currentColor 35%, transparent); padding-left: 8px; }
.djev-hist-zeile { display: flex; align-items: center; gap: 6px;
  font-size: .8em; opacity: .68; font-variant-numeric: tabular-nums;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  padding: 1px 4px; border-radius: 5px; }
.djev-hist-zeile i { opacity: .8; }
/* v1.8.6: DIESE Gabe - deutlich hervorgehoben, aber nur ueber Gewicht,
   Groesse und currentColor-Anteile, damit es in Hell, Dunkel und Farblos
   gleichermassen traegt. */
.djev-hist-zeile.jetzt {
  opacity: 1; font-weight: 800; font-size: .88em;
  background: color-mix(in srgb, currentColor 14%, transparent);
  outline: 1px solid color-mix(in srgb, currentColor 40%, transparent);
}
.djev-hist-nr { flex: 0 0 auto; min-width: 22px; opacity: .6; font-weight: 600; }
.djev-hist-sym { flex: 0 0 auto; display: grid; place-items: center; width: 15px; height: 15px; }
.djev-hist-sym svg, .djev-hist-sym img { width: 14px; height: 14px; display: block; object-fit: contain; }
.djev-hist-marke {
  font-size: .82em; font-weight: 800; text-transform: uppercase; letter-spacing: .05em;
  padding: 0 5px; border-radius: 4px;
  background: color-mix(in srgb, currentColor 22%, transparent);
}
.djev-ev-marke { flex: 0 0 auto; font-size: .82em; opacity: .8; line-height: 1; }
.djev-lauf { font-size: .8em; opacity: .7; font-variant-numeric: tabular-nums; }
.djev-lauf b { opacity: 1; font-weight: 800; }
.djev-danke { flex: 0 0 auto; font-size: 1em; }
.djev-danke:hover, .djev-danke:focus-visible { color: #34d399; background: rgba(52,211,153,.14); opacity: 1; }
.djev-rest { font-size: .78em; opacity: .55; padding: 1px 4px; align-self: center; }

/* --- v1.8.7: zwei kompakte Spalten mit Positionsnummern und Mini-Spacern --- */
.djev-spalten { display: grid; grid-template-columns: 1fr 1fr; gap: 0 10px; margin-top: 4px; align-items: start; }
.djev-spalte { display: flex; flex-direction: column; gap: 2px; min-width: 0;
  max-height: 42vh; overflow-y: auto; scrollbar-width: thin; }
@media (max-width: 700px) { .djev-spalten { grid-template-columns: 1fr; } }
.djev-spalte .djev-kachel { width: auto; }
/* v1.8.8: Position gross und in Blockfarbe - so praesent wie das Symbol */
.djev-pos { flex: 0 0 auto; font-size: 1.02em; font-weight: 800; opacity: .95;
  min-width: 34px; text-align: right; font-variant-numeric: tabular-nums; }
/* Richtungspfeil an der Auslassung: dicker, in der Farbe des gemeinten
   Blocks; links zeigt er nach rechts, rechts nach links */
.djev-pfeil { display: block; text-align: center; font-size: .95em; line-height: 1;
  font-weight: 800; user-select: none; opacity: .9;
  text-shadow: 0 0 6px color-mix(in srgb, currentColor 45%, transparent); }
.djev-pfeil.nach-links { transform: scaleX(-1); }

/* Ablaufbalken der automatischen Ausblendung - laeuft am unteren Rand der
   Kachel von voll auf leer (currentColor, funktioniert in jedem Modus) */
.djev-kachel { position: relative; overflow: hidden; }
.djev-ablauf { position: absolute; left: 0; bottom: 0; height: 2px; width: 100%;
  background: color-mix(in srgb, currentColor 55%, transparent);
  transform-origin: left; animation: djevAblauf linear forwards; pointer-events: none; }
@keyframes djevAblauf { from { transform: scaleX(1); } to { transform: scaleX(0); } }
@media (prefers-reduced-motion: reduce) { .djev-ablauf { animation: none; opacity: .3; } }

/* --- v1.8.7: LIVE-Chip (Kopfzeile + DJ-Topbar) --- */
.live-chip { display: inline-flex; align-items: center; gap: 5px;
  font-size: .74em; font-weight: 800; letter-spacing: .06em;
  padding: 1px 8px; border-radius: 999px;
  color: #fff; background: #e91916;
  box-shadow: 0 0 10px rgba(233,25,22,.55); }
.live-punkt { width: 7px; height: 7px; border-radius: 50%; background: #fff;
  animation: livePuls 1.6s ease-in-out infinite; }
@keyframes livePuls { 0%,100% { opacity: 1; } 50% { opacity: .35; } }
@media (prefers-reduced-motion: reduce) { .live-punkt { animation: none; } }
.live-dauer { font-weight: 600; opacity: .9; font-variant-numeric: tabular-nums; }

/* --- v1.8.7: Sendungs-Chronik --- */
.chronik-kasten { background: var(--card, #17151f); border: 2px solid var(--ev-farbe, #f59e0b);
  border-radius: 14px; width: min(680px, 94vw); max-height: 82vh;
  display: flex; flex-direction: column; box-shadow: 0 24px 70px rgba(0,0,0,.5); }
.chronik-kopf { display: flex; align-items: center; gap: 10px; padding: 12px 14px;
  border-bottom: 1px solid rgba(128,128,128,.25); }
.chronik-liste { overflow-y: auto; padding: 8px 14px 12px; display: flex; flex-direction: column; gap: 2px; }
.chronik-zeile { display: flex; align-items: center; gap: 8px; padding: 2px 8px;
  border-radius: 7px; border-left: 3px solid transparent; background: rgba(128,128,128,.07);
  font-size: .92em; }
.chronik-zeile.fertig { opacity: .5; }
.chronik-pos { min-width: 30px; text-align: right; font-size: .78em; opacity: .55;
  font-weight: 700; font-variant-numeric: tabular-nums; }
.chronik-zeit { margin-left: auto; font-size: .8em; opacity: .65; font-variant-numeric: tabular-nums; }
.chronik-stand { min-width: 16px; text-align: center; font-weight: 800; }
.chronik-zeile.fertig .chronik-stand { color: #34d399; }

/* --- v1.8.7: Verlaufs-Karte scrollbar --- */
.sup-rollbereich { max-height: 56vh; overflow-y: auto; scrollbar-width: thin; }

/* v1.8.8: EIGENE Abos deutlich abgesetzt von Gift-Subs - voller Rahmen und
   kraeftiger Grundton in der Sub-Farbe (Groesse bleibt gleich, wie gewuenscht) */
.djev-kachel.eigenes-abo {
  border: 1px solid var(--sup-sub);
  border-left-width: 3px;
  background: color-mix(in srgb, var(--sup-sub) 13%, transparent);
}
.djev-prime { color: #a970ff; font-size: 1.05em; line-height: 1;
  text-shadow: 0 0 7px rgba(169,112,255,.6); }
.djev-kachel.prime { border-color: #a970ff; }

/* v1.8.8: pulsierender Event-Chip im Blockkopf - solange etwas unbestaetigt ist */
.djev-puls-chip {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 1.18em; font-weight: 800; line-height: 1;
  padding: 2px 9px; border-radius: 999px;
  border: 1px solid var(--ev-farbe, #f59e0b);
  background: color-mix(in srgb, var(--ev-farbe, #f59e0b) 16%, transparent);
  animation: djevPuls 1.8s ease-in-out infinite;
}
@keyframes djevPuls {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--ev-farbe, #f59e0b) 45%, transparent); }
  50% { box-shadow: 0 0 10px 3px color-mix(in srgb, var(--ev-farbe, #f59e0b) 30%, transparent); }
}
@media (prefers-reduced-motion: reduce) { .djev-puls-chip { animation: none; } }

/* --- v1.8.9 --- */
/* Set-Toggles: gruen = zaehlt, gedaempft roetlich = aus */
.set-toggle.an {
  background: rgba(34,197,94,.16) !important;
  border-color: rgba(34,197,94,.55) !important;
  color: #4ade80 !important; font-weight: 700;
}
.set-toggle.aus {
  background: rgba(229,72,77,.07) !important;
  border-color: rgba(229,72,77,.22) !important;
  color: color-mix(in srgb, #e5484d 45%, var(--text-schwach, #8b93a7)) !important;
}
.set-toggle.aus:hover { border-color: rgba(34,197,94,.45) !important; color: #4ade80 !important; }

/* Kacheln ohne gezuendetes Event: deutlich entsaettigt - die farbigen
   Positionsnummern und Pfeile bleiben davon unberuehrt (Navigation!) */
.djev-kachel.blass > *:not(.djev-pos):not(.djev-ablauf) { filter: saturate(.25); opacity: .82; }
.djev-kachel.ausgeloest { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ev-farbe, #f59e0b) 35%, transparent); }

/* neutraler Puls-Chip (Support ohne Events) - gleiche Mechanik, eigene Farbe */
.djev-puls-chip.neutral { border-color: var(--sup-gift); background: color-mix(in srgb, var(--sup-gift) 14%, transparent); }
.djev-puls-chip .sup-mini-sym, .djev-puls-chip svg, .djev-puls-chip img { width: 17px; height: 17px; }

/* Laufband: Lok markiert den Schleifenanfang, Luecke vor der Wiederholung */
.dj-runde { display: inline-flex; gap: 8px; align-items: center; padding-right: 40px; }
.dj-lok { font-size: 1.05em; opacity: .8; }

/* Seitenleisten-Chips: Speichern/Verwerfen am aktiven Menuepunkt */
.sl-chips { display: inline-flex; gap: 4px; margin-left: auto; }
.sl-chip {
  display: grid; place-items: center; width: 26px; height: 24px;
  border-radius: 7px; border: 1px solid transparent; cursor: pointer;
  font-size: .85em; line-height: 1; background: transparent; padding: 0;
}
.sl-chip.gruen { background: rgba(34,197,94,.18); border-color: rgba(34,197,94,.5); }
.sl-chip.gruen:hover { background: rgba(34,197,94,.32); }
.sl-chip.rot { background: rgba(229,72,77,.14); border-color: rgba(229,72,77,.4); color: #ff6b6f; font-weight: 700; }
.sl-chip.rot:hover { background: rgba(229,72,77,.28); }

/* ===== v1.9.0: Raid – Kanalsuche, Kommentare, Raid-Texte, Chat-Vorschau ===== */
.rv-suchfeld { position: relative; flex: 1 1 200px; min-width: 0; }
.rv-suchfeld input { width: 100%; }
.rv-treffer {
  position: absolute; z-index: 40; left: 0; right: 0; top: calc(100% + 4px);
  background: var(--card, #17151f); border: 1px solid var(--border, rgba(255,255,255,.16));
  border-radius: 10px; box-shadow: 0 14px 40px rgba(0,0,0,.5); overflow: hidden; max-height: 300px; overflow-y: auto;
}
.rv-treffer-zeile {
  display: flex; align-items: center; gap: 9px; width: 100%; padding: 6px 10px;
  background: none; border: 0; color: inherit; cursor: pointer; text-align: left; font-size: .95em;
}
.rv-treffer-zeile:hover { background: rgba(255,255,255,.07); }
.rv-treffer-zeile img, .rv-treffer-leer { width: 28px; height: 28px; border-radius: 50%; flex: 0 0 auto; background: rgba(255,255,255,.08); }
.rv-treffer-name { font-weight: 700; }
.rv-treffer-spiel { margin-left: auto; font-size: .82em; opacity: .6; white-space: nowrap; }
.live-chip.mini { font-size: .62em; padding: 0 6px; }
.rv-kommentar { font-size: .85em; opacity: .75; font-style: italic; }

/* Raid-Texte */
.rt-kopf { display: flex; gap: 10px; align-items: center; margin-bottom: 10px; flex-wrap: wrap; }
.rt-reiter-leiste { display: inline-flex; gap: 4px; }
.rt-reiter {
  width: 30px; height: 30px; border-radius: 8px; cursor: pointer;
  border: 1px solid var(--border, rgba(255,255,255,.18)); background: rgba(255,255,255,.05);
  color: inherit; font-weight: 700;
}
.rt-reiter.aktiv { background: var(--acc, #a970ff); border-color: var(--acc, #a970ff); color: #fff; }
.rt-reiter.leer { opacity: .45; }
.rt-kopf input { flex: 1 1 180px; min-width: 0; }
.rt-felder { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
@media (max-width: 760px) { .rt-felder { grid-template-columns: 1fr; } }
.rt-feld { display: flex; flex-direction: column; gap: 4px; font-size: .9em; }
.rt-feld textarea { width: 100%; resize: vertical; font-family: inherit; }

.rt-emotes { margin-top: 12px; border-top: 1px solid rgba(128,128,128,.2); padding-top: 10px; }
.rt-emote-kopf { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.rt-emote-kopf input { flex: 1 1 160px; min-width: 0; }
.rt-emote-reiter { display: inline-flex; gap: 4px; }
.rt-emote-reiter .aktiv { background: var(--acc, #a970ff) !important; color: #fff !important; border-color: var(--acc, #a970ff) !important; }
.rt-emote-gitter {
  display: flex; flex-wrap: wrap; gap: 4px; margin-top: 8px;
  max-height: 132px; overflow-y: auto; padding: 4px; border-radius: 8px; background: rgba(255,255,255,.03);
}
.rt-emote { width: 34px; height: 34px; display: grid; place-items: center; padding: 2px;
  border: 1px solid transparent; border-radius: 7px; background: none; cursor: pointer; }
.rt-emote:hover { background: rgba(255,255,255,.1); border-color: var(--acc, #a970ff); }
.rt-emote img { max-width: 28px; max-height: 28px; }

.rt-hilfe { margin: 12px 0 4px; }
.rt-hilfe > summary { cursor: pointer; font-size: .86em; opacity: .7; padding: 4px 0; }
.rt-hilfe-text { font-size: .86em; opacity: .8; padding: 6px 0 2px; display: flex; flex-direction: column; gap: 6px; }
.rt-hilfe-text p { margin: 0; }
.rt-emotes > summary { cursor: pointer; font-size: .9em; font-weight: 600; padding: 6px 0; }
.rt-vorschau-block { margin-top: 16px; max-width: 460px; }
.rt-marke { font-size: .74em; text-transform: uppercase; letter-spacing: .07em; opacity: .55; }
.rt-fuss { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 12px; }
.rt-sprachwahl { display: inline-flex; gap: 4px; margin-left: auto; }

/* Der Twitch-Chat-Nachbau */
.tw-chat {
  margin-top: 6px; border-radius: 8px; overflow: hidden;
  background: #18181b; border: 1px solid #2f2f35; color: #efeff1;
  font-size: 13px; line-height: 1.5;
}
/* v1.10.0: EIN Textfluss statt Flexbox-Zeilen - der Text laeuft direkt
   hinter dem Doppelpunkt weiter und bricht darunter auf volle Breite um,
   genau wie im echten Twitch-Chat. */
.tw-chat-zeile { padding: 8px 10px; word-wrap: break-word; }
.tw-zeit { color: #adadb8; font-size: 11px; margin-right: 5px; }
.tw-badge, .tw-badge.leer {
  width: 18px; height: 18px; border-radius: 3px; background: #9147ff;
  display: inline-block; vertical-align: -4px; margin-right: 4px;
}
.tw-name { color: #bf94ff; font-weight: 700; }
.tw-doppel { color: #efeff1; margin-right: 3px; }
.tw-emote { height: 26px; vertical-align: middle; margin: -3px 1px; }
.tw-chat-leer { padding: 22px 10px; text-align: center; color: #adadb8; font-size: 12px; font-style: italic; }
.tw-eingabe {
  display: flex; align-items: center; gap: 8px; margin: 8px; padding: 8px 10px;
  border: 2px solid #3a3a3d; border-radius: 6px; background: #18181b; color: #adadb8;
}
.tw-eingabe-bild, .tw-eingabe-bild.leer { width: 20px; height: 20px; border-radius: 4px; background: #9147ff; flex: 0 0 auto; }
.tw-eingabe-text { flex: 1 1 auto; }
.tw-eingabe-zahnrad { opacity: .65; font-size: 15px; line-height: 1; }
.tw-chat-knopf { background: #9147ff; color: #fff; border-radius: 5px; padding: 3px 12px; font-weight: 700; font-size: 12px; }

/* Modus-Auswahl bei den eigenen Chat-Texten */
.mt-modus { margin: 14px 0; }

/* ===== v1.10.0: Luft im ganzen UI =====
   Wiederkehrender Befund: Hilfetexte klebten direkt unter Feldern, Knoepfe
   stiessen an Ueberschriften. Diese Regeln setzen einen einheitlichen
   Mindestabstand, ohne bestehende Layouts umzuwerfen. */
.hint { margin-top: 6px; margin-bottom: 0; line-height: 1.45; }
.field .hint, label + .hint, input + .hint, select + .hint, textarea + .hint { margin-top: 7px; }
.field { margin-bottom: 14px; }
.field > label { display: block; margin-bottom: 5px; }
.card h2 + .hint, .card h3 + .hint { margin-top: 8px; }
.card h3 { margin-top: 22px; margin-bottom: 8px; }
h4 + .hint { margin-top: 6px; }
.checkbox-row { display: flex; align-items: center; gap: 8px; }
.checkbox-row + .hint { margin-top: 6px; margin-bottom: 10px; }
.zd-aktionen, .rv-verwaltung, .hv-knoepfe, .ev-testzeile { gap: 8px; margin-top: 12px; }
.settings-page-body > *:first-child { margin-top: 4px; }
.settings-page-body { padding-top: 4px; }
textarea { padding: 8px 10px; line-height: 1.45; }

/* Farbwaehler in "Eigene Namen und Zeichen" */
.art-zeile input[type="color"] { width: 34px; height: 30px; padding: 2px; border-radius: 7px;
  border: 1px solid rgba(128,128,128,.35); background: transparent; cursor: pointer; flex: 0 0 auto; }

/* Kopfzeile des Blocks mit den sehr dezenten Knoepfen */
.djev-kopfzeile { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.djev-kopfzeile .djview-events-kopf { margin-bottom: 0; }
.djev-zahl { opacity: .7; font-weight: 700; margin-left: 6px; }
.djev-knoepfe { display: inline-flex; gap: 2px; }
.djev-geist {
  background: none; border: 0; padding: 0 5px; border-radius: 6px;
  color: inherit; opacity: .32; font-size: .9em; line-height: 1.6; cursor: pointer;
  transition: opacity .12s, background .12s;
}
.djev-geist:hover, .djev-geist:focus-visible { opacity: .85; background: rgba(255,255,255,.08); }
.djev-geist:focus-visible { outline: 1px solid var(--acc, #a970ff); }
.djev-zurueck { font-size: 1em; opacity: .38; }
.djview-support.zu { padding: 4px 10px; }

/* die kleinen Zoom-Schritte neben dem Fader */
.djview-scale-knopf {
  font-size: 1.05em; font-weight: 700; line-height: 1.2; padding: 0 6px;
}

/* --- v1.8.0: Hype-Train-Einblendung auf allen Seiten --- */
.hype-einblend {
  position: fixed; top: 50%; left: 50%; z-index: 3000;
  display: flex; align-items: center; gap: 16px;
  padding: 16px 26px 16px 16px; border-radius: 18px;
  background: rgba(20,23,30,.95);
  border: 2px solid var(--ev-farbe, #f59e0b);
  box-shadow: 0 22px 60px rgba(0,0,0,.55);
  pointer-events: none;
  animation: hypeRein var(--einblend-sek, 5s) cubic-bezier(.2,.85,.3,1) forwards;
}
.hype-einblend.von-rechts { animation-name: hypeReinRechts; }
@keyframes hypeRein {
  0%   { opacity: 0; transform: translate(-190%, -50%) scale(.9); }
  9%   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  90%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(.95); }
}
@keyframes hypeReinRechts {
  0%   { opacity: 0; transform: translate(90%, -50%) scale(.9); }
  9%   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  90%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(.95); }
}
.hype-bild {
  width: 104px; height: 104px; border-radius: 16px; flex: 0 0 auto;
  display: grid; place-items: center;
  color: var(--ev-farbe, #f59e0b);
  background: color-mix(in srgb, var(--ev-farbe, #f59e0b) 15%, transparent);
  /* Dieselbe Bewegung wie beim Raid-Alarm - der Kanal kennt sie schon. */
  animation: hypePulsen 1.2s ease-in-out infinite;
}
.hype-bild svg { width: 68px; height: 68px; }
.hype-bild img { width: 100%; height: 100%; object-fit: contain; border-radius: 16px; }
@keyframes hypePulsen {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--ev-farbe, #f59e0b) 55%, transparent); }
  50%      { box-shadow: 0 0 0 16px color-mix(in srgb, var(--ev-farbe, #f59e0b) 0%, transparent); }
}
.hype-worte b { display: block; font-size: 22px; color: var(--ev-farbe, #f59e0b); }
.hype-worte span { color: var(--text-schwach, #8b93a7); font-size: 14px; }

@media (prefers-reduced-motion: reduce) {
  .hype-bild { animation: none; }
  .hype-einblend { animation-duration: .01s; opacity: 1; transform: translate(-50%, -50%); }
}
@media (max-width: 560px) {
  .hype-einblend { gap: 12px; padding: 12px 18px 12px 12px; }
  .hype-bild { width: 74px; height: 74px; }
  .hype-bild svg { width: 48px; height: 48px; }
  .hype-worte b { font-size: 17px; }
}
.sup-auch { font-size: .74em; color: var(--text-schwach, #8b93a7); flex: 0 0 auto; }

/* --- v1.8.1: Die pochende Rückfrage (eine für alle) --- */
.abfrage-schleier {
  position: fixed; inset: 0; z-index: 4000;
  display: flex; align-items: flex-start; justify-content: center;
  padding-top: 16vh;
  background: rgba(6,8,14,.66);
  backdrop-filter: blur(3px);
  animation: abfrageAuf .13s ease-out;
}
@keyframes abfrageAuf { from { opacity: 0; } to { opacity: 1; } }
.abfrage-kasten {
  max-width: 440px; width: calc(100vw - 32px);
  background: var(--bg-card, #181b23);
  border: 2px solid #ef4444; border-radius: 12px;
  padding: 18px 20px;
  animation: abfragePochen 1.9s ease-in-out infinite;
}
@keyframes abfragePochen {
  0%,100% { box-shadow: 0 0 0 3px rgba(239,68,68,.10), 0 18px 46px rgba(0,0,0,.5); }
  50%     { box-shadow: 0 0 0 9px rgba(239,68,68,.22), 0 18px 46px rgba(0,0,0,.5); }
}
@media (prefers-reduced-motion: reduce) { .abfrage-kasten { animation: none; } }
.abfrage-kasten h4 { margin: 0 0 .45rem; font-size: 1.06rem; color: #ff9a9a; }
.abfrage-kasten p { font-size: .88rem; color: var(--text-schwach, #8b93a7); margin: 0 0 1rem; }
.abfrage-reihe { display: flex; gap: 8px; }

/* --- v1.8.1: Hype-Train-Verlauf --- */
/* v1.10.0: Das Zug-Zeichen klein und im Theme statt als weisse Riesenflaeche */
.hv-sym { display: inline-grid; place-items: center; width: 22px; height: 22px; vertical-align: -4px; }
.hv-sym svg, .hv-sym img { width: 20px; height: 20px; opacity: .82; }
.hv-sym svg { fill: currentColor; color: var(--sup-sub, #e0566b); }

.hv-liste { display: flex; flex-direction: column; gap: 3px; margin-top: 10px; }
.hv-zeile {
  display: flex; align-items: center; gap: 9px; padding: 6px 10px;
  border-radius: 6px; background: var(--flaeche-2, rgba(255,255,255,.04));
  border-left: 3px solid var(--ev-farbe, #f59e0b);
}
.hv-zeile.gewaehlt { background: rgba(239,68,68,.10); border-left-color: #ef4444; }
.hv-haken { display: flex; align-items: center; margin: 0; flex: 0 0 auto; }
.hv-stufe { flex: 0 0 auto; }
.hv-knoepfe { display: flex; gap: 8px; margin-top: 11px; }

/* --- v1.8.1: Sendungs-Bilanz --- */
.bil-block { padding: 9px 0; border-top: 1px solid var(--border); }
.bil-block:first-of-type { border-top: 0; padding-top: 0; }
.bil-kopf {
  display: flex; align-items: center; gap: 8px; margin-bottom: 6px;
  font-size: .72em; text-transform: uppercase; letter-spacing: .07em;
  color: var(--text-schwach, #8b93a7);
}
.bil-kopf svg { width: 15px; height: 15px; color: var(--ev-farbe, #f59e0b); }
.bil-namen { display: flex; flex-wrap: wrap; gap: 5px; }
.bil-namen .nam {
  font-size: .84em; padding: 2px 9px; border-radius: 999px;
  background: var(--flaeche-2, rgba(255,255,255,.04));
  border: 1px solid var(--border);
}
/* Wer SELBST abonniert hat, hat eine Entscheidung getroffen - das darf man
   sehen. Sanftes Leuchten in der Event-Farbe, kein Blinken. */
.bil-namen .nam-eigen {
  border-color: color-mix(in srgb, var(--ev-farbe, #f59e0b) 55%, transparent);
  background: color-mix(in srgb, var(--ev-farbe, #f59e0b) 12%, transparent);
  animation: namLeuchten 2.6s ease-in-out infinite;
}
@keyframes namLeuchten {
  0%,100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--ev-farbe, #f59e0b) 30%, transparent); }
  50%     { box-shadow: 0 0 7px 1px color-mix(in srgb, var(--ev-farbe, #f59e0b) 26%, transparent); }
}
@media (prefers-reduced-motion: reduce) { .bil-namen .nam-eigen { animation: none; } }
.nam-zusatz { color: var(--text-schwach, #8b93a7); font-size: .9em; }
.bil-fuss { display: flex; align-items: center; gap: 12px; margin-top: 12px; flex-wrap: wrap; }
.bil-fuss .hint { margin: 0; font-size: .78em; }

/* --- v1.8.1: Eigene Namen und Zeichen --- */
.art-tabelle { display: flex; flex-direction: column; gap: 7px; }
.art-zeile { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.art-vorschau { flex: 0 0 auto; min-width: 7.5em; }
.art-vorschau svg { width: 22px; height: 22px; color: var(--ev-farbe, #f59e0b); }
.art-label { flex: 0 0 auto; width: 10em; font-size: .88em; }
.art-zeile input { flex: 0 0 auto; width: 11em; }
@media (max-width: 700px) {
  .art-label { width: auto; }
  .art-zeile input { flex: 1 1 9em; width: auto; }
}

/* --- v1.8.1: Notizen-Seite aufgeräumt ---
   Der senkrechte Strich zwischen Überschrift und Löschknopf war ein
   .toolbar-sep ohne Nachbarn - ein Trenner, der nichts trennte. Statt
   dessen jetzt eine Kopfzeile mit Titel, Erklärung und Knopf rechts. */
.notiz-kopf {
  display: flex; align-items: flex-start; gap: 12px;
  margin-bottom: 10px; flex-wrap: wrap;
}
.notiz-kopf .z-fuell { flex: 1 1 auto; }
.notiz-vorlagen { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }
.notiz-vorlagen .btn { font-size: .82em; }

.vorlage-liste { display: flex; flex-direction: column; gap: 3px; }
.vorlage-zeile {
  display: flex; align-items: center; gap: 9px;
  padding: 5px 10px; border-radius: 6px;
  background: var(--flaeche-2, rgba(255,255,255,.04));
}
.vorlage-farbe { width: 4px; align-self: stretch; border-radius: 2px; flex: 0 0 auto; }
.vorlage-schloss { flex: 0 0 auto; font-size: .9em; }
.vorlage-text { font-weight: 600; }
.vorlage-form { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.vorlage-form input[type="text"][name="text"] { flex: 1 1 16em; min-width: 12em; }
.vorlage-form input[name="label"] { flex: 0 0 9em; width: 9em; }
.vorlage-form input[type="color"] { flex: 0 0 auto; width: 3.2em; padding: 2px; }

/* ==========================================================================
   v1.10.0 - INTERNER BEREICH (Team-Chat und Notizen)
   ==========================================================================
   Die Gestaltung folgt bewusst dem Event Feed: dieselben Karten, dieselben
   Chips, dieselben roten Zahlen. Es soll sich nicht wie ein nachtraeglich
   angebautes Notizsystem anfuehlen, sondern wie ein Bereich, der von Anfang
   an dazugehoert hat.
   ========================================================================== */

/* Der Puls am Tab: zwei-, dreimal ein heller Rand - danach Ruhe. Ein
   Dauerblinken haelt man in einer Sendung nicht aus. */
@keyframes intern-puls {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255,255,255,0); }
  25%      { box-shadow: 0 0 0 3px rgba(255,255,255,.42); }
  50%      { box-shadow: 0 0 0 0 rgba(255,255,255,0); }
  75%      { box-shadow: 0 0 0 3px rgba(255,255,255,.30); }
}
.tab-btn.intern-puls { animation: intern-puls 900ms ease-in-out 3; border-radius: 10px; }
@media (prefers-reduced-motion: reduce) {
  .tab-btn.intern-puls { animation: none; box-shadow: 0 0 0 2px rgba(255,255,255,.35); }
}

.in-bereich { display: flex; flex-direction: column; gap: 14px; }

/* ---------- Kopfleiste ---------- */
.in-leiste {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  padding: 10px 12px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.in-leiste .btn { white-space: nowrap; }
.in-zahl {
  display: inline-block; min-width: 17px; padding: 0 5px;
  border-radius: 999px;
  background: #ef4444; color: #fff;
  font-size: 10.5px; line-height: 17px; font-weight: 700; text-align: center;
}
.in-suche { position: relative; flex: 1 1 200px; min-width: 160px; max-width: 340px; }
.in-suche input {
  width: 100%; padding: 7px 28px 7px 30px;
  border: 1px solid var(--border); border-radius: 8px;
  background: var(--bg-card); color: var(--text);
}
.in-suche-lupe {
  position: absolute; left: 9px; top: 50%; transform: translateY(-50%);
  font-size: 13px; opacity: .7; pointer-events: none;
}
.in-suche-weg {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  border: 0; background: transparent; color: var(--text-dim);
  cursor: pointer; font-size: 13px; line-height: 1; padding: 3px;
}
.in-suche-weg:hover { color: var(--text); }

/* ---------- Chat ---------- */
.in-chat-karte { padding: 14px 16px; }
.in-chat-kopf { display: flex; align-items: baseline; gap: 10px; }
.in-chat-kopf h3 { margin: 0 0 8px 0; }
.in-chat-anzahl { color: var(--text-dim); font-size: 12px; }

/* Waechst mit dem Inhalt - bis rund 40 Zeilen, dann wird gescrollt.
   Die Leiste bekommt die App-Farben statt des Systemgraus. */
.in-chat-fenster {
  max-height: 42em; overflow-y: auto;
  padding: 6px 4px 6px 0;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  scrollbar-width: thin; scrollbar-color: var(--border) transparent;
}
.in-chat-fenster::-webkit-scrollbar { width: 9px; }
.in-chat-fenster::-webkit-scrollbar-track { background: transparent; }
.in-chat-fenster::-webkit-scrollbar-thumb {
  background: var(--border); border-radius: 999px;
  border: 2px solid var(--bg-card);
}
.in-chat-fenster::-webkit-scrollbar-thumb:hover { background: var(--accent); }

.in-chat-zeile {
  display: flex; align-items: baseline; gap: 7px;
  padding: 3px 6px; border-radius: 6px;
  line-height: 1.5; font-size: 13.5px;
  word-break: break-word;
}
.in-chat-zeile:hover { background: rgba(255,255,255,.045); }
.in-chat-zeit {
  flex: 0 0 auto; color: var(--text-dim);
  font-size: 11px; font-variant-numeric: tabular-nums;
}
.in-chat-name { flex: 0 0 auto; font-weight: 700; color: var(--accent); }
.in-chat-zeile.eigen .in-chat-name { color: var(--success); }
.in-chat-text { flex: 1 1 auto; }
.in-erwaehnt {
  padding: 0 3px; border-radius: 4px;
  background: rgba(139,92,246,.22); color: var(--text); font-weight: 600;
}
.in-chat-weg {
  flex: 0 0 auto; opacity: 0;
  border: 0; background: transparent; color: var(--text-dim);
  cursor: pointer; font-size: 12px; line-height: 1; padding: 2px 4px;
}
.in-chat-zeile:hover .in-chat-weg { opacity: 1; }
.in-chat-weg:hover { color: var(--danger); }

.in-chat-eingabe { position: relative; display: flex; align-items: flex-end; gap: 8px; margin-top: 10px; }
.in-chat-eingabe textarea {
  flex: 1 1 auto; resize: none; overflow-y: auto;
  min-height: 36px; max-height: 160px;
  padding: 8px 10px; line-height: 1.45;
  border: 1px solid var(--border); border-radius: 8px;
  background: var(--bg-card); color: var(--text); font-family: inherit; font-size: 13.5px;
}

/* @-Vorschlaege: klein, direkt ueber dem Feld, Tastatur bedienbar. */
/* WICHTIG: display:flex wuerde das hidden-Attribut aushebeln - die leere
   Box stand sonst als kurzer Strich ueber dem Eingabefeld. */
.in-vorschlaege[hidden] { display: none; }
.in-vorschlaege {
  position: absolute; bottom: calc(100% + 6px); left: 0;
  display: flex; flex-direction: column; min-width: 180px; max-width: 300px;
  padding: 4px; z-index: 40;
  background: var(--bg-elevated);
  border: 1px solid var(--border); border-radius: 8px;
  box-shadow: 0 8px 22px rgba(0,0,0,.35);
}
.in-vorschlag {
  text-align: left; padding: 5px 8px; border: 0; border-radius: 6px;
  background: transparent; color: var(--text); cursor: pointer; font-size: 13px;
}
.in-vorschlag.an, .in-vorschlag:hover { background: var(--accent); color: var(--accent-text); }

/* ---------- Die zwei Spalten ---------- */
.in-spalten { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; align-items: start; }
.in-spalte { padding: 14px 16px; }
.in-spalte-kopf { display: flex; align-items: baseline; gap: 8px; }
.in-spalte-kopf h3 { margin: 0 0 10px 0; }
.in-spalte-anzahl {
  padding: 1px 7px; border-radius: 999px;
  background: var(--border); color: var(--text-dim);
  font-size: 11px; font-weight: 700;
}
.in-chips { display: flex; flex-direction: column; gap: 7px; }
.in-plus { margin-top: 10px; }

.in-chip {
  border: 1px solid var(--border); border-radius: 9px;
  background: var(--bg-elevated);
  transition: border-color .12s ease, background .12s ease;
}
.in-chip.hat-neu { border-color: rgba(239,68,68,.55); }
.in-chip.zieht { opacity: .45; }
.in-chip.ziel { border-color: var(--accent); background: rgba(139,92,246,.12); }
.in-chip-kopf {
  display: flex; align-items: center; gap: 7px;
  padding: 7px 10px; cursor: pointer;
}
.in-chip-kopf:hover { background: rgba(255,255,255,.05); border-radius: 9px; }
.in-griff { color: var(--text-dim); cursor: grab; font-size: 12px; }
.in-chip-name { font-weight: 600; }
.in-frueher {
  color: var(--text-dim); font-size: 11px; font-style: italic;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 140px;
}
.in-kat {
  padding: 1px 7px; border-radius: 999px;
  background: rgba(139,92,246,.18); color: var(--text-dim); font-size: 10.5px;
}
.in-chip-anzahl {
  margin-left: auto; padding: 1px 7px; border-radius: 999px;
  background: var(--border); color: var(--text-dim);
  font-size: 10.5px; font-weight: 700; font-variant-numeric: tabular-nums;
}
.in-chip-pfeil { color: var(--text-dim); font-size: 11px; }
.in-vorschau {
  padding: 0 10px 8px 10px; margin-top: -2px;
  color: var(--text-dim); font-size: 12px; line-height: 1.4;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.in-chip-inhalt { padding: 2px 10px 10px 10px; border-top: 1px solid var(--border); }
.in-eintrag-klein { padding: 6px 0; border-bottom: 1px dashed var(--border); }
.in-eintrag-klein:last-of-type { border-bottom: 0; }
.in-eintrag-meta {
  display: flex; flex-wrap: wrap; align-items: center; gap: 7px;
  color: var(--text-dim); font-size: 11px; margin-bottom: 3px;
}
.in-eintrag-text { font-size: 13px; line-height: 1.5; white-space: pre-wrap; word-break: break-word; }
.in-mehr { color: var(--text-dim); font-size: 11.5px; padding: 6px 0 2px 0; }
.in-chip-knoepfe { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }

.in-leer { color: var(--text-dim); font-style: italic; font-size: 12.5px; padding: 8px 4px; }

/* ---------- Popups ---------- */
.in-modal { max-width: 640px; }
.in-verlauf-modal { max-width: 720px; }
.in-verlauf-modal h2 { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.in-verlauf-sym { font-size: 20px; }
.in-verlauf-kopf {
  padding: 12px; margin-bottom: 14px;
  background: var(--bg-elevated); border: 1px solid var(--border); border-radius: 9px;
}
.in-verlauf-zeile { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px; }
.in-verlauf-kopf .field { flex: 1 1 180px; margin-bottom: 0; }
.in-verlauf-kopf .hint { margin-top: 9px; margin-bottom: 0; }

/* Knopfreihen in den Popups: rechtsbuendig, jeder Knopf nur so breit wie
   noetig. Ohne das streckt .row sie auf Fensterbreite - der rote
   Loeschknopf war dann das Auffaelligste im ganzen Fenster. */
.in-knopfreihe { display: flex; flex-wrap: wrap; gap: 8px; justify-content: flex-end; margin-top: 14px; }
.in-knopfreihe .btn { flex: 0 0 auto; width: auto; }
.in-verlauf-liste { max-height: 46vh; overflow-y: auto; padding-right: 4px; }
.in-eintrag {
  padding: 9px 0; border-bottom: 1px solid var(--border);
}
.in-eintrag:last-child { border-bottom: 0; }
.in-eintrag-knoepfe { margin-left: auto; display: flex; gap: 4px; }
.in-geaendert { font-style: italic; }
.in-mini {
  border: 1px solid var(--border); border-radius: 6px;
  background: transparent; color: var(--text-dim);
  cursor: pointer; font-size: 11px; line-height: 1; padding: 3px 6px;
}
.in-mini:hover { color: var(--text); border-color: var(--accent); }
.in-mini.rot:hover { color: var(--danger); border-color: var(--danger); }
.in-bearbeiten {
  width: 100%; padding: 8px 10px; font-family: inherit; font-size: 13px;
  border: 1px solid var(--accent); border-radius: 8px;
  background: var(--bg-card); color: var(--text); resize: vertical;
}

.in-art-wahl { display: flex; gap: 10px; }
.in-art {
  display: flex; align-items: center; gap: 7px;
  padding: 7px 12px; border-radius: 8px; cursor: pointer;
  border: 1px solid var(--border); background: var(--bg-elevated);
}
.in-art.an { border-color: var(--accent); background: rgba(139,92,246,.14); }
.in-feld-schmal { max-width: 150px; }

/* ---------- Protokoll ---------- */
.in-protokoll { padding: 12px 16px; }
.in-protokoll summary { cursor: pointer; font-weight: 600; }
.in-protokoll-liste { max-height: 240px; overflow-y: auto; margin-top: 10px; }
.in-protokoll-zeile {
  display: flex; gap: 8px; align-items: baseline;
  padding: 4px 2px; font-size: 12.5px; border-bottom: 1px dashed var(--border);
}
.in-protokoll-zeile:last-child { border-bottom: 0; }

@media (max-width: 860px) {
  .in-spalten { grid-template-columns: 1fr; }
  .in-suche { max-width: none; }
  .in-chat-fenster { max-height: 24em; }
}

/* ==========================================================================
   v1.10.0 - LUFT, ZWEITER DURCHGANG
   ==========================================================================
   Beim Durchsehen des ganzen Programms blieben drei Stellen uebrig, an denen
   Text direkt an Knoepfen klebte. Der Grund war ueberall derselbe: die
   Bausteine standen als nackte Geschwister in einer Karte, und niemand hatte
   je gesagt, wie viel Platz dazwischen gehoert.
   ========================================================================== */

/* Fliesstext und Knopf als direkte Nachbarn - der haeufigste Klebefall. */
.card > p + .btn, .card > .hint + .btn,
.card > .btn + p, .card > .btn + .hint,
.card > p + button, .card > button + p, .card > button + .hint {
  margin-top: 12px;
}

/* Event Feed: der Erledigten-Block stand direkt am Hinweis darueber. */
.ev-fertig-block { margin-top: 14px; }
.ev-karte > .ev-fertig-block + p.hint { margin-top: 14px; }

/* Raid-Abstimmung: Modusleiste. Marke, Knoepfe und die Lagebeschreibung
   standen ohne jeden Abstand uebereinander. */
.rv-modus { margin: 16px 0 4px; }
.rv-modus-marke {
  display: block; margin-bottom: 7px;
  color: var(--text-dim); font-size: 11px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .05em;
}
.rv-modus-knoepfe { display: flex; flex-wrap: wrap; gap: 8px; }
.rv-modus-lage {
  display: block; margin-top: 9px;
  color: var(--text-dim); font-size: 12.5px; line-height: 1.45;
}
.rv-fluester-zeile { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.rv-karte > .rv-modus + * { margin-top: 14px; }
.rv-rechte { margin-top: 14px; }
.rv-rechte .checkbox-row + .checkbox-row { margin-top: 4px; }

/* ==========================================================================
   v1.10.1 - KONSISTENZ, DJ-MELDUNGEN, AUSWAHL
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. DIE SEITE DARF NICHT SPRINGEN
   --------------------------------------------------------------------------
   Befund aus dem Betrieb: "wenn ich von Notizen zu Musikfeed gehe, rutscht
   die ganze Seite ein bisschen nach links". Grund: Der eine Tab ist lang
   genug fuer einen Scrollbalken, der andere nicht - und mit dem Balken
   verschwindet auch der Platz, den er belegt hat. Bei zentriertem Inhalt
   wandert dann alles um seine halbe Breite.

   "scrollbar-gutter: stable" haelt den Platz IMMER frei. Kein Springen mehr,
   egal wie lang eine Seite ist.
   -------------------------------------------------------------------------- */
html { scrollbar-gutter: stable; }

/* --------------------------------------------------------------------------
   2. GLEICHE DINGE, GLEICHE GROESSE
   --------------------------------------------------------------------------
   Es gab drei Knopfgroessen, die sich um wenige Pixel unterschieden, und
   Chips in vier Hoehen. Das liest sich nicht als Absicht, sondern als
   Zufall. Hier stehen die Groessen jetzt EINMAL - wer bewusst klein baut,
   nimmt .mini, wer normal baut, nimmt gar nichts.
   -------------------------------------------------------------------------- */
:root {
  --knopf-hoch:  36px;   /* Standardknopf und Eingabefeld: gleiche Hoehe   */
  --knopf-klein: 30px;   /* .small - Werkzeugleisten                       */
  --knopf-mini:  24px;   /* .mini  - Symbolknoepfe in Zeilen               */
  --chip-hoch:   20px;   /* Etiketten, Zaehler, Statusmarken               */
}

/* Knopf und Eingabefeld nebeneinander sollen gleich hoch sein - sonst
   steht in jeder Werkzeugleiste eine kleine Stufe. */
.btn { min-height: var(--knopf-hoch); padding: 8px 16px; font-size: 14px; line-height: 1.2; }
.btn.small { min-height: var(--knopf-klein); padding: 5px 11px; font-size: 12.5px; }
.btn.mini  { min-height: var(--knopf-mini);  padding: 3px 8px;  font-size: 11.5px; }
input[type="text"], input[type="number"], input[type="password"], input[type="url"],
input[type="search"], input[type="email"], select {
  min-height: var(--knopf-hoch);
}

/* Etiketten und Zaehler - eine Hoehe fuer alle. Vorher lagen sie zwischen
   17 und 24 Pixeln, je nachdem, wer sie gebaut hat. */
.note-label-chip, .cat-chip, .abg-status, .in-kat, .in-chip-anzahl,
.in-spalte-anzahl, .tw-chip, .sup-chip {
  min-height: var(--chip-hoch);
  display: inline-flex; align-items: center;
  font-size: 11px; line-height: 1;
}

/* --------------------------------------------------------------------------
   3. EINSTELLUNGEN: SEITENLEISTE
   --------------------------------------------------------------------------
   Der Menuepunkt ist jetzt ein <div> mit einem Knopf darin (vorher lagen
   Knoepfe IN einem Knopf - ungueltiges HTML, das der Browser aufbrach; die
   Speicher-Chips fielen deshalb als zwei nackte Kaesten darunter).
   -------------------------------------------------------------------------- */
/* v1.28.24 - KOMPAKT MIT BESCHREIBUNG (Saschas Wunsch: "Kompakt sieht gut
   aus ABER mir fehlen die Beschreibungen aus Luftig"). Zwei Zeilen, aber
   enge Abstaende: Titel fett, darunter die Kurzbeschreibung in klein. Und
   dicht an den Kanten - "nicht so extrem weit weg vom Rand". */
.sl-punkt {
  display: flex; align-items: flex-start; gap: 3px; width: 100%;
  padding: 0 5px 0 0; border-radius: 8px;
  color: var(--text-dim); background: transparent;
  border-left: 3px solid transparent;
}
.sl-punkt:hover { background: rgba(148,163,184,.1); color: var(--text); }
.sl-punkt.aktiv {
  background: color-mix(in srgb, var(--accent) 20%, transparent);
  border-left-color: var(--accent);
  color: var(--text);
}
.sl-punkt-haupt {
  display: flex; align-items: flex-start; gap: 8px;
  flex: 1 1 auto; min-width: 0;
  border: 0; background: transparent; color: inherit; font: inherit;
  padding: 5px 2px 5px 7px; cursor: pointer; text-align: left;
  font-size: 13px;
}
.sl-punkt .sym { line-height: 1.3; }
.sl-punkt.aktiv .sl-punkt-haupt { font-weight: 600; }
.sl-punkt .txt {
  flex: 1 1 auto; min-width: 0; display: block; line-height: 1.25;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Die Kurzbeschreibung - klein, gedaempft, einzeilig abgeschnitten. */
.sl-punkt .txt-sub {
  display: block; font-size: 10.5px; font-weight: 400; opacity: .68;
  line-height: 1.25; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sl-punkt.aktiv .txt-sub { opacity: .8; }

/* ==========================================================================
   v1.29.1 - "GERADE EINGESTELLT" AM MENUEPUNKT

   Saschas Wunsch: sehen, was hinter einem Punkt steckt, ohne hineinzuklicken.

   Die Zeile muss sich von der Kurzbeschreibung DARUEBER unterscheiden - sonst
   liest sich beides wie ein Satz. Deshalb: eine Spur kraeftiger als die
   Beschreibung (sie ist die konkretere Angabe), leicht in Richtung der
   Akzentfarbe getoent, und Zahlen in gleichbreiten Ziffern, damit
   untereinander stehende Punkte nicht zappeln.

   Sie bricht NIE um. Die Leiste ist schmal; eine zweite Zeile wuerde jeden
   Punkt hoeher machen und die Liste auf dem Handy unbrauchbar in die Laenge
   ziehen. Was nicht passt, wird abgeschnitten - der ganze Text steht im
   Mouseover.
   ========================================================================== */
.sl-punkt .txt-stand {
  display: block; font-size: 10.5px; font-weight: 600;
  line-height: 1.25; margin-top: 1px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--accent); opacity: .85;
  font-variant-numeric: tabular-nums;
}
/* Im aktiven Punkt steht sie auf der Akzentfarbe - dort muss sie die
   Gegenfarbe nehmen, sonst verschwindet sie im Hintergrund. */
.sl-punkt.aktiv .txt-stand { color: var(--accent-text); opacity: .85; }

/* ==========================================================================
   v1.30.0 - SASCHAS GRUNDREGEL: HELL IST, WAS BENENNT

   Er hat sie zweimal gesagt. Erst zu den Schaltern: "wenn es optionen sind,
   nicht so 'extrem' dezent sondern dann darf die Option selbst die man damit
   schaltet auch in weiss statt dunkel grau sein." Dann zu den Menuepunkten:
   "gut waere wenn wie schonmal besprochen Ueberschriften oder Einstellungen
   selbst HELL sind... also Kategorien Ansichten Warnlisten usw das is alles
   auch grau zwar groesser als der text drunter aber nicht so deutlich
   sichtbar."

   Damit ist es keine Einzelfallbitte, sondern eine Regel:

       Hell ist, was etwas BENENNT oder SCHALTET.
       Gedaempft ist, was es ERKLAERT.

   Der NAME des Menuepunkts wird deshalb hell. Kurzbeschreibung (.txt-sub)
   und Zustandszeile (.txt-stand) bleiben, wie sie waren - der Abstand
   zwischen den drei Ebenen ist ja der Sinn der Sache. Wuerde man alles
   anheben, waere nichts mehr hervorgehoben.
   ========================================================================== */
.sl-punkt .txt { color: var(--text); font-weight: 600; }
/* Der aktive Punkt steht auf der Akzentfarbe - dort gilt die Gegenfarbe,
   sonst verschwindet die Schrift im Hintergrund. */
.sl-punkt.aktiv .txt { color: var(--accent-text); }

/* WICHTIG, im Browser nachgemessen: .txt-sub und .txt-stand stehen INNERHALB
   von .txt und erbten dessen Farbe - sie hatten nur eine geringere Deckkraft.
   Wird .txt heller, werden sie also MIT heller, und der Abstand zwischen den
   Ebenen bliebe genau so klein wie vorher. Genau das wollte Sascha nicht.
   Deshalb bekommen beide jetzt ihre eigene, gedaempfte Farbe. */
.sl-punkt .txt-sub { color: var(--text-dim); }
.sl-punkt.aktiv .txt-sub { color: var(--accent-text); }
/* Die Gruppentitel ordnen nur, sie benennen keine Sache - sie bleiben
   bewusst zurueckhaltend. Sonst konkurrieren sie mit den Punkten darunter,
   und die sind das, wonach man sucht. */

/* ==========================================================================
   v1.30.0 - HINWEISE AUF EINSTELLUNGEN SIND LINKS

   Saschas Wunsch: "mach 'vorgaben' dann auch gleich als Link zu genau den
   Menueeinstellungen wo diese einzustellen sind. Sowas sollten wir generell
   machen."

   Technisch ist es ein <button> (ein <a> ohne Ziel waere gelogen, und ein
   echtes Ziel gibt es nicht - die App wechselt den Bereich im Programm).
   Er soll aber wie Text im Satz liegen, nicht wie ein Bedienknopf:
   deshalb alles zurueckgesetzt, was ein Knopf sonst mitbringt.
   ========================================================================== */
.einst-link {
  background: none; border: 0; padding: 0; margin: 0;
  font: inherit; color: var(--accent); cursor: pointer;
  text-decoration: underline; text-decoration-style: dotted;
  text-underline-offset: 2px; text-decoration-thickness: 1px;
}
.einst-link:hover { text-decoration-style: solid; }
.einst-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }

/* Die Wegweiser, die nach einem Umzug am alten Ort stehen bleiben. */
.an-umzug, .rv-umzug { margin-top: 10px; }

/* ==========================================================================
   v1.31.0 - FOLLOW-WELLEN

   Saschas Anlass: ein Bot-Schwall mit hunderten Follows. Die Markierung ist
   bewusst ein HINWEIS, keine Warnung: Es ist nicht sicher, dass es Bots
   waren - deshalb gelb (schau mal nach), nicht rot (hier ist etwas kaputt).
   ========================================================================== */
.pa-e-welle {
  display: inline-block; padding: 1px 7px; border-radius: 999px;
  font-size: 10.5px; font-weight: 600; white-space: nowrap;
  color: var(--achtung, #eab308);
  background: rgba(234,179,8,.12); border: 1px solid rgba(234,179,8,.4);
}
/* In der Personenliste: ein schmaler Streifen am Rand. Er darf die Zeile
   nicht umbauen - sonst zappelt die ganze Liste, sobald eine Welle
   hereinkommt. */
.pa-zeile.welle { border-left: 3px solid rgba(234,179,8,.6); }

.welle-vorschau { margin-top: 14px; }
.welle-probe { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; align-items: center; }
.welle-name {
  font-size: 12px; padding: 2px 8px; border-radius: 999px;
  background: var(--bg-elevated); border: 1px solid var(--border); color: var(--text-dim);
}
.welle-name.markiert {
  color: var(--achtung, #eab308);
  border-color: rgba(234,179,8,.45); background: rgba(234,179,8,.10);
}
/* v1.28.24: der gelbe Punkt an einem Bereich mit ungespeicherten Aenderungen */
.sl-punkt .sl-dreckig {
  width: 7px; height: 7px; border-radius: 99px; background: var(--warn, #eab308);
  flex: 0 0 auto; margin: 9px 2px 0 0; display: none;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--warn, #eab308) 16%, transparent);
}
.sl-punkt.hat-aenderung .sl-dreckig { display: block; }
.sl-chips { display: inline-flex; gap: 4px; flex: 0 0 auto; margin-left: 0; }
.sl-chip { width: 26px; height: 24px; flex: 0 0 auto; }
/* Blass = "hier gibt es nichts nachzuholen, der Bereich speichert sofort".
   Der Knopf bleibt bedienbar und sagt das im Mouseover - er verschwindet
   nicht, damit die Leiste nicht bei jedem Bereichswechsel anders aussieht. */
.sl-chip.blass { opacity: .35; }
.sl-chip.blass:hover { opacity: .6; }

/* --------------------------------------------------------------------------
   4. VORLAGEN-KNOEPFE (Notizen)
   --------------------------------------------------------------------------
   Sie waren die groessten Knoepfe im Programm. Jetzt auf Normalmass, dafuer
   mit der Farbmarke ihres Etiketts am linken Rand - derselbe senkrechte
   Farbstreifen, den die Notizen in der Zeitleiste tragen.
   -------------------------------------------------------------------------- */
.notiz-vorlagen { display: flex; flex-wrap: wrap; gap: 7px; margin-bottom: 12px; }
/* ".btn.vorlage-knopf" statt nur ".vorlage-knopf": ".btn.secondary" setzt
   einen eigenen Rahmen und wiegt zwei Klassen - mit nur einer Klasse
   gewinnt es, und der Farbstreifen war unsichtbar. */
.btn.vorlage-knopf {
  border-left: 3px solid color-mix(in srgb, var(--vorlage-farbe, #38bdf8) 55%, transparent);
  padding-left: 10px;
  max-width: 260px;
  text-align: left;
  justify-content: flex-start;
  white-space: normal;
}
.btn.vorlage-knopf:hover {
  border-left-color: var(--vorlage-farbe, #38bdf8);
  background: color-mix(in srgb, var(--vorlage-farbe, #38bdf8) 9%, var(--bg-elevated));
}

/* --------------------------------------------------------------------------
   5. INTERNER BEREICH: Auswahl und "an den DJ"
   -------------------------------------------------------------------------- */

/* Der Haken erscheint erst beim Darueberfahren - solange man nur liest,
   soll die Liste ruhig bleiben. Gesetzt bleibt er natuerlich sichtbar. */
.in-haken { display: inline-flex; align-items: center; flex: 0 0 auto; cursor: pointer; }
.in-haken input { width: 14px; height: 14px; margin: 0; cursor: pointer; accent-color: var(--accent); }
.in-eintrag-klein .in-haken, .in-eintrag .in-haken, .in-haken-alle { opacity: 0; transition: opacity .12s ease; }
.in-eintrag-klein:hover .in-haken,
.in-eintrag:hover .in-haken,
.in-chip:hover .in-haken-alle,
.in-haken input:checked { opacity: 1; }
.in-haken:focus-within { opacity: 1; }
.in-eintrag-klein.gewaehlt { background: rgba(139,92,246,.10); border-radius: 6px; }

/* Der 🎧-Knopf: zurueckhaltend, bis man ihn braucht. */
.in-chat-dj, .in-eintrag-dj {
  flex: 0 0 auto; opacity: 0;
  border: 0; background: transparent; color: var(--text-dim);
  cursor: pointer; font-size: 12px; line-height: 1; padding: 2px 4px;
}
.in-chat-zeile:hover .in-chat-dj,
.in-eintrag-klein:hover .in-eintrag-dj { opacity: .8; }
.in-chat-dj:hover, .in-eintrag-dj:hover { opacity: 1; color: var(--accent); }
.in-spalte-sort { margin-left: auto; flex: 0 0 auto; }
.in-spalte-kopf { display: flex; align-items: center; gap: 8px; }

/* --------------------------------------------------------------------------
   6. DJ-ANZEIGE: Knopf und Meldung
   --------------------------------------------------------------------------
   Der Knopf ist blass, solange nichts anliegt - da, aber nicht laut. Liegt
   etwas an, blinkt sein RAND dezent, und zwar in der global eingestellten
   Akzentfarbe: die Farbe, die der Kanal sich ausgesucht hat, nicht ein
   eigener Signalton.
   -------------------------------------------------------------------------- */
.dj-intern-knopf {
  position: relative;
  display: inline-grid; place-items: center;
  width: 34px; height: 30px;
  border-radius: 9px; border: 1px solid var(--border);
  background: transparent; color: var(--text-dim);
  cursor: pointer; opacity: .42; font-size: 15px; padding: 0;
  transition: opacity .15s ease, border-color .15s ease;
}
.dj-intern-knopf:hover { opacity: .85; color: var(--text); }
.dj-intern-knopf.hat-neu {
  opacity: 1; color: var(--text);
  animation: dj-intern-rand 1.6s ease-in-out infinite;
}
@keyframes dj-intern-rand {
  0%, 100% { border-color: color-mix(in srgb, var(--accent) 35%, transparent); box-shadow: 0 0 0 0 transparent; }
  50%      { border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 26%, transparent); }
}
@media (prefers-reduced-motion: reduce) {
  .dj-intern-knopf.hat-neu { animation: none; border-color: var(--accent); }
}
.dj-intern-zahl {
  position: absolute; top: -5px; right: -5px;
  min-width: 15px; height: 15px; padding: 0 4px; border-radius: 999px;
  background: #ef4444; color: #fff;
  font-size: 9.5px; line-height: 15px; font-weight: 700;
}

/* Die Meldung selbst: oben rechts, schmal. Mitten im Bild wuerde sie die
   Wunschliste verdecken - und genau die liest der DJ waehrend der Sendung. */
.dj-intern-popups {
  position: fixed; top: 62px; right: 16px; z-index: 900;
  display: flex; flex-direction: column; gap: 8px;
  width: min(360px, 84vw);
}
.dj-intern-popup {
  border: 1px solid color-mix(in srgb, var(--accent) 45%, var(--border));
  border-left: 4px solid var(--accent);
  border-radius: 11px;
  background: var(--bg-elevated);
  box-shadow: 0 12px 34px rgba(0,0,0,.45);
  padding: 9px 11px;
  animation: dj-intern-rein .28s ease-out;
}
@keyframes dj-intern-rein {
  from { opacity: 0; transform: translateX(14px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) { .dj-intern-popup { animation: none; } }
.dj-intern-kopf {
  display: flex; align-items: center; gap: 7px;
  color: var(--text-dim); font-size: 11px; margin-bottom: 5px;
}
.dj-intern-von { font-weight: 700; color: var(--accent); }
.dj-intern-zeit { font-variant-numeric: tabular-nums; }
.dj-intern-zu {
  margin-left: auto; border: 0; background: transparent; color: var(--text-dim);
  cursor: pointer; font-size: 13px; line-height: 1; padding: 2px 4px;
}
.dj-intern-zu:hover { color: var(--danger); }
.dj-intern-text { font-size: 14px; line-height: 1.45; word-break: break-word; white-space: pre-wrap; }
.dj-intern-mehr { color: var(--text-dim); font-size: 11.5px; text-align: right; }

/* Das interne Blatt als Overlay - der DJ soll nachlesen, ohne seine
   Ansicht zu verlassen und danach wiederfinden zu muessen. */
.dj-intern-blatt-schleier {
  position: fixed; inset: 0; z-index: 950;
  background: rgba(0,0,0,.6);
  display: grid; place-items: center; padding: 20px;
}
.dj-intern-blatt {
  width: min(760px, 94vw); max-height: 84vh;
  display: flex; flex-direction: column;
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: 14px; box-shadow: 0 24px 60px rgba(0,0,0,.55);
}
.dj-intern-blatt-kopf {
  display: flex; align-items: center; gap: 12px;
  padding: 14px 16px; border-bottom: 1px solid var(--border);
}
.dj-intern-blatt-kopf h3 { margin: 0; flex: 1 1 auto; }
.dj-intern-blatt-inhalt { overflow-y: auto; padding: 14px 16px 18px; }
.dj-intern-blatt-inhalt h4 { margin: 16px 0 8px; font-size: 13px; color: var(--text-dim); }
.dj-intern-blatt-inhalt h4:first-child { margin-top: 0; }
.dj-intern-thema {
  padding: 8px 10px; margin-bottom: 6px;
  border: 1px solid var(--border); border-radius: 9px;
  background: var(--bg-elevated);
}

/* ==========================================================================
   v1.10.3 - MELDUNGEN: @NENNUNGEN, CHAT-PULS, DJ-BLATT
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. CHAT-PULS AM TAB-ZEICHEN
   --------------------------------------------------------------------------
   Bewusst SEHR langsam (4 s) und sehr schwach. Der Chat ist fluechtig - er
   soll auffallen, wenn man hinschaut, aber niemanden aus der Sendung
   reissen. Eine Zahl bekommt er nicht: Zahlen sind fuer Dinge, die liegen
   bleiben (Notizen).
   -------------------------------------------------------------------------- */
@keyframes in-chat-puls {
  0%, 100% { opacity: 1;   transform: scale(1); }
  50%      { opacity: .45; transform: scale(1.08); }
}
.tab-sym.in-chat-puls { animation: in-chat-puls 4s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) {
  .tab-sym.in-chat-puls { animation: none; opacity: .7; }
}

/* --------------------------------------------------------------------------
   2. @NENNUNG AM EIGENEN NAMEN
   -------------------------------------------------------------------------- */
.kopf-user { position: relative; }
.ku-name { display: inline-flex; align-items: center; gap: 6px; }
.ku-nennung {
  min-width: 17px; height: 17px; padding: 0 5px;
  border: 0; border-radius: 999px;
  background: #ef4444; color: #fff;
  font-size: 10px; line-height: 17px; font-weight: 700;
  cursor: pointer;
  animation: ku-nennung-puls 2.4s ease-in-out 4;
}
@keyframes ku-nennung-puls {
  0%, 100% { box-shadow: 0 0 0 0 rgba(239,68,68,0); }
  50%      { box-shadow: 0 0 0 4px rgba(239,68,68,.30); }
}
@media (prefers-reduced-motion: reduce) { .ku-nennung { animation: none; } }

.ku-nennung-schleier { position: fixed; inset: 0; z-index: 960; background: rgba(0,0,0,.45); }
.ku-nennung-liste {
  position: absolute; top: 62px; right: 16px;
  width: min(420px, 92vw); max-height: 70vh; overflow-y: auto;
  padding: 10px;
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: 12px; box-shadow: 0 18px 46px rgba(0,0,0,.5);
}
.ku-nennung-kopf {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 2px 4px 10px; border-bottom: 1px solid var(--border); margin-bottom: 6px;
}
.ku-nennung-zeile {
  display: block; width: 100%; text-align: left;
  padding: 8px 10px; margin-bottom: 5px;
  border: 1px solid var(--border); border-radius: 9px;
  background: var(--bg-elevated); color: var(--text);
  cursor: pointer; font: inherit;
}
.ku-nennung-zeile:hover { border-color: var(--accent); background: rgba(139,92,246,.10); }
.ku-nennung-meta {
  display: flex; flex-wrap: wrap; align-items: center; gap: 7px;
  color: var(--text-dim); font-size: 11px; margin-bottom: 4px;
}
.ku-nennung-text { font-size: 13px; line-height: 1.45; }

/* --------------------------------------------------------------------------
   3. DJ-BLATT
   -------------------------------------------------------------------------- */
.dj-intern-blatt-kopf h3 { margin: 0; flex: 1 1 auto; }
.dj-blatt-post {
  padding: 9px 11px; margin-bottom: 7px;
  border: 1px solid var(--border); border-left: 3px solid var(--accent);
  border-radius: 9px; background: var(--bg-elevated);
}
/* Schon weggeklickt: bleibt lesbar, tritt aber zurueck. */
.dj-blatt-post.gesehen { border-left-color: var(--border); opacity: .62; }
.dj-blatt-post-kopf {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  color: var(--text-dim); font-size: 11px; margin-bottom: 5px;
}
.dj-blatt-post-text { font-size: 13.5px; line-height: 1.5; white-space: pre-wrap; word-break: break-word; }

/* Die Ueberschrift ist ein Weg, kein Text - das muss man sehen. */
.dj-intern-quelle {
  display: inline-flex; align-items: center; gap: 7px;
  border: 0; background: transparent; color: inherit; font: inherit;
  padding: 2px 5px; margin: -2px -5px; border-radius: 6px; cursor: pointer;
}
.dj-intern-quelle:hover { background: rgba(139,92,246,.18); color: var(--text); }
.dj-intern-quelle b { color: var(--accent); }
.dj-intern-betreff {
  padding: 1px 7px; border-radius: 999px;
  background: rgba(139,92,246,.18); font-size: 10.5px;
}

.dj-blatt-eingabe { display: flex; align-items: flex-end; gap: 8px; margin-bottom: 12px; }
.dj-blatt-eingabe textarea {
  flex: 1 1 auto; resize: none; overflow-y: auto;
  min-height: 36px; max-height: 120px;
  padding: 8px 10px; line-height: 1.45;
  border: 1px solid var(--border); border-radius: 8px;
  background: var(--bg-card); color: var(--text); font-family: inherit; font-size: 13.5px;
}

/* Angesprungener Chip kurz hervorheben - sonst sucht man ihn in einer
   langen Liste trotz Scrollen. */
@keyframes in-chip-gefunden {
  0%, 100% { box-shadow: 0 0 0 0 rgba(139,92,246,0); }
  30%      { box-shadow: 0 0 0 4px rgba(139,92,246,.45); }
}
.in-chip.in-chip-gefunden {
  border-color: var(--accent);
  animation: in-chip-gefunden 1.1s ease-in-out 2;
}

/* --------------------------------------------------------------------------
   4. "MEINE MELDUNGEN"
   -------------------------------------------------------------------------- */
.melde-liste { display: flex; flex-direction: column; gap: 8px; margin: 12px 0; }
.melde-zeile {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 12px;
  border: 1px solid var(--border); border-radius: 10px;
  background: var(--bg-elevated);
}
.melde-sym { font-size: 17px; width: 24px; text-align: center; flex: 0 0 auto; }
.melde-text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.melde-text .hint { margin: 0; font-size: 12px; }
.melde-zeile select { flex: 0 0 auto; max-width: 230px; }
@media (max-width: 620px) {
  .melde-zeile { flex-wrap: wrap; }
  .melde-zeile select { width: 100%; max-width: none; }
}

/* ==========================================================================
   v1.10.4 - SPEICHERN/VERWERFEN + DIE SEITE STEHT ENDGUELTIG STILL
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. DIE SEITE DARF NICHT SPRINGEN - ZWEITER ANLAUF
   --------------------------------------------------------------------------
   "scrollbar-gutter: stable" allein hat nicht ueberall gereicht. Es ist
   jung (Chrome 94+) und wirkt nicht in jeder Kombination aus Zoomstufe und
   Systemeinstellung.

   "overflow-y: scroll" ist der alte, stumpfe, absolut verlaessliche Weg:
   Die vertikale Leiste ist IMMER da - bei der kurzen Notizen-Seite genauso
   wie beim langen Event Feed. Damit gibt es keinen Breitenunterschied mehr,
   den irgendetwas ausgleichen muesste. Beides zusammen deckt jeden Browser
   ab; wo "gutter" wirkt, bleibt die Leiste sogar unsichtbar leer.
   -------------------------------------------------------------------------- */
html {
  scrollbar-gutter: stable;
  overflow-y: scroll;
}
/* Und der Gegencheck: Nichts darf breiter sein als der Bildschirm - eine
   waagerechte Leiste wuerde die Seite genauso verschieben. */
body { overflow-x: hidden; }

/* --------------------------------------------------------------------------
   2. SPEICHERN / VERWERFEN unter dem aktiven Menuepunkt
   --------------------------------------------------------------------------
   Volle Breite, Zeichen vorn, Wort dahinter. Zwei nackte Symbole im
   Menuepunkt waren geraten statt gelesen - und bei "Benachrichtigungen"
   wurde es dort schlicht zu eng.
   -------------------------------------------------------------------------- */
/* v1.28.24 - DIE LEISTE STEHT FEST OBEN UEBER DEM BAUM.
   Saschas Vorgabe woertlich: "vielleicht sind die speicher und verwerfen
   button oben im baum immer fest und nur wenn sie anklickbar sind weil was
   zu speichern ist, werden sie gruen oder rot". Genau so: Sie ist IMMER da
   (deshalb springt der Baum nicht mehr, wenn man den Bereich wechselt) und
   bekommt Farbe erst, wenn es wirklich etwas zu sichern gibt. */
.sl-sichern {
  display: grid; grid-template-columns: 1fr 1fr; gap: 5px;
  margin: 0; padding: 7px 7px;
  border-bottom: 1px solid var(--border);
  flex: 0 0 auto;
  background: var(--bg-elevated);
}
.sl-sichern-knopf {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  min-height: 30px; padding: 5px 8px;
  border-radius: 8px; border: 1px solid var(--border);
  background: color-mix(in srgb, var(--bg) 55%, var(--bg-elevated));
  color: var(--text-dim);
  font: inherit; font-size: 12px; font-weight: 700;
  cursor: default; white-space: nowrap; opacity: .75;
  transition: background .18s, border-color .18s, color .18s, opacity .18s;
}
/* Nur wenn es etwas zu tun gibt, wird sie klickbar und farbig. */
.sl-sichern-knopf.gruen, .sl-sichern-knopf.rot { cursor: pointer; opacity: 1; }
.sl-sichern-sym { font-size: 12px; line-height: 1; }
.sl-sichern-knopf.gruen {
  border-color: rgba(34,197,94,.55);
  background: rgba(34,197,94,.14);
}
.sl-sichern-knopf.gruen:hover { background: rgba(34,197,94,.26); border-color: rgba(34,197,94,.8); }
.sl-sichern-knopf.rot {
  border-color: rgba(229,72,77,.45);
  background: rgba(229,72,77,.10); color: #ff8a8d;
}
.sl-sichern-knopf.rot:hover { background: rgba(229,72,77,.22); border-color: rgba(229,72,77,.7); }

/* Der Pfeil gehoert wieder in den Punkt (die Chips sind ja raus).
   v1.28.24 - HIER standen die alten, weiten Abstaende (8px 11px) und haben
   den kompakten Block weiter oben ueberschrieben, weil sie spaeter kommen.
   Jetzt tragen beide dieselben engen Werte: Saschas "mach doch bitte diese
   Icons und Menuepunkte nicht so extrem weit weg vom Rand". */
.sl-punkt { padding: 0 4px 0 0; }
.sl-punkt-haupt { padding: 5px 2px 5px 6px; width: 100%; }
.sl-punkt .pfeil { display: none; opacity: .5; flex: 0 0 auto; }

/* Grau und ohne Farbe: dieser Bereich hat gar nichts zum Sammelspeichern.
   Die Leiste bleibt trotzdem stehen (siehe slLeisteAbgleichen) und erklaert
   sich im Mouseover. */
.sl-sichern-leer .sl-sichern-knopf { opacity: .45; }

/* ==========================================================================
   v1.28.24 - REGLER NEBENEINANDER STATT UEBER DIE VOLLE BREITE

   Sascha: "Ausserdem sind mir die Regler etwas zu uebertrieben gross. da
   koennte man z.b. platz sparen wenn man sowas 'nebeneinander' macht und die
   regler halb so breit?!"

   .reg-reihe stellt die Reglerfelder nebeneinander und bricht auf schmalen
   Fenstern von selbst um. Die halbe Breite steckt in der max-width: die
   Regler standen vorher mit flex:1 bzw. width:100% ueber die ganze Spalte.
   ========================================================================== */
.reg-reihe {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 4px 18px;
  align-items: start;
}
.reg-reihe > .field { margin-bottom: 6px; }
.einst-inhalt input[type="range"] { max-width: 190px; }
.einst-inhalt .reg-zeile input[type="range"] { max-width: 150px; }

/* v1.28.24 - Der dezente DEV-Hinweis unten an der Versionszeile.
   Gleiche Groesse wie die Version selbst (er erbt sie), nur in Warnfarbe -
   "es soll nur so sein das man sieht 'oh das is noch an'". */
#version-info .dev-marke { color: #fde047; font-weight: 700; }
#version-info.dev-an { border-color: #eab308 !important; }

@media (max-width: 820px) {
  /* Auf dem Handy steht die Liste ueber dem Inhalt - dort sind die zwei
     Knoepfe breit genug fuer eine Zeile nebeneinander. */
  .sl-sichern { margin-bottom: 14px; }
  .sl-punkt .pfeil { display: block; }
}

/* ==========================================================================
   v1.11.0 - RAIDKARTE: SCHIEBEREGLER, VERLAUF, LUFT
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. SCHIEBEREGLER statt Haekchen
   --------------------------------------------------------------------------
   AUS: nur das rote Wort leuchtet. AN: die ganze Bahn glimmt gruen.

   Das Wort steht bewusst NICHT im HTML, sondern kommt aus ::after. Im
   ersten Entwurf stand es fest im Markup, waehrend der Zustand am Haekchen
   hing - zwei Wahrheiten, die auseinanderliefen (der Schalter zeigte "ON",
   obwohl er aus war). So kann das nicht mehr passieren.
   -------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------
   v1.25.0 - BEHOBEN: DER SCHALTER WAR IN EINEM .field ZERQUETSCHT.

   Nutzer-Befund (Screenshot): "Der On/Off Button ist irgendwie kaputt und
   falsch positioniert" - der weisse Knopf lag halb ueber der Beschriftung,
   das rote OFF stand LINKS AUSSERHALB des Kastens.

   URSACHE, im Browser nachgemessen: '.field > label { display: block }'
   (weiter oben in dieser Datei) hat die HOEHERE Spezifitaet (0,2,1) als
   '.swa' (0,1,0) und gewinnt deshalb - obwohl .swa spaeter steht.
   Ergebnis: display wurde block statt inline-flex, die Bahn schrumpfte von
   76px auf 2px, und das absolut positionierte OFF-Wort landete bei x=7
   statt im Schalter.

   Betroffen war genau EIN Schalter: der einzige, der in einem .field steht
   (Erkennung & Ampel). Deshalb ist es vorher niemandem aufgefallen.

   Behoben mit gleicher Spezifitaet statt mit !important, damit die Regel
   auch fuer kuenftige Schalter in .field-Kaesten traegt.
   --------------------------------------------------------------------------- */
.swa, .field > label.swa, .field label.swa {
  display: inline-flex; align-items: center; gap: 12px;
  cursor: pointer; user-select: none;
  padding: 4px 0;
  /* Die .field-Regeln setzen ausserdem Schriftgroesse, Farbe und einen
     unteren Abstand, die fuer eine Feld-Beschriftung gedacht sind. Der
     Schalter bringt seine eigenen mit (siehe .swa-text). */
  font-size: inherit; font-weight: inherit; color: inherit;
  margin-bottom: 0;
}
.swa input { position: absolute; opacity: 0; width: 0; height: 0; }

.swa-bahn {
  position: relative; flex: 0 0 auto;
  width: 76px; height: 32px; border-radius: 999px;
  background: var(--bg-elevated); border: 1px solid var(--border);
  transition: background .2s ease, border-color .2s ease, box-shadow .2s ease;
}
.swa-knopf {
  position: absolute; top: 2px; left: 2px;
  width: 26px; height: 26px; border-radius: 50%;
  background: #F5F3F9; box-shadow: 0 2px 5px rgba(0,0,0,.35);
  transition: left .2s ease;
}
.swa-wort {
  position: absolute; top: 50%; right: 10px; transform: translateY(-50%);
  font-family: ui-monospace, "JetBrains Mono", monospace;
  font-size: 10.5px; font-weight: 700; letter-spacing: .06em;
  color: var(--danger); text-shadow: 0 0 8px var(--danger);
  transition: color .2s ease, left .2s ease;
}
.swa-wort::after { content: "OFF"; }

.swa input:checked + .swa-bahn {
  background: rgba(34,197,94,.16); border-color: var(--success);
  box-shadow: 0 0 0 3px rgba(34,197,94,.12), inset 0 0 13px rgba(34,197,94,.26);
}
.swa input:checked + .swa-bahn .swa-knopf { left: 46px; }
.swa input:checked + .swa-bahn .swa-wort {
  left: 11px; right: auto;
  color: var(--success); text-shadow: 0 0 9px var(--success);
}
.swa input:checked + .swa-bahn .swa-wort::after { content: "ON"; }
.swa input:focus-visible + .swa-bahn { outline: 2px solid var(--accent); outline-offset: 3px; }

.swa-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.swa-text b { font-size: 14px; font-weight: 600; }
.swa-text span { font-size: 12px; color: var(--text-dim); line-height: 1.45; }

/* Gesperrt = es fehlt eine Voraussetzung. Sichtbar, aber sichtlich tot. */
.swa.gesperrt { cursor: not-allowed; opacity: .45; }
.swa.gesperrt .swa-wort { text-shadow: none; }

@media (prefers-reduced-motion: reduce) {
  .swa-bahn, .swa-knopf, .swa-wort { transition: none; }
}

/* --------------------------------------------------------------------------
   2. RAID-VERLAUF
   -------------------------------------------------------------------------- */
.rh-karte { display: flex; flex-direction: column; gap: 12px; }
.rh-kopf { display: flex; align-items: center; gap: 10px; }
.rh-kopf h2 { margin: 0; }
.rh-gesamt {
  padding: 2px 9px; border-radius: 999px;
  background: var(--border); color: var(--text-dim);
  font-size: 11.5px; font-weight: 700; font-variant-numeric: tabular-nums;
}
.rh-liste { display: flex; flex-direction: column; gap: 7px; }

.rh-eintrag {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 10px 13px;
  border: 1px solid var(--border); border-radius: 10px;
  background: var(--bg-elevated);
  font-size: 13.5px;
}
.rh-datum {
  flex: 0 0 auto; color: var(--text-dim);
  font-size: 12px; font-variant-numeric: tabular-nums;
  min-width: 108px;
}
.rh-mitte { flex: 1 1 220px; display: flex; align-items: center; gap: 9px; flex-wrap: wrap; min-width: 0; }
.rh-ziel { font-weight: 700; color: var(--accent); text-decoration: none; }
.rh-ziel:hover { text-decoration: underline; }
.rh-titel {
  color: var(--text-dim); font-style: italic;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 260px;
}
.rh-kat {
  padding: 2px 8px; border-radius: 999px;
  background: rgba(139,92,246,.16); color: var(--text-dim);
  font-size: 10.5px; white-space: nowrap;
}
/* "extern" = nicht über die App geraidet, sondern direkt bei Twitch. */
.rh-extern {
  padding: 2px 8px; border-radius: 999px;
  background: rgba(145,70,255,.18); color: var(--accent);
  font-size: 10px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
}
.rh-rechts { margin-left: auto; display: flex; align-items: center; gap: 10px; flex: 0 0 auto; }
.rh-zuschauer { font-size: 12px; font-weight: 600; font-variant-numeric: tabular-nums; }
.rh-durch { font-size: 11.5px; color: var(--text-dim); }

.rh-mehr { align-self: flex-start; }
.rh-rest { margin-top: 2px; }
.rh-zusammen { margin-top: 4px; }
.rh-zusammen summary { cursor: pointer; font-weight: 600; font-size: 13.5px; padding: 4px 0; }
.rh-zusammen .raid-historie { margin-top: 10px; }
.rh-anzahl-klein {
  padding: 1px 7px; border-radius: 999px;
  background: var(--border); color: var(--text-dim); font-size: 10.5px; font-weight: 700;
}

@media (max-width: 620px) {
  .rh-datum { min-width: 0; }
  .rh-rechts { margin-left: 0; width: 100%; }
  .rh-titel { max-width: none; }
}

/* --------------------------------------------------------------------------
   3. LUFT IN DER RAIDKARTE
   --------------------------------------------------------------------------
   Die Karte ist ueber Monate gewachsen; zuletzt klebten Schalter, Eingaben
   und aufklappbare Abschnitte aneinander. Hier bekommen die direkten
   Nachbarn einen einheitlichen Abstand - statt an jeder Stelle einzeln.
   -------------------------------------------------------------------------- */
.rv-karte { display: flex; flex-direction: column; gap: 0; }
.rv-karte > * + * { margin-top: 14px; }
.rv-karte > .rv-liste + .rv-modus,
.rv-karte > p.hint + .rv-modus { margin-top: 18px; }
.rv-karte > .swa { margin-top: 16px; }
.rv-karte > .rv-neu { margin-top: 16px; }
.rv-karte > details { margin-top: 16px; }
.rv-karte .rv-verwaltung { margin-top: 14px; }
/* v1.12.0: Die Stufe steht ganz oben und braucht darunter dieselbe Luft
   wie alles andere - und die Kanalpunkte-Karte den Abstand einer
   Klappkarte, nicht den einer Textzeile. */
.rv-karte > .rv-stufe + * { margin-top: 16px; }
.rv-karte > .rv-vorgemerkt { margin-top: 10px; }
.rv-karte > .rv-kp { margin-top: 16px; margin-bottom: 0; }
.rv-karte > .rv-kp + * { margin-top: 18px; }
/* v1.12.0: Zwei Altlasten, die sich zum Kartenabstand ADDIERT haben und
   dadurch aus dem 14/16/18-Raster ausbrachen: .rv-ergebnis bringt ein
   eigenes margin-bottom mit, und die letzte Klappkarte erbte zusaetzlich
   das margin-bottom von .settings-page-section. Beides hier abgeraeumt -
   den Abstand setzt die Karte, nicht das einzelne Stueck. */
.rv-karte > .rv-ergebnis { margin-bottom: 0; }
.rv-karte > .settings-page-section { margin-bottom: 0; }

/* Eingabezeile: Feld und Knopf gleich hoch, mit Luft dazwischen. */
.rv-neu { align-items: center; gap: 10px; }
.rv-neu input { min-height: var(--knopf-hoch, 36px); }
.rv-neu .btn { flex: 0 0 auto; }

/* ==========================================================================
   v1.12.0 - DIE STUFE VOR DER ABSTIMMUNG
   ==========================================================================
   Ganz oben in der Raidkarte steht jetzt, ob ueberhaupt ein Raid ansteht.
   Zwei Zustaende, die man aus zwei Metern erkennen soll - deshalb ein
   farbiger Punkt statt nur Text: RUHE ist grau und still, VORBEREITET
   leuchtet gruen. Ohne den Punkt muesste man lesen, um zu wissen, woran
   man ist.
   ========================================================================== */
.rv-stufe {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  padding: 14px 16px;
  border-radius: 12px;
  border: 1px solid var(--rand, rgba(255,255,255,.12));
  background: rgba(255,255,255,.03);
}
.rv-stufe.an {
  border-color: rgba(34,197,94,.45);
  background: rgba(34,197,94,.08);
}

.rv-stufe-lage { display: flex; align-items: center; gap: 12px; min-width: 0; }

/* Der Zustandspunkt. Bei "vorbereitet" pulsiert er leise - genug, um das
   Auge zu holen, wenig genug, um beim Arbeiten nicht zu nerven. */
.rv-stufe-punkt {
  flex: 0 0 auto;
  width: 12px; height: 12px;
  border-radius: 50%;
  background: rgba(255,255,255,.25);
}
.rv-stufe.an .rv-stufe-punkt {
  background: #22c55e;
  box-shadow: 0 0 0 4px rgba(34,197,94,.18);
  animation: rvPuls 2.4s ease-in-out infinite;
}
@keyframes rvPuls {
  0%, 100% { box-shadow: 0 0 0 4px rgba(34,197,94,.18); }
  50%      { box-shadow: 0 0 0 7px rgba(34,197,94,.06); }
}
/* Wer Bewegung abgestellt hat, bekommt sie auch hier nicht. */
@media (prefers-reduced-motion: reduce) {
  .rv-stufe.an .rv-stufe-punkt { animation: none; }
}

.rv-stufe-text { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.rv-stufe-text b { font-size: 1.02rem; }
.rv-stufe-text span {
  font-size: .86rem;
  color: var(--text-leise, rgba(255,255,255,.6));
  line-height: 1.35;
}
.rv-stufe .btn { flex: 0 0 auto; }

/* --------------------------------------------------------------------------
   Vorgemerkte Vorschlaege - was hereinkam, waehrend kein Raid lief.
   -------------------------------------------------------------------------- */
.rv-vorgemerkt {
  padding: 12px 14px;
  border-radius: 10px;
  border: 1px dashed var(--rand, rgba(255,255,255,.14));
  background: rgba(255,255,255,.02);
}
.rv-vorgemerkt-kopf {
  font-size: .8rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--text-leise, rgba(255,255,255,.55));
  margin-bottom: 8px;
}
.rv-vm-zeile {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 0;
  flex-wrap: wrap;
}
.rv-vm-zeile + .rv-vm-zeile { border-top: 1px solid rgba(255,255,255,.06); }
.rv-vm-art { flex: 0 0 auto; font-size: 1rem; }
.rv-vm-art.ziel { filter: drop-shadow(0 0 5px rgba(245,158,11,.55)); }
.rv-vm-zeile a { font-weight: 600; }
.rv-vm-zeit {
  margin-left: auto;
  font-size: .78rem;
  color: var(--text-leise, rgba(255,255,255,.45));
  font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   v1.12.0 - DIE KANALPUNKTE-KNOEPFE IN DER RAIDKARTE
   ==========================================================================
   Geschaltet wird HIER, nicht in den Einstellungen: beim Raiden will
   niemand erst durch drei Menues.
   ========================================================================== */
.rv-kp > summary { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.rv-kp-an,
.rv-kp-warn {
  font-size: .72rem;
  padding: 2px 8px;
  border-radius: 999px;
  letter-spacing: .04em;
  text-transform: uppercase;
}
.rv-kp-an   { background: rgba(34,197,94,.18);  color: #4ade80; }
.rv-kp-warn { background: rgba(245,158,11,.18); color: #fbbf24; }

/* Die Zeilen brauchen mehr Luft als der uebrige Kasteninhalt: ein Schieber
   ist ein Schalter, kein Listeneintrag - zwei davon direkt uebereinander
   liest man als Block statt als zwei Entscheidungen. */
.rv-kp-body { display: flex; flex-direction: column; gap: 14px; }
.rv-kp-body > .rv-kp-zeile + .rv-kp-zeile { margin-top: 2px; }
/* Der Hinweis darunter gehoert zu beiden - deshalb etwas abgesetzt. */
.rv-kp-body > p.hint { margin: 4px 0 0; }

.rv-kp-zeile { display: flex; align-items: center; gap: 14px; }
.rv-kp-zeile.fehlt { opacity: .6; }
.rv-kp-symbol { flex: 0 0 auto; font-size: 1.15rem; width: 24px; text-align: center; }
.rv-kp-zeile .swa { flex: 1 1 auto; min-width: 0; }
.rv-kp-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.rv-kp-text span { font-size: .84rem; color: var(--text-leise, rgba(255,255,255,.55)); }

/* Offene Erstattungen - der Teil, den man nicht uebersehen darf. */
.rv-kp-erstattung {
  margin-top: 2px;
  padding: 12px 14px;
  border-radius: 10px;
  border: 1px solid rgba(245,158,11,.35);
  background: rgba(245,158,11,.07);
}
.rv-kp-erstattung-kopf { font-size: .86rem; font-weight: 600; margin-bottom: 8px; }
.rv-kp-e-zeile {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 0;
  flex-wrap: wrap;
}
.rv-kp-e-zeile + .rv-kp-e-zeile { border-top: 1px solid rgba(255,255,255,.06); }
.rv-kp-e-zeile > span:first-child { font-weight: 600; }
/* Die beiden Knoepfe rechts, aber nicht aneinandergeklebt: "Erstatten" und
   "Behalten" sind gegensaetzliche Entscheidungen und brauchen den Abstand,
   damit man nicht danebengreift. */
.rv-kp-e-zeile .btn { margin-left: auto; }
.rv-kp-e-zeile .btn + .btn { margin-left: 8px; }

/* ==========================================================================
   v1.12.0 - EINRICHTUNG (Einstellungen -> Raids)
   ==========================================================================
   Hier steht nur, was man EINMAL festlegt. Bewusst schmal gehalten: ein
   Namensfeld muss nicht ueber die halbe Seite laufen.
   ========================================================================== */
.rkp-block {
  padding: 14px 16px;
  border-radius: 10px;
  border: 1px solid var(--rand, rgba(255,255,255,.12));
  background: rgba(255,255,255,.02);
}
.rkp-block + .rkp-block { margin-top: 14px; }
.rkp-block.angelegt { border-color: rgba(34,197,94,.3); }

.rkp-kopf { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 6px; }
.rkp-symbol { font-size: 1.1rem; }
.rkp-marke {
  margin-left: auto;
  font-size: .72rem;
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(255,255,255,.08);
  color: var(--text-leise, rgba(255,255,255,.6));
}
.rkp-block.angelegt .rkp-marke { background: rgba(255,255,255,.06); }

.rkp-felder { display: flex; gap: 12px; flex-wrap: wrap; }
.rkp-feld { display: flex; flex-direction: column; gap: 5px; flex: 1 1 220px; min-width: 0; }
.rkp-feld.schmal { flex: 0 0 130px; }
.rkp-feld > span {
  font-size: .8rem;
  color: var(--text-leise, rgba(255,255,255,.6));
}
.rkp-feld input { width: 100%; min-height: var(--knopf-hoch, 36px); }
/* Zwischen den Feldreihen und dem Knopf darunter Luft. */
.rkp-block > * + * { margin-top: 12px; }
.rkp-knoepfe { display: flex; gap: 10px; flex-wrap: wrap; }

/* Auf schmalen Schirmen bricht alles auf volle Breite um. */
@media (max-width: 560px) {
  .rkp-feld, .rkp-feld.schmal { flex: 1 1 100%; }
  .rv-stufe { align-items: stretch; }
  .rv-stufe .btn { width: 100%; }
}

/* ==========================================================================
   v1.12.1 - AUFGERAEUMTE EINSTELLUNGEN
   ========================================================================== */

/* Development-Modus in den Server-Einstellungen. Der Warnkasten war vorher
   mit festen Farben direkt im HTML gesetzt - hier gehoert er hin, damit er
   sich am Design mitdreht statt in jedem Farbschema gleich zu bleiben. */
.srv-dev { margin-top: 14px; }
.srv-dev-warn {
  margin-top: 12px;
  padding: 11px 14px;
  border-radius: 8px;
  border-left: 3px solid #ef4444;
  background: rgba(239,68,68,.08);
}
.srv-dev-warn strong { color: #fca5a5; font-size: .9rem; }
.srv-dev-warn p {
  margin: 5px 0 0;
  font-size: .84rem;
  line-height: 1.4;
  color: var(--text-dim, rgba(255,255,255,.72));
}

/* Die Kontrolleingabe beim Passwort: die Rueckmeldung steht dort, wo man
   ohnehin hinsieht - direkt unter dem zweiten Feld. */
.pw-form .field + .field { margin-top: 14px; }
.pw-form .btn { margin-top: 16px; }
.pw-gleich   { color: #4ade80; }
.pw-ungleich { color: #fbbf24; }

/* ==========================================================================
   v1.12.5 - ABGESCHNITTENE NAMEN IN DER DJ-ANZEIGE
   ==========================================================================
   Befund aus dem Betrieb: "kmrz_zugga_daddy" und "Luxemburger1982" waren am
   rechten Rand hart abgeschnitten - ohne "…", einfach weg.

   Ursache: .djview-from ist bewusst genau 116px breit (damit die Knöpfe in
   allen Zeilen an derselben Stelle stehen). Der Namens-Chip darin durfte
   aber bis 220px breit werden. Er lief also aus seiner Spalte heraus und
   wurde von deren "overflow: hidden" abgeschnitten. Die Ellipsis im Namen
   griff nie, weil der Chip selbst nie unter Druck geriet - er hielt sich ja
   für breit genug.

   Lösung: Der Chip darf in dieser Spalte nur so breit werden wie die Spalte
   selbst. Erst dann greift das text-overflow im Namen und macht aus dem
   harten Schnitt ein lesbares "kmrz_zugga_…". Der volle Name steht im
   Mouseover (title) - siehe wunschVonChipHtml.
   ========================================================================== */
.djview-from .wunschvon-chip {
  max-width: 100%;
  min-width: 0;
}
.djview-from .wunschvon-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

/* v1.12.5: Twitch trennen - zwei Spalten, damit "wird gelöscht" und
   "bleibt erhalten" nebeneinander stehen. Nebeneinander liest man den
   Unterschied; untereinander muss man ihn sich merken. */
.srv-trennen { margin-top: 16px; border-color: rgba(229,72,77,.3); }
.srv-trennen-was {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
  margin: 14px 0 16px;
}
.srv-trennen-was > div { min-width: 0; }
.srv-trennen-was b { font-size: .88rem; display: block; margin-bottom: 6px; }
.srv-trennen-was > div:first-child b { color: #ff8a8d; }
.srv-trennen-was > div:last-child  b { color: #4ade80; }
.srv-trennen-was ul { margin: 0; padding-left: 18px; }
.srv-trennen-was li {
  font-size: .84rem;
  line-height: 1.45;
  color: var(--text-dim, rgba(255,255,255,.7));
  margin-bottom: 4px;
}
.srv-trennen-feld { margin-bottom: 14px; }
.srv-trennen-feld code {
  padding: 1px 7px;
  border-radius: 5px;
  background: rgba(255,255,255,.09);
}
@media (max-width: 620px) {
  .srv-trennen-was { grid-template-columns: 1fr; gap: 14px; }
}

/* v1.12.5: "Kommt etwas an?" - Empfangszähler je Ereignisart. */
.ev-empfang { margin-top: 14px; }
.ev-empfang-tabelle { margin-top: 4px; }
.ev-empfang-tabelle td, .ev-empfang-tabelle th { padding: 7px 10px; }
/* Eine 0 bei aktivem Abo ist der interessante Fall - der soll auffallen. */
.ev-null { color: #fbbf24; }

/* v1.12.2: Die Kanalsuche am Twitch-Namen eines Team-Kontos.
   .rv-suchfeld bringt aus der Raidkarte ein "flex: 1 1 200px" mit - dort
   steht es in einer Zeile neben Kommentarfeld und Knopf. Im Team-Formular
   steht es allein untereinander und soll die volle Breite nutzen. */
.field > .rv-suchfeld { flex: 1 1 auto; width: 100%; }

/* ==========================================================================
   v1.13.0 - WARNSTUFEN UND NAMENSHISTORIE BEI PERSONEN
   ==========================================================================
   Zwei Symbole, die sofort auffallen sollen: eine Warnstufe und der Hinweis,
   dass jemand schon einmal anders hieß. Bewusst Symbol UND Farbe - Farbe
   allein reicht nicht (Rot-Grün-Schwäche betrifft rund jeden zwölften Mann),
   Symbol allein ginge in einer vollen Zeile unter.
   ========================================================================== */
.in-warn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; flex: 0 0 auto;
  border-radius: 6px; font-size: 12px; line-height: 1;
}
.in-warn-beobachten { background: rgba(245,158,11,.22); color: #fbbf24; }
.in-warn-vorsicht   { background: rgba(239,68,68,.24);  color: #fca5a5; }

.in-umbenannt {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; flex: 0 0 auto;
  border-radius: 6px; font-size: 11px; line-height: 1;
  background: rgba(139,92,246,.2); color: #c4b5fd;
}

/* Erster und aktueller Name nebeneinander. */
.in-namen {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin: 4px 0 10px; font-size: .86rem;
}
.in-namen-alt   { text-decoration: line-through; opacity: .6; }
.in-namen-pfeil { opacity: .5; }
.in-namen-neu   { font-weight: 700; }
.in-namen .hint { margin: 0; font-size: .78rem; }

/* Die Leiste zum Setzen der Warnstufe. */
.in-warnleiste {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 11px 13px; margin-bottom: 12px;
  border-radius: 10px; border: 1px solid var(--border, rgba(255,255,255,.12));
  background: rgba(255,255,255,.03);
}
.in-warnleiste-marke {
  font-size: .74rem; text-transform: uppercase; letter-spacing: .06em;
  opacity: .6; margin-right: 2px;
}
.in-warn-knopf {
  padding: 5px 11px; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--border, rgba(255,255,255,.16));
  background: rgba(255,255,255,.05); color: inherit;
  font: inherit; font-size: .8rem; white-space: nowrap;
}
.in-warn-knopf:hover { background: rgba(255,255,255,.1); }
.in-warn-knopf.aktiv.w-beobachten {
  background: rgba(245,158,11,.2); border-color: rgba(245,158,11,.55); color: #fbbf24;
}
.in-warn-knopf.aktiv.w-vorsicht {
  background: rgba(239,68,68,.2); border-color: rgba(239,68,68,.55); color: #fca5a5;
}
.in-warn-knopf.aktiv.w-keine {
  background: rgba(255,255,255,.14); border-color: rgba(255,255,255,.3);
}
.in-warn-grund { flex: 1 1 200px; min-width: 0; }

/* Automatisch eingetragen (Bann, Timeout, Umbenennung). */
.in-auto { font-size: .78rem; opacity: .75; }

/* Kategorie am EINZELNEN Eintrag - klein, damit sie die Zeile nicht sprengt. */
.in-eintrag-kat {
  font-size: .74rem; padding: 1px 5px; max-width: 110px;
  border-radius: 6px;
}
.in-kat-chip {
  font-size: .72rem; padding: 1px 7px; border-radius: 999px;
  background: rgba(255,255,255,.08); opacity: .8; white-space: nowrap;
}
.in-kat-chip.klein { font-size: .68rem; padding: 0 6px; }

/* In der Karte sollen Name und Symbole eine Zeile bilden. */
.in-chip-kopf .in-warn, .in-chip-kopf .in-umbenannt { margin-right: 2px; }

/* ==========================================================================
   v1.13.0 - DIE PERSONEN-AKTE
   ==========================================================================
   Links die Liste, rechts die Akte. Zwei Spalten, weil man beim Durchgehen
   mehrerer Personen sonst ständig zwischen zwei Ansichten springen müsste.
   ========================================================================== */

/* Umschalter zwischen "Chat & Notizen" und "Personen". */
.in-unterseiten { display: flex; gap: 8px; margin-bottom: 14px; flex-wrap: wrap; }
.in-unterseite {
  padding: 8px 16px; border-radius: 10px; cursor: pointer;
  border: 1px solid var(--border, rgba(255,255,255,.14));
  background: rgba(255,255,255,.04); color: inherit;
  font: inherit; font-size: .9rem; font-weight: 600;
}
.in-unterseite:hover { background: rgba(255,255,255,.09); }
.in-unterseite.aktiv {
  background: var(--accent, #8b5cf6); border-color: var(--accent, #8b5cf6); color: #fff;
}

.pa-layout {
  display: grid;
  grid-template-columns: minmax(240px, 340px) 1fr;
  gap: 16px;
  align-items: start;
}

/* ---- Liste links ---- */
.pa-liste-kopf { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
.pa-liste-kopf h2 { margin: 0; }
.pa-suche { flex: 1 1 130px; min-width: 0; }
.pa-zeilen { display: flex; flex-direction: column; gap: 2px; max-height: 62vh; overflow-y: auto; }
.pa-zeile {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 7px 9px; border-radius: 8px; cursor: pointer;
  border: 1px solid transparent; background: none; color: inherit;
  font: inherit; text-align: left;
}
.pa-zeile:hover { background: rgba(255,255,255,.06); }
.pa-zeile.aktiv { background: rgba(139,92,246,.18); border-color: rgba(139,92,246,.5); }
.pa-kein-warn { width: 20px; flex: 0 0 auto; }
.pa-name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.pa-arten { display: inline-flex; gap: 2px; flex: 0 0 auto; }
.pa-art { font-size: .8rem; }
.pa-notizen {
  font-size: .7rem; padding: 0 6px; border-radius: 999px;
  background: rgba(255,255,255,.1); flex: 0 0 auto;
}
.pa-zeit {
  margin-left: auto; font-size: .74rem; opacity: .5;
  white-space: nowrap; flex: 0 0 auto; font-variant-numeric: tabular-nums;
}

/* ---- Akte rechts ---- */
.pa-akte-kopf h2 { display: flex; align-items: center; gap: 9px; margin: 0 0 4px; flex-wrap: wrap; }
.pa-warnung { color: #fbbf24; margin-top: 6px; }

/* Kennzahlen: nur was tatsächlich vorkommt, sonst stehen dort lauter Nullen. */
.pa-kacheln { display: flex; gap: 9px; flex-wrap: wrap; margin: 14px 0; }
.pa-kachel {
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  min-width: 62px; padding: 8px 12px; border-radius: 9px;
  border: 1px solid var(--border, rgba(255,255,255,.12));
  background: rgba(255,255,255,.03);
}
.pa-kachel b { font-size: 1.12rem; font-variant-numeric: tabular-nums; }
.pa-kachel span { font-size: .72rem; opacity: .65; white-space: nowrap; }

.pa-leiste { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
.pa-leiste-marke {
  font-size: .72rem; text-transform: uppercase; letter-spacing: .06em; opacity: .55;
}
.pa-leiste .btn.aktiv {
  background: rgba(139,92,246,.2); border-color: rgba(139,92,246,.5);
}

/* ---- Die Zeitleiste ---- */
.pa-zeitleiste { display: flex; flex-direction: column; max-height: 58vh; overflow-y: auto; }
.pa-eintrag {
  display: flex; align-items: baseline; gap: 9px;
  padding: 7px 4px; flex-wrap: wrap;
  border-bottom: 1px solid rgba(255,255,255,.05);
}
.pa-eintrag:last-child { border-bottom: 0; }
.pa-e-zeit {
  font-size: .76rem; opacity: .5; flex: 0 0 auto;
  font-variant-numeric: tabular-nums; min-width: 118px;
}
.pa-e-sym { flex: 0 0 auto; }
.pa-e-text { flex: 1 1 200px; min-width: 0; }
.pa-e-von { font-size: .76rem; opacity: .55; }
.pa-e-test {
  font-size: .66rem; padding: 0 6px; border-radius: 999px;
  background: rgba(255,255,255,.1); opacity: .7;
}
/* Vorfälle stechen heraus - genau deshalb schlägt man die Akte auf. */
.pa-art-bann    { background: rgba(239,68,68,.07); }
.pa-art-timeout { background: rgba(245,158,11,.06); }
.pa-art-bann .pa-e-text    { color: #fca5a5; }
.pa-art-timeout .pa-e-text { color: #fbbf24; }
.pa-eintrag.auto .pa-e-text { font-style: italic; opacity: .9; }

/* ---------------------------------------------------------------------------
   v1.31.22 - Verwarnung und die aufklappbaren Details.

   Die Verwarnung liegt zwischen Timeout und Bann, und genau so sieht sie
   aus: derselbe Bau wie die beiden, eine Spur ruhiger.

   Die Details sind ZUGEKLAPPT - Saschas Vorgabe: "muss nicht direkt mit
   eingeblendet werden aber in die details". Der Aufklapper nimmt die ganze
   Breite unter dem Eintrag, damit er die Zeile nicht auseinanderzieht.
   --------------------------------------------------------------------------- */
.pa-art-verwarnung    { background: rgba(245,158,11,.04); }
.pa-art-verwarnung .pa-e-text { color: #fcd34d; }

.pa-detail { flex: 1 0 100%; margin: 4px 0 0 0; }
.pa-detail > summary {
  cursor: pointer; font-size: .76rem; opacity: .65;
  list-style: none; padding: 2px 0; width: fit-content;
}
.pa-detail > summary::-webkit-details-marker { display: none; }
.pa-detail > summary:hover { opacity: 1; }
.pa-detail[open] > summary { opacity: .9; }
.pa-detail-liste {
  display: flex; flex-direction: column; gap: 2px;
  margin: 4px 0 0 0; padding: 6px 9px;
  border-left: 2px solid rgba(255,255,255,.14);
  background: rgba(255,255,255,.03); border-radius: 0 6px 6px 0;
}
.pa-detail-zeile { display: flex; gap: 8px; align-items: baseline; font-size: .8rem; }
.pa-detail-zeit {
  flex: 0 0 auto; opacity: .45; font-size: .72rem;
  font-variant-numeric: tabular-nums; min-width: 52px;
}
.pa-detail-text { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; opacity: .75; }
/* Die letzte Zeile vor dem Vorfall ist die, die man sucht. */
.pa-detail-zeile.letzte .pa-detail-text { opacity: 1; color: var(--text, #e7e7ea); }
.pa-detail-hinweis { margin: 5px 0 0 0; font-size: .7rem; }

/* ---------------------------------------------------------------------------
   v1.31.22 - Stream Together: die Kanäle der laufenden Runde.
   --------------------------------------------------------------------------- */
.zs-kanaele { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.zs-kanal {
  padding: 2px 10px; border-radius: 999px; font-size: .82rem;
  background: rgba(139,92,246,.14); border: 1px solid rgba(139,92,246,.4);
}

@media (max-width: 900px) {
  .pa-layout { grid-template-columns: 1fr; }
  .pa-zeilen { max-height: 34vh; }
  .pa-e-zeit { min-width: 0; }
}

/* ==========================================================================
   v1.13.2 - KOPFZEILE: PLATZ SCHAFFEN
   ==========================================================================
   Drei zusammenhängende Änderungen. Der Abmelde-Knopf trug das Wort
   "Logout" quer durch die Zeile; die Breite fehlte dem Namen und der
   Verbindungsanzeige. Ein Symbol sagt dasselbe auf einem Viertel des
   Platzes - und der gewonnene Raum geht an die Dinge, die man während der
   Sendung wirklich liest.
   ========================================================================== */

/* Zuschauerzahl im LIVE-Chip - klein, direkt hinter der Sendedauer. */
.live-zuschauer {
  font-weight: 700;
  font-size: .92em;
  opacity: .92;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* Der Abmelde-Knopf als Symbol. Quadratisch, damit er neben den anderen
   Symbolknöpfen der Kopfzeile nicht aus der Reihe fällt. */
.kopf-abmelden {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px;
  padding: 0;
  flex: 0 0 auto;
  border-radius: 8px;
}
.kopf-abmelden svg { display: block; }
/* Beim Überfahren rot: Abmelden ist ein Abbruch, kein Weiterklicken. */
.kopf-abmelden:hover {
  color: #ff8a8d;
  border-color: rgba(229,72,77,.5);
  background: rgba(229,72,77,.12);
}

/* Die Uhr darf kleiner sein - sie wird gelesen, nicht gesucht. */
.kopf-uhr { font-size: 12px; padding: 2px 7px; }

/* Name und Rolle rücken näher zusammen und geben Breite ab. */
.kopf-rechts { gap: 8px; }
.kopf-user .ku-name { font-size: .94em; }
.kopf-user .ku-rolle { font-size: .72em; }

/* --------------------------------------------------------------------------
   DJ-Anzeige: Sendedauer und echte Uhr untereinander.
   Nebeneinander brauchten sie Breite, die dort knapp ist - und inhaltlich
   gehören sie zusammen: oben wie lange läuft die Sendung, darunter wie spät
   es ist. Ohne LIVE-Chip steht die Uhr allein und braucht nicht mehr Platz
   als vorher.
   -------------------------------------------------------------------------- */
.djview-zeiten {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 3px;
  flex: 0 0 auto;
}
.djview-zeiten .kopf-uhr {
  font-size: 11.5px;
  padding: 1px 6px;
}
.djview-zeiten .live-chip { font-size: .7em; }

/* Auf schmalen Schirmen wieder nebeneinander - dort ist Höhe knapper. */
@media (max-width: 700px) {
  .djview-zeiten { flex-direction: row; align-items: center; gap: 6px; }
}

/* ==========================================================================
   v1.13.3 - DJ-ANZEIGE: TITEL UND INTERPRET WIEDER LESBAR
   ==========================================================================
   Befund aus dem Betrieb: Auf schmalen Fenstern stand in der Zeile nur noch
   "L... 🎤 G" - Titel und Interpret waren nicht mehr zu erkennen. Ursache:
   Beide teilten sich EINE Zeile mit Griff, Cover, Kategorie, Namens-Chip
   und vier Knöpfen. Was übrig blieb, war zu wenig zum Lesen.

   Lösung in drei Schritten:
     1. Titel und Interpret stehen jetzt ÜBEREINANDER statt nebeneinander -
        beide bekommen die volle verbleibende Breite.
     2. Unter 700px rutscht der Textblock in eine eigene Zeile über die
        ganze Breite; dort ist seitlich nichts mehr zu holen.
     3. Griff, Kategorie und Cover geben Breite ab (siehe unten).

   Die Positionsnummer bleibt in allen Fällen rechts außen.
   ========================================================================== */

/* Der Textblock: Titel oben, Interpret darunter. */
.djview-row .djview-main {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;          /* ohne das kürzt flex gar nicht erst */
  flex: 1 1 auto;
}
/* v1.14.5: center statt baseline. Mit baseline richtet sich die Zeile am
   hoechsten Element aus - und das Mikrofon-Emoji bringt eine deutlich
   hoehere Zeilenbox mit als der Text. Nachgemessen bei 700px: 33 statt 16
   Pixel, wodurch die ganze Zeile 58 statt 43 Pixel hoch wurde. Das war die
   letzte der ungewollten Zwischenstufen. */
.djview-row .djview-song {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
.djview-row .row-title {
  font-weight: 700;
  font-size: 13.5px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.djview-artist-zeile {
  display: flex;
  align-items: center;   /* v1.14.5 - siehe Begruendung oben */
  gap: 5px;
  min-width: 0;
  line-height: 1.25;
}
.djview-artist-zeile .row-artist {
  font-size: 11.5px;
  color: var(--text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.djview-artist-zeile .mini-icon { font-size: 10px; flex: 0 0 auto; }

/* Die Zeile darf jetzt zwei Textzeilen hoch werden. */
.djview-row { min-height: 38px; padding-top: 3px; padding-bottom: 3px; }

/* --------------------------------------------------------------------------
   Der Ziehgriff ganz an den linken Rand.
   Er stand mit Innenabstand mitten in der Zeile und kostete dort Breite,
   die dem Titel fehlte. Angefasst wird er trotzdem gleich gut - die
   Trefferfläche bleibt gleich hoch, nur schmaler.
   -------------------------------------------------------------------------- */
.djview-row { padding-left: 4px; }
.djview-row .drag-handle {
  padding: 2px 1px;
  font-size: 12px;
  opacity: .45;
  flex: 0 0 auto;
}
.djview-row .drag-handle:hover { opacity: 1; }

/* --------------------------------------------------------------------------
   Kategorie-Chip: FESTE Breite.
   Ohne sie ist "PSST" breiter als "STD", und dann beginnt jedes Cover an
   einer anderen Stelle - die Spalte franst aus. Mit fester Breite starten
   alle Cover exakt auf derselben Linie, egal wie die Kategorie heißt.
   (Die 88px-Regel weiter oben wird hier bewusst überschrieben: sie stammt
   aus der Zeit, als der Chip die erste Spalte war und mehr Raum hatte.)
   -------------------------------------------------------------------------- */
.djview-row .row-cat {
  flex: 0 0 52px;
  width: 52px;
  min-width: 52px;
  font-size: 9.5px;
  padding: 3px 4px;
  text-align: center;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Das Cover sitzt jetzt hinter der Kategorie und braucht keinen eigenen
   Abstand mehr - die feste Chipbreite davor richtet es aus. */
.djview-row .djview-icon-gutter { flex: 0 0 auto; }

/* --------------------------------------------------------------------------
   Unter 700px: der Textblock bekommt eine eigene Zeile über die volle Breite.
   Am Rechner ist die gestapelte Form platzsparender; auf dem Handy ist
   seitlich schlicht nichts mehr zu holen, dort zählt nur noch Lesbarkeit.
   Die Positionsnummer bleibt auch hier rechts außen.
   -------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------
   v1.14.0 - UMBRUCH NICHT MEHR PER FESTER PIXELGRENZE.
   Vorher stand hier @media (max-width: 700px). Das hatte zwei Nachteile:
     1. Die 700 waren fest verdrahtet und nicht einstellbar.
     2. Die Liste wird per CSS "zoom" skaliert. Eine Media-Query misst aber
        IMMER die echte Fensterbreite, nie die skalierte. Bei 80 % Zoom war
        also real noch massig Platz da, und es wurde trotzdem schon
        umgebrochen - bei 130 % umgekehrt viel zu spaet.
   Jetzt setzt JavaScript die Klasse .djview-schmal auf die Tabelle, sobald
   die VERFUEGBARE Breite geteilt durch den Zoomfaktor unter die eingestellte
   Grenze faellt (siehe djUmbruchPruefen in app.js). Damit wirkt die Grenze
   an jeder Zoomstufe gleich - und ist unter Ansicht frei einstellbar.
   -------------------------------------------------------------------------- */
.djview-table.djview-schmal .djview-row {
  flex-wrap: wrap;
  row-gap: 2px;
  padding-bottom: 5px;
}
/* Der Textblock springt in die zweite Reihe und nimmt sie ganz ein. */
/* Gilt nur noch fuer NOTIZ-Zeilen - Wunschzeilen sind seit v1.14.5 ein
   Raster und kennen kein .djview-main mehr. */
.djview-table.djview-schmal .djview-row-note .djview-main {
  order: 10;
  flex: 1 0 100%;
  width: 100%;
  padding-left: 2px;
}
.djview-table.djview-schmal .djview-row .row-title { font-size: 14px; white-space: normal; }
.djview-table.djview-schmal .djview-artist-zeile .row-artist { font-size: 12px; }
/* Der rechte Block (Name + Knoepfe) bleibt in der oberen Reihe und bleibt
   dort RECHTSBUENDIG - auch wenn er selbst intern umbricht. Vorher rutschten
   die Knoepfe bei langen Namen nach unten LINKS (Nutzer-Screenshot). */
.djview-table.djview-schmal .djview-row .djview-rechts {
  order: 5;
  margin-left: auto;
}
/* Die Positionsnummer bleibt rechts aussen - in BEIDEN Groessen.
   Sie ist der Anker, an dem man die Reihenfolge abliest; springt sie je
   nach Fensterbreite woanders hin, sucht man sie jedes Mal neu. */
/* ===== VERALTET ab v1.14.5 =====
   Der Kasten .djview-rechts gibt es im Markup nicht mehr - die Zeile ist
   jetzt selbst ein Raster (siehe ganz unten, v1.14.5). Die Regeln bleiben
   nur stehen, falls ein alter Browser-Zwischenstand sie noch braucht; sie
   greifen ins Nichts, weil das Element fehlt.
   ===============================
   ---- Die Positionsnummer bekommt ein eigenes RASTER statt Flex-Umbruch.
   Zwei Anlaeufe haben hier nicht gereicht, deshalb der Wechsel der Technik:
   Bei flex-wrap entscheidet jedes Element FUER SICH, ob es umbricht. Genau
   das war der Befund aus dem Betrieb - bei kurzem Namen ("nacktluxi")
   passten Name und Knoepfe in die erste Reihe und die Nummer fiel allein
   in eine zweite; bei langem Namen ("kmrz_zugga_daddy") brachen die
   Knoepfe um und die Nummer stand daneben. Jede Zeile sah anders aus.

   Mit einem festen Raster gibt es nichts mehr zu entscheiden: Spalte 1
   nimmt Name und Knoepfe (darf intern umbrechen), Spalte 2 ist allein fuer
   die Nummer reserviert und immer gleich breit. Nutzer-Vorgabe:
   "die Nummerierung darf nicht untergehen! diese ist wie gesagt wichtiger"
   ---- */
.djview-table.djview-schmal .djview-row .djview-rechts {
  order: 5;
  /* Nimmt den ganzen Rest der Reihe - dadurch faellt der Textblock
     darunter, aber Nummer und Knoepfe bleiben zusammen oben. */
  flex: 1 1 auto;
  min-width: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  column-gap: 6px;
  row-gap: 3px;
  justify-items: end;
}
/* Name und Knopfleiste teilen sich die linke Spalte und stapeln sich dort,
   wenn es eng wird - beide rechtsbuendig (Nutzer-Wunsch). */
.djview-table.djview-schmal .djview-row .djview-rechts .djview-from,
.djview-table.djview-schmal .djview-row .djview-rechts .djview-actions {
  grid-column: 1;
  justify-self: end;
  max-width: 100%;
}
/* Die Nummer steht in der rechten Spalte und ueberspannt beide Zeilen -
   dadurch sitzt sie mittig neben Name UND Knoepfen und kostet keine eigene
   Zeilenhoehe mehr. */
.djview-table.djview-schmal .djview-row .pos-badge,
.djview-table.djview-schmal .djview-row .pos-badge-lg {
  grid-column: 2;
  grid-row: 1 / -1;
  align-self: stretch;
  flex: 0 0 auto;
  margin-left: 0;
}

/* ==========================================================================
   v1.14.0 - DJ-ANZEIGE: RECHTER BLOCK, KLEINERES COVER, WARNUNG IN DER ZEILE
   ==========================================================================
   Befund aus dem Betrieb (Nutzer-Screenshot vom 28.08., iPhone 13): Jede
   Zeile sah anders aus. Ursache waren DREI getrennte Flex-Kaesten rechts -
   Name, Knopfleiste, Positionsnummer -, die beim Umbruch jeder fuer sich
   entschieden haben, wohin sie rutschen:
     - kurzer Name  -> Name + Knoepfe passten zusammen in die erste Reihe,
                       die Nummer wurde ALLEIN in eine zweite gedrueckt
                       (eine komplette Zeilenhoehe fuer eine Zahl).
     - langer Name  -> die Knoepfe rutschten nach unten LINKS.
   Jetzt sind Name und Knoepfe EIN Block (.djview-rechts), der als Ganzes
   umbricht und dabei rechtsbuendig bleibt. Die Positionsnummer steht
   bewusst DANEBEN und nicht darin: sie ist die wichtigste Angabe der Zeile
   und darf nie mit umbrechen oder untergehen.
   ========================================================================== */

.djview-rechts {
  display: flex;
  align-items: center;
  justify-content: flex-end;   /* rechtsbuendig, auch beim internen Umbruch */
  flex-wrap: wrap;
  gap: 4px 6px;
  /* WICHTIG: schrumpfen erlaubt, aber NICHT wachsen - der Block darf nie so
     breit werden, dass die Positionsnummer daneben keinen Platz mehr hat
     und in eine eigene Zeile faellt. Genau das war der Fehler im ersten
     Anlauf: Name + Knoepfe fuellten die Reihe, die Nummer rutschte allein
     nach unten (siehe Screenshot-Befund). */
  flex: 0 1 auto;
  min-width: 0;
  margin-left: auto;           /* schiebt den Block an den rechten Rand */
}

/* Innerhalb des Blocks duerfen Name und Knoepfe ihre feste Spaltenbreite
   verlieren - die Ausrichtung uebernimmt jetzt der Block selbst. Feste
   Breiten hier wuerden bei schmalen Fenstern nur unnoetig Platz belegen. */
.djview-rechts .djview-from {
  flex: 0 1 auto;
  width: auto;
  max-width: 190px;
  justify-content: flex-end;
}
.djview-rechts .djview-actions {
  flex: 0 0 auto;
  width: auto;
  justify-content: flex-end;
}

/* Die Positionsnummer bekommt eine feste, geschuetzte Spalte ganz rechts.
   Nutzer-Vorgabe: "die Nummerierung darf nicht untergehen! diese ist wie
   gesagt wichtiger" - deshalb flex-shrink:0 und eine Mindestbreite, die
   auch dreistellige Positionen (#100) noch ohne Umbruch traegt. */
.djview-row .pos-badge-lg {
  flex: 0 0 auto;
  margin-left: 6px;
}
/* Am Monitor (breite Ansicht) bleibt der rechte Block eine schlichte Reihe:
   Name, Knoepfe, Nummer nebeneinander - dort ist Platz genug, ein Raster
   waere unnoetig. Das Raster oben greift nur im schmalen Modus. */
.djview-row .djview-rechts .pos-badge-lg { margin-left: 2px; }

/* -- Cover in der DJ-Anzeige kleiner (spart Hoehe). Listen/Musikfeed
      bleiben ausdruecklich unveraendert - nur .djview-cover. -- */
.djview-row .djview-cover,
.djview-row .djview-icon-wish img.wish-cover {
  width: 26px;
  height: 26px;
}

/* -- Variante C: Warnung neben dem Interpreten, faellt bei Enge in eine
      eigene Zeile. Der Interpret darf dabei nicht auf ein paar Buchstaben
      zusammenschrumpfen, deshalb eine Mindestbreite, ab der stattdessen
      umgebrochen wird. -- */
/* ===== ERSETZT in v1.14.5 =====
   Hier stand bis v1.14.0:
       .djview-artist-zeile   { flex-wrap: wrap; row-gap: 2px; }
       .../.row-artist        { flex: 1 1 auto; min-width: 8ch; }
       .../.restricted-badge  { flex: 1 1 22ch; min-width: 22ch; }
   Das war die Fassung, in der die Warnung noch IN der Interpretenzeile
   sass. Die beiden min-width-Angaben waren dabei der eigentliche Fehler:
   Ein Interpret wie "Vicky Leandros, Claude Denjean" konnte damit nicht
   unter 8 Zeichen schrumpfen, brach also um - und die Zeile wuchs von 43
   auf 58 Pixel, ganz ohne Warnung. Genau so entstanden die ungewollten
   Zwischenstufen bei rund 700 und 800 Pixeln.
   Seit v1.14.5 hat die Warnung ein eigenes Rasterfeld; die Regeln unten
   (Abschnitt v1.14.5) legen die Interpretenzeile fest.
   =============================== */

/* ==========================================================================
   v1.14.0 - DJ-KOPFLEISTE SCHMALER
   ==========================================================================
   Nutzer-Befund (Screenshot iPhone 13): Die Kopfleiste nahm fast ein
   Fuenftel des Bildes ein. Zwei Ursachen:
     1. "DJ-Anzeige · Eaglelator" stand in EINER Zeile und ist dort
        umgebrochen - unkontrolliert, mitten im Wort ("DJ-" / "Anzeige ·" /
        "Eaglelator"), also drei Zeilen hoch.
     2. Zoom-Regler, Prozentanzeige und die beiden +/- Knoepfe standen mit
        grossen Abstaenden nebeneinander und trieben die Leiste breit.
   Jetzt: Titel und Name stehen bewusst UNTEREINANDER (statt zufaellig
   umzubrechen), und die Zoom-Gruppe wird zusammengezogen.
   ========================================================================== */

.djview-topbar {
  gap: 6px 8px;
  padding: 5px 10px;
  /* Umbruch erlauben: ohne das haben sich Titel, Uhr und Regler auf dem
     Handy UEBERLAPPT (Pruefung 28.08. bei 390px) statt untereinander zu
     rutschen. align-items:center bleibt, damit die Teile in ihrer Reihe
     mittig sitzen. */
  flex-wrap: wrap;
}
/* Die rechte Gruppe darf umbrechen und bleibt dabei rechtsbuendig. */
.djview-topbar-right { flex-wrap: wrap; justify-content: flex-end; }

/* Titel und Name gestapelt - der Name klein darunter. Das ist die gleiche
   Hoehe wie vorher der unkontrollierte Umbruch, aber lesbar sortiert. */
.djview-topbar .brand {
  flex-direction: column;
  align-items: flex-start;
  gap: 0;
  line-height: 1.15;
  min-width: 0;
  font-size: 13px;
}
.djview-topbar .brand > * { min-width: 0; overflow: hidden; text-overflow: ellipsis; }

/* Die Zoom-Gruppe enger: kleinere Abstaende, schmalere Prozentanzeige,
   die +/- Knoepfe direkt am Regler statt mit Luft dazwischen. */
.djview-topbar-right { gap: 8px; }
.djview-scale-fader { gap: 3px; }
.djview-scale-readout {
  min-width: 3.4em;
  text-align: right;
  font-variant-numeric: tabular-nums;   /* springt beim Zaehlen nicht */
  font-size: 11px;
}
.djview-scale-knopf {
  padding: 0 6px;
  line-height: 1;
  min-width: 22px;
}

/* Auf schmalen Schirmen wird die Kopfleiste sonst dreistoeckig (gemessen:
   107px auf 390px Breite - fast ein Achtel des Bildes fuer eine Leiste).
   Deshalb dort: Regler schmaler, Prozentanzeige und "Verlassen" auf das
   Noetigste, damit alles in EINE Reihe neben den Titel passt. */
@media (max-width: 560px) {
  .djview-topbar { padding: 4px 8px; gap: 4px 6px; }
  .djview-topbar .brand { font-size: 12px; }
  .djview-topbar .brand-name { font-size: 10px; }
  .djview-topbar-right { gap: 5px; }
  .djview-scale-track-wrap { width: 46px; }
  .djview-scale-readout { min-width: 2.8em; font-size: 10px; }
  .djview-scale-knopf { min-width: 20px; padding: 0 4px; }
  /* Der Verlassen-Knopf braucht auf dem Handy keinen ausgeschriebenen Text -
     das Kreuz allein ist eindeutig und spart eine ganze Umbruchstufe. */
  .djview-topbar .leave { padding: 3px 7px; font-size: 11px; }
  .djview-zeiten { font-size: 11px; }
}
.djview-topbar .brand-titel { white-space: nowrap; font-weight: 800; }
.djview-topbar .brand-name {
  font-size: 11px;
  font-weight: 600;
  opacity: .75;
  white-space: nowrap;
  max-width: 100%;
}

/* ==========================================================================
   v1.14.0 - TEAM-MITGLIEDER: KOMPAKTE ZEILE, GRUPPIERT, DEZENTE ROLLENFARBEN
   ==========================================================================
   Nutzer-Befund: "Die Uebersicht auf der Team Mitglieder Karte ist recht
   schlecht." Jede Zeile war vier bis fuenf Zeilen hoch, weil Name, Rolle,
   Twitch-Name und der ausgeschriebene Rechte-Hinweis alle NEBENeinander
   standen und dabei umbrachen. Jetzt stehen die Hinweise UNTER dem Namen,
   die Knoepfe sind kleiner, und die Liste ist nach Rolle gruppiert.

   Zu den Farben ausdruecklich der Nutzer-Wunsch: "keine extrem knalligen
   Farben". Deshalb gedeckte Toene mit niedriger Saettigung - erkennbar
   unterschiedlich, aber nichts, was aus der Karte heraussticht. Die Flaeche
   ist bewusst nur leicht getoent, die Farbe traegt der Text und der Rand.
   ========================================================================== */

/* Gruppen-Ueberschrift (Admin / DJ / Moderator / ...) */
.team-gruppe {
  margin: 14px 0 4px;
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--text-dim);
  opacity: .8;
  border-bottom: 1px solid var(--border);
  padding-bottom: 3px;
}
.team-gruppe:first-child { margin-top: 2px; }

.user-row {
  align-items: center;
  gap: 10px;
  padding: 6px 10px;
}
.user-haupt { min-width: 0; flex: 1 1 auto; display: flex; flex-direction: column; gap: 2px; }
.user-kopf { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; min-width: 0; }
.user-kopf strong { font-size: 13.5px; }
/* Die Hinweise unter dem Namen: klein, gedaempft, in EINER Zeile. */
.user-unten {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  font-size: 11px;
  color: var(--text-dim);
  line-height: 1.3;
}
.user-unten .user-twitch { font-size: 11px; padding: 1px 5px; }
.user-twitch-fehlt { opacity: .7; font-style: italic; }
.user-rechte { opacity: .75; }

/* Knopfgruppe rechts - kleiner als vorher (Nutzer-Wunsch "Knoepfe kleiner"). */
.user-knoepfe { display: flex; gap: 4px; flex: 0 0 auto; align-items: center; }
.btn.tiny {
  padding: 3px 8px;
  font-size: 11px;
  line-height: 1.25;
  border-radius: 6px;
  min-height: 0;
}

/* ---- Rollen-Chips: je Rolle eine eigene, DEZENTE Farbe ----
   Gewaehlt als gedeckte Toene aus verschiedenen Farbfamilien, damit sie
   sich unterscheiden lassen, ohne zu leuchten. Reihenfolge entspricht der
   Rangfolge: Admin (warmes Rotbraun) -> DJ (gedecktes Violett, die
   Hausfarbe) -> Moderator (Blaugrau) -> Supporter (gedecktes Gruen) ->
   Gast (neutrales Grau). */
.rollen-chip {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .03em;
  padding: 2px 7px;
  border-radius: 999px;
  border: 1px solid transparent;
  white-space: nowrap;
}
.rollen-chip-admin     { background: rgba(158, 96, 78, .17);  color: #c08a72; border-color: rgba(158,96,78,.34); }
.rollen-chip-dj        { background: rgba(124, 104, 175, .18); color: #a493d4; border-color: rgba(124,104,175,.34); }
.rollen-chip-moderator { background: rgba(86, 116, 148, .18);  color: #8aa6c4; border-color: rgba(86,116,148,.34); }
.rollen-chip-supporter { background: rgba(88, 134, 108, .18);  color: #8ab89b; border-color: rgba(88,134,108,.34); }
.rollen-chip-gast      { background: rgba(120, 120, 128, .16); color: #a0a0a8; border-color: rgba(120,120,128,.32); }
/* "Du" bewusst farblos - es ist keine Rolle, nur ein Hinweis. */
.rollen-chip-du        { background: transparent; color: var(--text-dim); border-color: var(--border); }

/* Im hellen Modus brauchen dieselben Toene dunklere Schrift, sonst sind sie
   auf weissem Grund nicht zu lesen. */
html[data-theme="light"] .rollen-chip-admin     { color: #7d4632; background: rgba(158,96,78,.13); }
html[data-theme="light"] .rollen-chip-dj        { color: #574a86; background: rgba(124,104,175,.13); }
html[data-theme="light"] .rollen-chip-moderator { color: #3d5670; background: rgba(86,116,148,.13); }
html[data-theme="light"] .rollen-chip-supporter { color: #3c6650; background: rgba(88,134,108,.13); }
html[data-theme="light"] .rollen-chip-gast      { color: #5b5b63; background: rgba(120,120,128,.12); }

/* Schmale Schirme: Knoepfe unter die Zeile statt daneben. */
@media (max-width: 620px) {
  .user-row { flex-wrap: wrap; }
  .user-knoepfe { width: 100%; justify-content: flex-end; }
}

/* ==========================================================================
   v1.14.0 - ZURUECKSETZEN IN VIER TEILEN
   ==========================================================================
   Statt einem Alles-oder-nichts-Knopf jetzt einzeln waehlbar. Der fuenfte
   Block (Belohnungen) ist bewusst abgesetzt und braucht einen zusaetzlichen
   Haken - er ist der einzige, der eingerichtete Inhalte kostet.
   ========================================================================== */
.reset-teile { display: flex; flex-direction: column; gap: 8px; margin-top: 12px; }
.reset-teil {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: 9px;
  background: var(--bg-card);
}
.reset-teil-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.reset-teil-text b { font-size: 13px; }
.reset-teil-text .hint { margin: 0; font-size: 11.5px; line-height: 1.35; }
.reset-teil .btn { flex: 0 0 auto; }
/* Der Belohnungs-Block hebt sich ab, ohne zu schreien - gedecktes Warnrot
   nur am Rand und als leichte Tuenung. */
.reset-teil-warnung {
  border-color: rgba(180, 90, 70, .45);
  background: rgba(180, 90, 70, .07);
}
@media (max-width: 620px) {
  .reset-teil { flex-wrap: wrap; }
  .reset-teil .btn { width: 100%; }
}

/* ==========================================================================
   v1.14.0 - GEFUEHRTE BOT-EINRICHTUNG
   ==========================================================================
   Nutzer-Wunsch: "nicht nur stumpf den Button 'mit twitch verbinden'
   sondern dann auch gleich die 'persoenlichen einstellungen' in dem Fenster
   anklickbar haben". Deshalb haengt direkt am Anmeldeknopf eine Liste mit
   dem, was noch fehlt - je mit einem Knopf, der genau dorthin springt.
   Erledigtes wird abgehakt statt versteckt: man soll SEHEN, dass es
   erledigt ist, statt zu raten.
   ========================================================================== */
.bot-schritte { margin-top: 10px; }
.bot-schritte > summary {
  cursor: pointer;
  font-size: 12.5px;
  font-weight: 700;
  padding: 5px 0;
  display: flex;
  align-items: center;
  gap: 8px;
}
.bot-schritte-zahl {
  font-size: 11px;
  font-weight: 700;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}
.bot-schritte-liste { display: flex; flex-direction: column; gap: 6px; margin-top: 6px; }
.bot-schritt {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-card);
}
.bot-schritt-marke {
  flex: 0 0 auto;
  width: 18px; height: 18px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  font-size: 11px; font-weight: 800;
  margin-top: 1px;
}
/* Gedeckte Toene - der Kasten soll fuehren, nicht alarmieren. */
.bot-schritt.fertig  .bot-schritt-marke { background: rgba(88,134,108,.25);  color: #8ab89b; }
.bot-schritt.offen   .bot-schritt-marke { background: rgba(180,140,70,.25);  color: #d0a860; }
.bot-schritt.neutral .bot-schritt-marke { background: var(--bg-elevated);    color: var(--text-dim); }
.bot-schritt.offen { border-color: rgba(180,140,70,.38); }
.bot-schritt-text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.bot-schritt-text b { font-size: 12.5px; font-weight: 650; }
.bot-schritt-text .hint { margin: 0; font-size: 11px; line-height: 1.35; }
.bot-schritt-knoepfe { flex: 0 0 auto; display: flex; gap: 4px; flex-wrap: wrap; justify-content: flex-end; }
@media (max-width: 620px) {
  .bot-schritt { flex-wrap: wrap; }
  .bot-schritt-knoepfe { width: 100%; }
}

/* ==========================================================================
   v1.14.5 - DJ-ZEILE ALS FESTES RASTER
   ==========================================================================
   Warum ein Raster und kein Flex-Umbruch mehr:

   Bis v1.14.0 war die Zeile ein Flex-Kasten, der bei Enge umbrach. Dabei
   entscheidet aber JEDES Element fuer sich, wohin es rutscht. Im Betrieb
   (Nutzer-Screenshots vom 28.08., Fenster rund 800px auf 1920er-Monitor)
   ergab das vier verschiedene Anordnungen:
     - kurzer Name  -> Name + Knoepfe oben, Positionsnummer ALLEIN darunter
     - langer Name  -> Knoepfe nach unten LINKS gerutscht
     - dazwischen   -> zwei ungeplante Zwischenstufen bei ~800 und ~700px
   Jede Zeile sah anders aus, je nach Zufall.

   Jetzt drei Spalten mit fester Aufgabe (Vorgabe des Nutzers):
     links   Ziehgriff, Kategorie, Cover
     mitte   Titel, Interpret+Jahr, Warnung
     rechts  oben Name + Position, darunter die Knoepfe
   Es gibt nichts zu entscheiden, also kann nichts springen.
   ========================================================================== */

.djview-row.djview-row-wish {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas:
    "links mitte rechtsoben"
    "links mitte rechtsunten";
  align-items: center;
  column-gap: 8px;
  row-gap: 2px;
  padding: 4px 10px 4px 4px;
}

/* Spalte 1 - liegt oben am Rand, nicht mittig schwebend. Nutzer-Befund:
   "Die Kategorie und das Icon usw ist so schwebend... Kann das nicht
   minimal weiter nach oben an den Rand in der zeile?" */
.djr-links {
  grid-area: links;
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
}
.djr-mitte { grid-area: mitte; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.djr-oben  { grid-area: rechtsoben; display: flex; align-items: center; gap: 6px; justify-self: end; }
.djr-unten { grid-area: rechtsunten; display: flex; align-items: center; gap: 4px; justify-self: end; }

/* Breit: alles in EINER Reihe. Die beiden rechten Bereiche stehen dann
   nebeneinander statt uebereinander - eine Zeile spart Hoehe, und Platz
   ist da. Die WARNUNG bekommt hier eine eigene Rasterzelle rechts neben
   dem Text: so kostet sie keine zusaetzliche Zeilenhoehe, steht aber
   trotzdem immer an derselben Stelle. */
.djview-table:not(.djview-schmal) .djview-row.djview-row-wish {
  grid-template-areas: "links mitte warnung rechtsoben rechtsunten";
  grid-template-columns: auto minmax(0, 1fr) auto auto auto;
}
.djview-table:not(.djview-schmal) .djview-row-wish .restricted-badge-compact {
  grid-area: warnung;
  align-self: center;
  margin: 0;
  /* Der TITEL hat Vorrang. Bei 800px Fensterbreite wurde sonst "Land of
     Confusion (Single Version)" zu "Land of Confusio..." gekuerzt, damit
     die Warnung ihre volle Breite bekommt - die falsche Reihenfolge:
     welcher Song gemeint ist, muss man lesen koennen, die Warnung ist
     schon am roten Rahmen erkennbar. Deshalb schrumpft sie zuerst. */
  min-width: 0;
  max-width: 30ch;
  flex-shrink: 1;
}
/* Die Mittelspalte darf sich den Platz nehmen, den sie braucht. */
.djview-table:not(.djview-schmal) .djview-row.djview-row-wish {
  grid-template-columns: auto minmax(12ch, 1fr) minmax(0, auto) auto auto;
}
/* Der "Gewuenscht von"-Name darf breiter werden - bei 800px Fensterbreite
   wurde "kmrz_zugga_daddy" sonst schon zu "kmrz_zugga..." gekuerzt, obwohl
   rechts noch Platz war (beim Pruefen aufgefallen). */
.djview-row-wish .djview-from { max-width: none; width: auto; flex: 0 0 auto; }
.djview-row-wish .wunschvon-chip { max-width: 210px; }

/* Interpret und Jahr bleiben IMMER in einer Zeile: der Interpret kuerzt
   sich, das Jahr schrumpft nie. Ohne das rutschte das Jahr bei langen
   Namen in eine eigene Zeile (beim Pruefen aufgefallen).

   overflow:hidden am Interpreten ist dabei entscheidend, nicht Beiwerk:
   Ohne das wuchs die Zeile bei "Vicky Leandros, Claude Denjean" von 43 auf
   58 Pixel, obwohl gar keine Warnung da war - der Name brach einfach um.
   Genau so entstanden die ungewollten Zwischenstufen. */
/* align-items MUSS center sein, nicht baseline.
   Nachgemessen bei 700px: Mit baseline war die Interpretenzeile 33 Pixel
   hoch, obwohl ihre Kinder nur 13 bis 14 Pixel messen - das Mikrofon-Emoji
   bringt eine hohe Zeilenbox mit, und baseline richtet daran aus. Die ganze
   Zeile wurde dadurch 58 statt 43 Pixel hoch, ganz ohne Warnung. Das war
   die Ursache der letzten ungewollten Zwischenstufe. */
.djview-artist-zeile {
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  min-width: 0;
  overflow: hidden;
  line-height: 1.25;
}
.djview-artist-zeile .row-artist {
  min-width: 0;
  flex: 0 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Auch der Titel darf nie umbrechen, solange die Zeile breit ist. */
.djview-table:not(.djview-schmal) .djr-mitte .row-title {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.djview-table:not(.djview-schmal) .djview-song { flex-wrap: nowrap; min-width: 0; overflow: hidden; }
.djview-artist-zeile .jahr-erst,
.djview-artist-zeile .jahr-version,
.djview-artist-zeile .jahr-art { flex: 0 0 auto; }

/* Die Warnung: eigene Zeile, linksbuendig, volle Breite der Mittelspalte.
   Nutzer-Vorgabe fuer die schmale Ansicht: "waers gut, wenn das
   Eingeschraenkte Kuenstler LINKSbuendig ist damit es nicht abgeschnitten
   ist". Gilt jetzt in jeder Breite - ein fester Platz ist mehr wert als
   ein paar gesparte Pixel. */
/* ---- SCHMAL: der Text bekommt eine eigene Reihe ueber die volle Breite.
   Beim Pruefen bei 390px aufgefallen: Standen alle drei Spalten weiter
   nebeneinander, blieb fuer den Titel so wenig uebrig, dass er Buchstabe
   fuer Buchstabe umbrach ("L / a / n / d"). Unlesbar.

   Deshalb hier ein anderes Raster - aber mit DERSELBEN Zuordnung wie
   breit, nur anders angeordnet:
     Reihe 1   links (Griff/Kategorie/Cover) | Name + Position
     Reihe 2   links                         | die Knoepfe
     Reihe 3   Titel, Interpret, Jahr        (volle Breite)
     Reihe 4   Warnung                       (volle Breite, linksbuendig)
   ---- */
.djview-table.djview-schmal .djview-row-wish {
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas:
    "links mitte rechtsoben"
    "links mitte rechtsunten"
    "warnung warnung warnung";
  align-items: center;
  row-gap: 2px;
  padding-bottom: 5px;
}
/* Griff, Kategorie und Cover stehen hier UNTEREINANDER statt nebeneinander -
   so bleibt der linken Spalte genug Breite fuer den Titel daneben. */
.djview-table.djview-schmal .djr-links {
  flex-direction: column;
  align-items: center;
  align-self: start;
  gap: 3px;
  padding-top: 2px;
  width: 48px;
}
.djview-table.djview-schmal .djr-links .row-cat { flex: 0 0 auto; width: 46px; }
.djview-table.djview-schmal .djr-mitte { padding-left: 0; align-self: center; }
/* Nutzer-Befund zur alten Fassung: "es sieht so leer aus zwischen der
   kategorie und dem cover und dann kommt nix und dann titel und interpret".
   Deshalb steht der Titel jetzt NEBEN Kategorie und Cover, nicht darunter -
   die Breite wird ausgenutzt statt verschenkt.

   ABER: Unterhalb von etwa 460px reicht auch das nicht mehr. Dann steht
   neben Cover, Name, Position und Knoepfen so wenig Platz fuer den Titel,
   dass er ueber drei Zeilen bricht ("Guten / Abend / Gute / Nacht") -
   nachgemessen bei 390px. Dort bekommt der Text deshalb doch die volle
   Breite. Das ist kein Rueckfall in den alten Zustand: Kategorie und Cover
   stehen weiterhin NEBENeinander und damit schmal, nicht untereinander. */
@media (max-width: 460px) {
  .djview-table.djview-schmal .djview-row-wish {
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas:
      "links rechtsoben"
      "links rechtsunten"
      "mitte mitte"
      "warnung warnung";
  }
  .djview-table.djview-schmal .djr-links {
    flex-direction: row;
    width: auto;
    align-self: center;
  }
  .djview-table.djview-schmal .djr-mitte { padding-left: 2px; }
}
.djview-table.djview-schmal .djr-mitte .row-title { white-space: normal; font-size: 13.5px; }
/* Bei sehr wenig Platz duerfen Interpret und Jahr doch umbrechen - lieber
   zwei Zeilen als ein abgeschnittener Interpret. */
.djview-table.djview-schmal .djview-artist-zeile { flex-wrap: wrap; row-gap: 2px; }
.djview-row-wish .restricted-badge-compact {
  grid-area: warnung;
  justify-self: start;
  align-self: center;
  margin: 2px 0 0;
  max-width: 100%;
}

/* ---- Optionen-Knopf: zwei Knoepfe sichtbar, der Rest dahinter ----
   Nutzer-Vorgabe: "der gruene Haken und der Ausblendebutton sichtbar [...]
   So eine art 'options button' [...] so spart man sich mehrere buttons".
   Begruendung dahinter: "in der dj ansicht wird idr NIE verwaltet
   hoechstens ausgeblendet oder bestaetigt". */
/* Die Huelle braucht einen eigenen z-index, nicht nur position:relative.
   Befund beim Pruefen: Das Menue war zwar deckend (rgb(29,27,33)), aber die
   NACHFOLGENDEN Zeilen malten sich darueber - denn .djview-row > * hat
   z-index:1, und ohne eigenen Stapelwert gewinnt bei gleichem Rang das
   spaetere Element. Sichtbar war das als "durchscheinendes" Menue, obwohl
   es das gar nicht war. */
.djopt-huelle { position: relative; display: inline-flex; z-index: 70; }
/* Und die ganze Zeile mit offenem Menue nach vorn holen. */
.djview-row-wish:has(.djopt-menu) { z-index: 70; }
.djopt-knopf.offen {
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
}
.djopt-menu {
  position: absolute;
  top: calc(100% + 5px);
  right: 0;
  z-index: 60;
  min-width: 216px;
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 3px;
  /* Deckend, nicht durchscheinend: Beim Pruefen lagen die Zeilen sichtbar
     unter dem Menue - man las zwei Texte uebereinander. --bg-card ist an
     dieser Stelle halbtransparent, deshalb hier ein eigener, voll deckender
     Untergrund plus Rueckfall. */
  background: var(--bg-elevated, #1d1b21);
  background-color: #1d1b21;
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, .55);
}
html[data-theme="light"] .djopt-menu { background-color: #ffffff; }
.djopt-kopf {
  font-size: 9.5px;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--text-dim);
  padding: 3px 7px 5px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Bewusst GROSSZUEGIG bemessen: Das Menue wird selten und meist in Eile
   benutzt - dann darf man sich nicht vertippen. Genau dafuer war der
   Optionen-Knopf gedacht ("kann man dann in etwas groesser dargestellt
   entscheiden was man machen will"). */
.djopt-zeile {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 9px;
  border-radius: 7px;
  border: 1px solid transparent;
  background: var(--bg-elevated);
  color: var(--text);
  cursor: pointer;
  text-align: left;
  font: inherit;
  width: 100%;
}
.djopt-zeile:hover { border-color: var(--border); background: var(--bg-hover, var(--bg-elevated)); }
.djopt-sym { width: 19px; text-align: center; flex: 0 0 auto; font-size: 13px; }
.djopt-text { display: flex; flex-direction: column; min-width: 0; }
.djopt-text b { font-size: 12px; font-weight: 650; }
.djopt-text span { font-size: 10px; color: var(--text-dim); line-height: 1.3; }

/* ==========================================================================
   v1.14.5 - ERSCHEINUNGSJAHR
   ==========================================================================
   Zwei Jahre, die nichts miteinander zu tun haben: die Erstveroeffentlichung
   (von MusicBrainz, das ECHTE Datum) und das Jahr der gefundenen Ausgabe.
   Nutzer-Wunsch: Erstveroeffentlichung als eigener kleiner Badge in
   gedecktem Gruen, die Version ohne Badge grau dahinter, beide mit Icon.
   Der Gruenton ist bewusst leiser als der "gespielt"-Knopf, der in
   derselben Zeile sitzt - sonst konkurrieren zwei Gruens miteinander.
   ========================================================================== */
.jahr-erst {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: 9.5px;
  /* line-height MUSS klein bleiben: Der Badge sitzt in der Interpretenzeile,
     und ohne diese Zeile wuchs sie von 16 auf 33 Pixel - die ganze Zeile
     wurde dadurch 58 statt 43 Pixel hoch (beim Pruefen bei 700px
     nachgemessen). Padding und Rand kommen noch dazu, deshalb hier bewusst
     knapp gerechnet. */
  line-height: 1.1;
  font-weight: 700;
  padding: 1px 5px;
  border-radius: 4px;
  background: rgba(127, 196, 157, .16);
  color: #7fc49d;
  border: 1px solid rgba(127, 196, 157, .34);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  vertical-align: middle;
}
html[data-theme="light"] .jahr-erst { color: #2f6d4a; background: rgba(47, 109, 74, .11); }
.jahr-version {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  line-height: 1.1;
  margin-left: 5px;
  font-size: 10px;
  color: var(--text-dim);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
/* Art der Ausgabe (Best-of/Live/Remix) - standardmaessig ausgeblendet,
   per Einstellung zuschaltbar. Gedecktes Gelb, damit es als Hinweis und
   nicht als Warnung gelesen wird. */
.jahr-art {
  display: inline-flex;
  align-items: center;
  line-height: 1.1;
  margin-left: 5px;
  font-size: 9px;
  font-weight: 700;
  padding: 1px 5px;
  border-radius: 4px;
  background: rgba(217, 164, 65, .14);
  color: #c99a3f;
  border: 1px solid rgba(217, 164, 65, .32);
  white-space: nowrap;
}
html[data-theme="light"] .jahr-art { color: #8a6414; }
/* Das schlichte Ausgabejahr, wenn (noch) kein MusicBrainz-Treffer da ist.
   Muss ein Element sein, kein blanker Text - siehe jahrHtml in app.js. */
.jahr-schlicht {
  flex: 0 0 auto;
  white-space: nowrap;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   v1.14.6 - AUSGELOESTE EVENTS BLEIBEN STEHEN
   ==========================================================================
   Nutzer-Vorgabe: "Wenn es aktive Event-belohnungen gibt also z.b. das
   Gluecksrad eingeschaltet ist [...] dann MUSS!!! der Gluecksrad Event mit
   dem Einloeser und der Zeit am besten in einer art Liste da stehen
   bleiben."

   Der Unterschied zu den Support-Kacheln darueber ist grundsaetzlich:
   Eine Support-Gabe ist eine MELDUNG ("Danke!") - die darf nach der
   eingestellten Frist verblassen. Ein ausgeloestes Event ist eine AUFGABE
   ("dreh das Rad fuer sie") - die verschwindet erst, wenn ein Mod sie
   abhakt. Deshalb gilt die Ausblendfrist djAusblendenSek hier NICHT.

   Bewusst schmal gehalten (Nutzer: "in klein einblenden das man nicht zu
   viel Platz verschwendet"): eine Zeile je Aufgabe, rund 18 Pixel hoch.
   ========================================================================== */
.djev-offen {
  margin-top: 5px;
  border-top: 1px solid var(--border);
  padding-top: 4px;
}
.djev-offen-kopf {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 9.5px;
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ev-farbe, #eab308);
  padding: 0 2px 3px;
}
.djev-offen-zahl {
  font-size: 9px;
  font-weight: 800;
  padding: 0 5px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--ev-farbe, #eab308) 26%, transparent);
  font-variant-numeric: tabular-nums;
}
.djev-offen-zeile {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 2px 6px;
  border-radius: 5px;
  font-size: 10.5px;
  line-height: 1.35;
  background: color-mix(in srgb, var(--ev-farbe, #eab308) 9%, transparent);
  border-left: 2px solid color-mix(in srgb, var(--ev-farbe, #eab308) 55%, transparent);
  margin-bottom: 2px;
  cursor: grab;   /* per Ziehen umsortierbar - siehe Nutzer-Wunsch */
}
.djev-offen-zeile:active { cursor: grabbing; }
/* Die laufende Nummer: an ihr liest man ab, wer als naechstes dran ist. */
.djev-offen-nr {
  flex: 0 0 auto;
  min-width: 14px;
  text-align: right;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  color: var(--ev-farbe, #eab308);
  opacity: .85;
}
/* Woraus das Los entstanden ist ("aus 15 Abos") - erklaert, warum jemand
   mehrfach in der Liste steht. */
.djev-offen-woraus {
  flex: 0 0 auto;
  font-size: 9.5px;
  color: var(--text-dim);
  opacity: .75;
  white-space: nowrap;
}
.djev-offen-ok {
  flex: 0 0 auto;
  margin-left: auto;
  padding: 0 5px;
  font-size: 11px;
  line-height: 1;
}
.djev-offen-zeile:last-child { margin-bottom: 0; }

/* ==========================================================================
   v1.16.0 - Der Kasten waechst nicht mehr endlos.
   
   Nutzer-Befund: 22 offene Eintraege fuellten den halben Bildschirm.
   Hoechstens fuenf Zeilen, die sechste angeschnitten - so sieht man ohne
   Scrollbalken, dass es weitergeht.
   
   Der Balken selbst ist unsichtbar, bis die Maus ueber dem Kasten steht.
   Auf dem reinen Anzeige-Bildschirm (Bilduebertragung, niemand kann dort
   klicken) erscheint er also nie - genau so gewollt.
   ========================================================================== */
.djev-offen-roll {
  /* Zeilenhoehe 21px (10.5px Schrift * 1.35 Zeilenabstand + 2*2px Innenabstand
     + 2px Abstand). Fuenf ganze plus ein Drittel der sechsten. */
  max-height: calc(5 * 21px + 7px);
  overflow-y: auto;
  overscroll-behavior: contain;
  /* Kein Balken im Ruhezustand - weder Firefox noch WebKit. */
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.djev-offen-roll::-webkit-scrollbar { width: 0; }

/* Erst beim Zeigen: schmal und in der Event-Farbe, damit er sich nicht
   vordraengt. */
.djev-offen:hover .djev-offen-roll {
  scrollbar-width: thin;
  scrollbar-color: color-mix(in srgb, var(--ev-farbe, #eab308) 45%, transparent) transparent;
}
.djev-offen:hover .djev-offen-roll::-webkit-scrollbar { width: 5px; }
.djev-offen-roll::-webkit-scrollbar-track { background: transparent; }
.djev-offen-roll::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--ev-farbe, #eab308) 45%, transparent);
  border-radius: 999px;
}

/* Die kleine Zweitzahl: "22" gesamt, "5" davon hier sichtbar. Nur da, wenn
   ein Mod wirklich etwas ausgeblendet hat. */
.djev-offen-teil {
  font-size: 8px;
  font-weight: 700;
  margin-left: 3px;
  padding-left: 3px;
  border-left: 1px solid color-mix(in srgb, var(--ev-farbe, #eab308) 45%, transparent);
  opacity: .75;
}
.djev-offen-wer {
  font-weight: 700;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 16ch;
  flex: 0 1 auto;
}
.djev-offen-was {
  color: var(--text-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 1 1 auto;
  min-width: 0;
}
.djev-offen-menge {
  font-weight: 800;
  font-size: 9.5px;
  padding: 0 4px;
  border-radius: 4px;
  background: color-mix(in srgb, var(--ev-farbe, #eab308) 24%, transparent);
  flex: 0 0 auto;
  font-variant-numeric: tabular-nums;
}
.djev-offen-zeit {
  color: var(--text-dim);
  font-size: 9.5px;
  flex: 0 0 auto;
  font-variant-numeric: tabular-nums;
  opacity: .8;
}
.djev-offen-was { flex: 0 1 auto; }
/* Der Auge-Knopf beim Mod, solange etwas auf dem Anzeige-Bildschirm steht. */
.ev-anzeige-an {
  border-color: rgba(127, 196, 157, .55);
  color: #7fc49d;
}

/* ==========================================================================
   v1.16.0 - DJ-ANSICHT NACH KATEGORIEN GEBUENDELT
   
   Nutzer-Vorgabe zur Optik: "nicht jedes wirklich so krass abgetrennt mit
   eigenen kaestchen und luecken usw weil das platz verschwendet sondern
   gedacht ist eher das die Kaesten direkt vielleicht mit 2-3 pixel abstand
   nur aneinander sitzen und den Rand der jeweiligen Kategorie haben."
   Und: "ohne dicke scrolleisten, raender, fancy animationen oder sowas".
   
   Deshalb: 3px Abstand, 1px Rand in der Kategoriefarbe, keine Schatten,
   keine Uebergaenge, keine Rundungen ausser den noetigen.
   ========================================================================== */
.djkat-block {
  border: 1px solid color-mix(in srgb, var(--cat-color, var(--border)) 42%, transparent);
  border-radius: 5px;
  margin-bottom: 3px;
  overflow: hidden;
  /* Die tatsaechliche Hoehe einer DJ-Zeile - daraus rechnet das Frontend die
     Bereichshoehe (siehe djviewNachKategorien). Steht hier und nicht im JS,
     damit beides nicht auseinanderlaufen kann, wenn jemand die Zeile
     umbaut. */
  --dj-zeile: 43px;
}
/* Die Wunschzeilen im Bereich brauchen keinen eigenen Aussenabstand mehr -
   der Bereich haelt sie zusammen. Sonst entstuenden die Luecken, die der
   Nutzer ausdruecklich nicht will ("weil das platz verschwendet"). */
.djkat-roll > .djview-row { margin: 0; }
.djkat-block:last-child { margin-bottom: 0; }

/* Die Kopfzeile ist bewusst winzig - sie sagt nur, welcher Bereich das ist.
   Auf dem Anzeige-Bildschirm zaehlt jeder Pixel fuer die Songs selbst. */
.djkat-kopf {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 1px 8px;
  font-size: 9.5px;
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--cat-color, var(--text-dim));
  background: color-mix(in srgb, var(--cat-color, transparent) 13%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--cat-color, transparent) 28%, transparent);
}
.djkat-kuerzel { font-weight: 900; }
.djkat-name { font-weight: 600; opacity: .8; letter-spacing: .03em; }
.djkat-zahl {
  margin-left: auto;
  font-size: 9px;
  padding: 0 5px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--cat-color, transparent) 26%, transparent);
  font-variant-numeric: tabular-nums;
}

/* Der Scrollbereich je Kategorie. Gleiche Regel wie beim Noch-offen-Kasten:
   kein Balken, solange die Maus weg ist. Auf dem Anzeige-Bildschirm, wo
   niemand klicken kann, erscheint er deshalb nie. */
.djkat-roll {
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.djkat-roll::-webkit-scrollbar { width: 0; }
.djkat-block:hover .djkat-roll {
  scrollbar-width: thin;
  scrollbar-color: color-mix(in srgb, var(--cat-color, #8b5cf6) 45%, transparent) transparent;
}
.djkat-block:hover .djkat-roll::-webkit-scrollbar { width: 5px; }
.djkat-roll::-webkit-scrollbar-track { background: transparent; }
.djkat-roll::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--cat-color, #8b5cf6) 45%, transparent);
  border-radius: 999px;
}

/* Innerhalb eines Bereichs braucht die letzte Zeile keinen Trennstrich mehr -
   der Rahmen des Bereichs uebernimmt das. */
.djkat-roll > .djview-row:last-child { border-bottom: none; }

/* Wuensche ohne Kategorie: gleicher Aufbau, nur ohne farbigen Kopf. */
.djkat-rest { border-color: var(--border); }

/* ==========================================================================
   v1.16.0 - Einstellung "Anordnung der Liste", mit Vorschaubildern.
   
   Nutzer-Wunsch: "Am besten mit einem Screenshot oder sowas damit man sieht
   was man einstellt." Statt eines echten Bildes eine kleine Nachbildung aus
   denselben Bausteinen - ein Bild waere nach der ersten Farbaenderung falsch,
   diese Vorschau bleibt immer richtig.
   ========================================================================== */
.djg-wahl {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 10px;
  margin-bottom: 10px;
}
.djg-karte {
  display: block;
  padding: 10px;
  border: 2px solid var(--border);
  border-radius: 8px;
  background: var(--bg-elevated);
  cursor: pointer;
}
.djg-karte:hover { border-color: color-mix(in srgb, var(--accent, #8b5cf6) 45%, var(--border)); }
.djg-karte.aktiv {
  border-color: var(--accent, #8b5cf6);
  background: color-mix(in srgb, var(--accent, #8b5cf6) 8%, var(--bg-elevated));
}
/* Der Radioknopf selbst ist unsichtbar - die ganze Karte IST der Knopf.
   Fuer die Tastatur bleibt er erreichbar (siehe :focus-visible unten). */
.djg-karte input[type="radio"] {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}
.djg-karte:focus-within {
  outline: 2px solid var(--accent, #8b5cf6);
  outline-offset: 2px;
}
.djg-titel {
  display: block;
  font-weight: 700;
  font-size: 13px;
  margin-top: 8px;
}
.djg-text {
  display: block;
  font-size: 11.5px;
  color: var(--text-dim);
  line-height: 1.4;
  margin-top: 2px;
}

/* Die Miniatur-Nachbildung. */
.djg-bild {
  background: var(--bg);
  border-radius: 5px;
  padding: 4px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.djg-zeile {
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 2px 4px;
  border-left: 2px solid var(--c, #8b5cf6);
  background: color-mix(in srgb, var(--c, #8b5cf6) 12%, transparent);
  border-radius: 2px;
}
.djg-nr {
  font-size: 8px;
  font-weight: 800;
  color: var(--c, #8b5cf6);
  font-variant-numeric: tabular-nums;
  min-width: 7px;
}
.djg-balken {
  height: 3px;
  flex: 1 1 auto;
  border-radius: 999px;
  background: color-mix(in srgb, var(--text-dim) 35%, transparent);
}
.djg-gruppe {
  border: 1px solid color-mix(in srgb, var(--c, #8b5cf6) 45%, transparent);
  border-radius: 3px;
  padding: 2px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.djg-kopf {
  font-size: 7.5px;
  font-weight: 900;
  letter-spacing: .07em;
  color: var(--c, #8b5cf6);
  padding: 0 2px;
}

/* Reihenfolge und Hoehe je Bereich. */
.djg-bereiche { margin-top: 4px; }
.djg-bereich-zeile {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 8px;
  margin-bottom: 3px;
  border-radius: 5px;
  border-left: 3px solid var(--c, #8b5cf6);
  background: color-mix(in srgb, var(--c, #8b5cf6) 9%, transparent);
  cursor: grab;
}
.djg-bereich-zeile.zieht { opacity: .45; }
.djg-b-kuerzel {
  font-weight: 900;
  font-size: 10px;
  letter-spacing: .05em;
  color: var(--c, #8b5cf6);
}
.djg-b-name { font-size: 12px; flex: 1 1 auto; }
.djg-b-zeilen {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 11px;
  color: var(--text-dim);
  flex: 0 0 auto;
}
.djg-b-zeilen input {
  width: 52px;
  padding: 2px 5px;
  font-size: 12px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   v1.17.0 - VORHOEREN UND ANBIETER-LINK IM PRUEFEN-TAB
   
   Nutzer-Wunsch: ein "vorhoeren"-Knopf je Zeile, und der Link zum Song
   "NUR dort dann zusaetzlich sichtbar sein in dem Notizfeed und DJ Ansicht
   soll der NICHT sichtbar sein weiterhin" - beide Klassen kommen deshalb
   ausschliesslich in kandidatHtml() vor.
   ========================================================================== */
.tw-hoer, .tw-link-knopf {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 5px;
  border: 1px solid var(--border);
  background: var(--bg-elevated);
  color: var(--text-dim);
  font-size: 11px;
  line-height: 1;
  cursor: pointer;
  text-decoration: none;
}
.tw-hoer:hover, .tw-link-knopf:hover {
  color: var(--text);
  border-color: color-mix(in srgb, var(--accent, #8b5cf6) 55%, var(--border));
}
/* Laeuft gerade: deutlich, aber nicht schreiend. */
.tw-hoer.laeuft {
  color: #7fc49d;
  border-color: rgba(127, 196, 157, .55);
  background: rgba(127, 196, 157, .12);
}

/* Das kleine Fenster fuer Spotify/TIDAL. Unten rechts, damit es die
   Pruefliste nicht verdeckt - dort arbeitet man ja weiter. */
.hoer-fenster {
  position: fixed;
  right: 14px;
  bottom: 14px;
  z-index: 900;
  /* v1.25.1 - Von 300 auf 340 Pixel. Nutzer-Befund: "er wirkt eh
     'abgeschnitten'". Stimmte: Bei 300 Pixel Breite und 80 Pixel Hoehe
     blieb von TIDALs Bedienleiste nichts uebrig. Breiter, aber immer noch
     schmal genug, dass die Pruefliste daneben bedienbar bleibt. */
  width: min(340px, calc(100vw - 28px));
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 8px 28px rgba(0, 0, 0, .45);
  overflow: hidden;
}
.hoer-kopf {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 10px;
  border-bottom: 1px solid var(--border);
}
.hoer-was {
  font-size: 12px;
  font-weight: 650;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 1 1 auto;
  min-width: 0;
}
.hoer-zu {
  flex: 0 0 auto;
  background: none;
  border: none;
  color: var(--text-dim);
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  padding: 2px 4px;
}
.hoer-zu:hover { color: var(--text); }
.hoer-fenster iframe {
  display: block;
  width: 100%;
  /* v1.25.1 - Von 80 auf 118 Pixel.
     Im Stylesheet des TIDAL-Players nachgesehen (classic.css): Er hat
     min-height:80px und schaltet bei "height <= 120px" auf eine
     zusammengestauchte Fassung um, bei der die Bedienleiste wegfaellt.
     Genau darin lag das "abgeschnitten" - mit 80px bekam man das gestauchte
     Layout OHNE Zeitleiste. Mit 118px zeigt er die volle Leiste, und damit
     funktioniert auch das Spulen, das der Nutzer bestaetigt hat. */
  height: 118px;
  border: 0;
}
.hoer-hinweis {
  padding: 6px 10px;
  font-size: 10.5px;
  line-height: 1.4;
  color: var(--text-dim);
  border-top: 1px solid var(--border);
}

/* ==========================================================================
   v1.19.0 - Lautstaerkeregler und Sammel-Ablehnung
   ========================================================================== */

/* Der Regler im Hoerfenster. Nutzer-Wunsch: "ZWINGEND auch ein
   Lautstaerkeregler einbauen. Ansonsten droehnt der song mit 100%
   lautstaerke ggf. das ohr weg." */
.hoer-regler {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 10px;
  border-top: 1px solid var(--border);
}
.hoer-sym { flex: 0 0 auto; font-size: 12px; opacity: .8; }
.hoer-regler input[type="range"] {
  flex: 1 1 auto;
  min-width: 0;
  height: 4px;
  accent-color: var(--accent, #8b5cf6);
  cursor: pointer;
}
.hoer-prozent {
  flex: 0 0 auto;
  font-size: 10.5px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  min-width: 4ch;
  text-align: right;
}

/* Kopfzeile der Pruefliste mit dem Sammel-Knopf rechts. */
.tw-h-zeile {
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
}
.tw-h-zeile .tw-h { flex: 1 1 auto; }
/* Bewusst zurueckhaltend: Der Knopf raeumt die ganze Liste ab, soll aber
   nicht wie der Hauptweg aussehen. Erst beim Zeigen wird er rot. */
.tw-alle-weg {
  flex: 0 0 auto;
  opacity: .75;
}
.tw-alle-weg:hover {
  opacity: 1;
  border-color: rgba(220, 90, 90, .6);
  color: #e79a9a;
}
.tw-alle-weg:disabled { opacity: .5; cursor: default; }

/* ==========================================================================
   v1.20.0 - ERKANNT, ABER NOCH NICHT ABGEHAKT
   
   Nutzer-Vorgabe: "daher NUR als erledigt/gespielt gruen z.b. markieren die
   ganze Zeile oder so... es muss aber manuell dann auf 'gespielt' geklickt
   werden, damit die funktion dahinter wirklich ausgeloest wird."
   
   Die Markierung ist ein HINWEIS, kein Zustand: "der lief gerade, magst du
   abhaken?" Deshalb gruen, aber zurueckhaltend - der Wunsch ist ja weiter
   offen und soll nicht wie erledigt aussehen.
   ========================================================================== */
.wish-row.erkannt-offen,
.djview-row.erkannt-offen {
  /* Der Streifen links ist das eigentliche Zeichen - die Kategoriefarbe
     bleibt daneben sichtbar, sie sagt ja etwas anderes aus. */
  box-shadow: inset 3px 0 0 0 #4ade80;
  background-image: linear-gradient(
    to right,
    rgba(74, 222, 128, .13) 0%,
    rgba(74, 222, 128, .05) 40%,
    transparent 75%
  );
}
/* Unsicherer Treffer (unter 100 %): dieselbe Idee, nur blasser und in
   Bernstein - man soll den Unterschied sehen, ohne den Text zu lesen. */
.wish-row.erkannt-offen.unsicher,
.djview-row.erkannt-offen.unsicher {
  box-shadow: inset 3px 0 0 0 #eab308;
  background-image: linear-gradient(
    to right,
    rgba(234, 179, 8, .12) 0%,
    rgba(234, 179, 8, .04) 40%,
    transparent 75%
  );
}
/* In der DJ-Zeile liegt schon ein Farbverlauf fuer die Kategorie darunter
   (siehe .djview-row::before). Der Streifen muss darueber liegen, sonst
   verschwindet er. */
.djview-row.erkannt-offen { z-index: 0; }

/* ==========================================================================
   v1.22.0 - PRUEFLISTE GESTRAFFT
   
   Nutzer-Befund (Screenshot vom Handy): "das cover und das Tidal Chip die
   uebereinstimmung und die play und link chips sind so gross das man den
   titel und co kaum noch sieht."
   
   NACHGEMESSEN bei 390px Hochformat: Von der Breite blieben fuer Titel und
   Interpret ganze 60px uebrig - etwa acht Zeichen. Genau das "S.." aus dem
   Screenshot. Der Rest ging an Raender (54px), Luecken (60px), Cover (44px)
   und den TIDAL-Chip (62px).
   
   Die Aenderungen unten bringen das auf 175px - rund 23 Zeichen.
   ========================================================================== */

/* --- Ueberall: enger, aber nicht gequetscht --- */
.tw-cands{ padding: 8px 13px 4px; gap: 4px; }
.tw-cand{
  gap: 9px; padding: 5px 9px;
  border-width: 1px; border-radius: 8px;
}
.tw-cover{ width: 32px; height: 32px; border-radius: 5px; }
.tw-cand-t{ font-size: 13px; line-height: 1.25; }
.tw-cand-a{ font-size: 11.5px; line-height: 1.25; opacity: .68; }
.tw-score{ font-size: 12px; min-width: 40px; }

/* Der Kopfbereich in EINER Zeile - Beschriftung, Eingabe, Trefferchip.
   Vorher: Ueberschrift ueber Kasten ueber Trefferzeile = rund 96px. */
.tw-raw{
  padding: 8px 13px 0;
  display: flex; align-items: center; gap: 9px;
}
.tw-raw-lbl{
  font-size: 10px; opacity: .5; margin: 0; flex: 0 0 auto;
  text-transform: uppercase; letter-spacing: .6px;
}
.tw-raw-txt{
  font-size: 14px; padding: 5px 10px; border-radius: 7px;
  flex: 1 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tw-raw .tw-pill{ flex: 0 0 auto; font-size: 11px; padding: 2px 9px; }

/* --- Handy: das Wesentliche bekommt den Platz --- */
@media (max-width: 620px) {
  /* Raender links/rechts deutlich schmaler - im Hochformat zaehlt jeder
     Pixel fuer Titel und Interpret. */
  .tw-cands{ padding: 6px 8px 4px; }
  .tw-cand{ gap: 5px; padding: 5px 7px; }

  /* Anbieter-Logo und Name UEBEREINANDER statt nebeneinander.
     Nutzer-Vorschlag woertlich: "das Icon vom Anbieter und die schrift dazu
     'TIDAL' z.b. uebereinander und dann direkt daneben die
     uebereinstimmungsangabe und das anklickbare runde checkbox ding".
     Spart 32px - der groesste Einzelposten. */
  .tw-prov{
    flex-direction: column;
    align-items: center;
    gap: 1px;
    padding: 2px 4px;
    font-size: 8px;
    line-height: 1;
    letter-spacing: .02em;
  }
  .tw-prov-icon{ width: 13px; height: 13px; }

  /* Die drei Angaben rechts ruecken zusammen. */
  .tw-score{ min-width: 32px; font-size: 11.5px; }
  .tw-hoer, .tw-link-knopf{ width: 20px; height: 20px; font-size: 10px; }

  /* Der Kopfbereich klappt um, sobald es eng wird - sonst waere die Eingabe
     auf wenige Zeichen gequetscht. */
  .tw-raw{ flex-wrap: wrap; gap: 5px 8px; }
  .tw-raw-txt{ flex-basis: 100%; order: 3; font-size: 13px; }
  .tw-raw-lbl{ order: 1; }
  .tw-raw .tw-pill{ order: 2; margin-left: auto; }
}

/* ==========================================================================
   v1.23.0 - PLAY/LINK GESTAPELT, LEICHTE FARBEN, TIDAL OBEN

   Nutzer-Wunsch woertlich:
     "am besten den Playbutton und den Link button auch stapeln sonst den
      Playbutton gern in einem leichten gruen und wenn man das lied dann
      gestartet hat wirds ja zum pause/stopbutton (gern in leichtem rot)
      aber wirklich nur LEICHT!"
     "die luecken zwischen den button muss nicht 100px breit sein aber kann
      da natuerlich kompfortabler groesser sein klar"

   Umgesetzt in drei Teilen:
     1. Ein gemeinsamer Behaelter .tw-knoepfe (siehe kandidatHtml in app.js).
        Am grossen Schirm nebeneinander, am Handy uebereinander.
     2. Die Luecken der Zeile von 9px auf 7px - spuerbar, aber nicht gequetscht.
     3. Farben: gruen im Ruhezustand, rot waehrend der Wiedergabe. Beides
        bewusst als Fuellung mit ~10 % Deckkraft plus getoenter Rahmen -
        das ist die "leichte" Variante, kein Signalknopf.
   ========================================================================== */

/* --- Der Knopf-Behaelter --- */
.tw-knoepfe{
  display: flex;
  align-items: center;
  gap: 5px;
  flex: 0 0 auto;
}

/* Luecken der Kandidatenzeile etwas enger - der Nutzer wollte weniger
   verschenkten Platz, aber es soll nicht kleben. */
.tw-cand{ gap: 7px; }

/* --- Farben: LEICHT, nicht schreiend --- */
/* Ruhezustand: leichtes Gruen. Die Fuellung liegt bei 10 % Deckkraft, der
   Rahmen bei 30 % - kraeftig genug, um den Knopf als "hier startet etwas"
   zu lesen, weit entfernt von einem gruenen Vollflaechen-Knopf. */
.tw-hoer{
  color: #86c9a4;
  border-color: rgba(134, 201, 164, .30);
  background: rgba(134, 201, 164, .10);
}
.tw-hoer:hover{
  color: #a5dbbe;
  border-color: rgba(134, 201, 164, .55);
  background: rgba(134, 201, 164, .16);
}

/* Waehrend der Wiedergabe (Knopf zeigt dann das Stopp-Zeichen): leichtes Rot.
   Ueberschreibt die v1.17.0-Regel .tw-hoer.laeuft, die vorher gruen war -
   gruen und rot bedeuteten sonst beide "laeuft". */
.tw-hoer.laeuft{
  color: #e0a0a0;
  border-color: rgba(224, 160, 160, .35);
  background: rgba(224, 160, 160, .11);
}
.tw-hoer.laeuft:hover{
  color: #f0b8b8;
  border-color: rgba(224, 160, 160, .60);
  background: rgba(224, 160, 160, .17);
}

/* Der Link-Knopf bleibt bewusst neutral - sonst konkurrieren zwei Farben
   um dieselbe Aufmerksamkeit. */

/* --- Handy: Play und Link uebereinander --- */
@media (max-width: 620px) {
  /* Genau wie beim Anbieter-Chip: senkrecht statt waagerecht.
     Das bringt 23px Breite fuer Titel und Interpret.

     NACHGEMESSEN: Mit den bisherigen 20px hohen Knoepfen waere die ZEILE
     dabei von 44px auf 55px gewachsen (2x20 + 3 Luecke = 43px, mehr als das
     32px hohe Cover). Bei fuenf Vorschlaegen waeren das 55px verlorene
     Hoehe - der gewonnene Platz links waere rechts wieder draufgegangen.

     Deshalb sind die gestapelten Knoepfe flacher: 2x14 + 2 Luecke = 30px,
     das bleibt unter der Cover-Hoehe und die Zeile behaelt ihre 44px.
     Angetippt werden sie trotzdem gut - die Trefferflaeche wird ueber
     ::before auf 30px Hoehe verbreitert, ohne den Umbruch zu beeinflussen. */
  .tw-knoepfe{
    flex-direction: column;
    gap: 2px;
  }
  .tw-hoer, .tw-link-knopf{
    width: 22px;
    height: 14px;
    font-size: 9px;
    border-radius: 4px;
    position: relative;
  }
  /* Unsichtbare Vergroesserung der Trefferflaeche - der Knopf bleibt optisch
     flach, laesst sich mit dem Daumen aber sauber treffen. */
  .tw-hoer::before, .tw-link-knopf::before{
    content: '';
    position: absolute;
    inset: -7px -3px;
  }
  .tw-cand{ gap: 5px; }
}

/* --- Helles Thema: dieselbe Idee, dunklere Toene ---
   Die Werte oben sind fuer den dunklen Hintergrund gemischt. Auf Weiss waeren
   #86c9a4 und #e0a0a0 zu blass zum Lesen - hier deshalb kraeftigere, aber
   ebenso zurueckhaltende Toene, gebaut wie die bestehenden Hell-Regeln
   (.jahr-erst und die Rollen-Chips) auch. */
html[data-theme="light"] .tw-hoer{
  color: #2f7d55;
  border-color: rgba(47, 125, 85, .30);
  background: rgba(47, 125, 85, .09);
}
html[data-theme="light"] .tw-hoer:hover{
  color: #24663f;
  border-color: rgba(47, 125, 85, .55);
  background: rgba(47, 125, 85, .15);
}
html[data-theme="light"] .tw-hoer.laeuft{
  color: #a8443f;
  border-color: rgba(168, 68, 63, .32);
  background: rgba(168, 68, 63, .09);
}
html[data-theme="light"] .tw-hoer.laeuft:hover{
  color: #8d322e;
  border-color: rgba(168, 68, 63, .58);
  background: rgba(168, 68, 63, .16);
}

/* Auto-Modus ohne gesetztes data-theme: dieselben Werte, wenn das
   Betriebssystem auf hell steht. */
@media (prefers-color-scheme: light) {
  html:not([data-theme]) .tw-hoer{
    color: #2f7d55;
    border-color: rgba(47, 125, 85, .30);
    background: rgba(47, 125, 85, .09);
  }
  html:not([data-theme]) .tw-hoer:hover{
    color: #24663f;
    border-color: rgba(47, 125, 85, .55);
    background: rgba(47, 125, 85, .15);
  }
  html:not([data-theme]) .tw-hoer.laeuft{
    color: #a8443f;
    border-color: rgba(168, 68, 63, .32);
    background: rgba(168, 68, 63, .09);
  }
  html:not([data-theme]) .tw-hoer.laeuft:hover{
    color: #8d322e;
    border-color: rgba(168, 68, 63, .58);
    background: rgba(168, 68, 63, .16);
  }
}

/* Hinweis, wenn TIDAL oben steht, die gespeicherte Vorauswahl aber noch auf
   einen anderen Anbieter zeigt (nur bei Eintraegen aus der Zeit vor v1.23.0).
   Bewusst ruhig gehalten - es ist kein Fehler, nur eine Ungereimtheit. */
.tw-tidal-hinweis{
  margin: 4px 13px 0;
  padding: 6px 10px;
  border-radius: 7px;
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--text-dim);
  background: rgba(255,255,255,.04);
  border: 1px solid var(--border);
}
.tw-tidal-hinweis b{ color: var(--text); font-weight: 650; }
@media (max-width: 620px) {
  .tw-tidal-hinweis{ margin: 4px 8px 0; font-size: 11px; }
}

/* ==========================================================================
   v1.25.0 - DER ERKENNUNGS-CHIP: "DIE ECKE"

   Nutzer-Wahl aus drei Entwuerfen: "Bitte Nimm variante C die Ecke."

   GRUNDGEDANKE: Ein schmales Band in der oberen rechten Ecke. Dort steht in
   keiner Ansicht Text - deshalb kann es die harte Vorgabe erfuellen:
   "es darf niemals titel und interpret ueberdeckt werden in keiner ansicht
   egal wie klein!"

   Es waechst nach LINKS, nie nach unten - so behaelt der Titel immer seine
   volle Breite.
   ========================================================================== */

/* Die Zeile bekommt oben Luft fuer das Band. */
.wish-row:has(.erk-ecke){ padding-top: 21px; }

.erk-ecke{
  position: absolute; top: 0; right: 0;
  display: flex; align-items: center; gap: 8px;
  height: 20px; padding: 0 12px 0 18px;
  /* Antippbar, damit man sie am Handy kurz wegblenden kann (siehe
     attachHandlers in app.js). Aber KEIN Knopf-Aussehen: kein Zeigefinger,
     keine Auswahl, kein Rahmen. Nutzer: "Es soll kein anklickbarer button
     sein und der soll also auch nicht so aussehen." */
  pointer-events: auto; cursor: default; user-select: none;
  -webkit-tap-highlight-color: transparent;
  transition: opacity .22s ease;
  border-bottom-left-radius: 10px;
  max-width: calc(100% - 40px);         /* nie ueber die ganze Zeile */
  overflow: hidden;
  z-index: 1;
}
.erk-txt{
  font-size: 10px; font-weight: 700; letter-spacing: .3px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  text-shadow: 0 1px 2px rgba(0,0,0,.5);
  text-transform: uppercase;
}
.erk-q{
  font-size: 9.5px; white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; min-width: 0;
  padding-left: 8px; border-left: 1px solid currentColor;
  border-left-color: rgba(255,255,255,.22);
}
.erk-p{
  font-size: 11px; font-weight: 800; flex: 0 0 auto;
  font-variant-numeric: tabular-nums;
}

/* --- STUFE 1: ab "gruen" - volle Farbe, volle Info --- */
.erk-ecke.gruen{
  background: linear-gradient(90deg, rgba(34,197,94,.14), rgba(34,197,94,.40));
}
.erk-ecke.gruen .erk-txt{ color: #e7fbee; }
.erk-ecke.gruen .erk-q{ color: rgba(220,252,231,.82); }
.erk-ecke.gruen .erk-p{ color: #bbf7d0; }

/* --- STUFE 2: ab "gelb" - DEZENTER.
   Nutzer: "bei 80 dezenter". Halbe Deckkraft im Verlauf, kleinere Schrift,
   und die Fahne wird flacher. --- */
.erk-ecke.gelb{
  height: 18px;
  background: linear-gradient(90deg, rgba(234,179,8,.07), rgba(234,179,8,.22));
}
.erk-ecke.gelb .erk-txt{ color: #fdf0cd; font-size: 9.5px; font-weight: 650; }
.erk-ecke.gelb .erk-q{ color: rgba(254,243,199,.7); font-size: 9px; }
.erk-ecke.gelb .erk-p{ color: #f6dd9c; font-size: 10px; }

/* --- STUFE 3: ab "Uebernahme-Schwelle" - NUR NOCH SCHWACH LEUCHTEND.
   Nutzer: "bei 70 nur noch 'schwach' leuchtend in der farbe".
   Kaum noch Fuellung, blasse Schrift - da ist, aber draengt sich nicht auf. --- */
.erk-ecke.orange{
  height: 16px; padding: 0 10px 0 14px; gap: 6px;
  background: linear-gradient(90deg, rgba(234,179,8,.02), rgba(234,179,8,.10));
}
.erk-ecke.orange .erk-txt{ color: rgba(253,240,205,.66); font-size: 9px; font-weight: 600; }
.erk-ecke.orange .erk-q{ color: rgba(254,243,199,.5); font-size: 8.5px; }
.erk-ecke.orange .erk-p{ color: rgba(246,221,156,.8); font-size: 9.5px; }

/* Maus drueber -> weg. Nutzer: "im besten fall verschwindet das wenn man
   mit der maus drueberfaehrt bzw. wird nahezu 100% transparent". */
.wish-row:hover .erk-ecke,
.djview-row-wish:hover .erk-ecke{ opacity: .05; }

/* Angetippt (Handy/Tablet - dort gibt es kein Mausover): kurz weg.
   Der Chip ist dafuer ausnahmsweise anklickbar, sieht aber nicht so aus. */
.erk-ecke.weg{ opacity: .05 !important; }

/* --- Gestaffeltes Wegfallen bei wenig Platz ---
   Nutzer: "die medienquelle ist dabei das wichtigste also mittlere anzeige
   dann eher kuerzen aber nicht gleich ganz weg."
   Reihenfolge des Wegfallens: Tool -> Uhrzeit -> Medienquelle. */
@media (max-width: 860px){
  .erk-ecke .nur-breit{ display: none; }     /* zuerst das Tool */
}
@media (max-width: 620px){
  .erk-ecke{ height: 18px; padding: 0 9px 0 13px; gap: 6px; }
  .erk-txt{ font-size: 9px; }
  .erk-q{ padding-left: 6px; font-size: 8.5px; }
  .erk-ecke .nur-mittel{ display: none; }    /* dann die Uhrzeit */
  .wish-row:has(.erk-ecke){ padding-top: 19px; }
}

/* --------------------------------------------------------------------------
   DIE ECKE IN DER DJ-ANZEIGE

   WICHTIGER UNTERSCHIED zum Musikfeed: In der DJ-Zeile ist die obere rechte
   Ecke NICHT frei - dort stehen der Wuenscher-Name und die Position
   (Rasterzelle "rechtsoben"). Beim Bauen der Vorschau gemessen: die Fahne
   lag dort 91px ueber dem Titel und 75px ueber dem Namen.

   Deshalb wird sie hier nicht positioniert, sondern bekommt eine eigene
   RASTERZELLE. Dann kann sie konstruktiv nichts ueberdecken.
   -------------------------------------------------------------------------- */

/* BREIT: eigene Spalte zwischen Text und Knoepfen (dieselbe Zelle, die
   auch die Sperrlisten-Warnung nutzt). */
.djview-table:not(.djview-schmal) .djview-row-wish:has(.erk-ecke){
  grid-template-areas: "links mitte warnung rechtsoben rechtsunten";
  grid-template-columns: auto minmax(120px, 1fr) minmax(0, auto) auto auto;
}
.djview-row-wish .erk-ecke{
  position: static; grid-area: warnung; align-self: center;
  border-radius: 6px; max-width: 260px;
  padding: 0 10px; margin: 0;
}
/* Die Textteile muessen selbst kuerzen duerfen. Ohne das ragte der Textblock
   im Test 110px nach LINKS aus seinem eigenen Chip heraus - ein Deckel am
   Chip allein reicht nicht. */
.djview-row-wish .erk-txt,
.djview-row-wish .erk-q{ min-width: 0; }

/* SCHMAL: eigene Reihe unter der Zeile - dort ist der Platz, auf den der
   Nutzer hingewiesen hat ("da hast du dann unten oder oben 'mittig' sehr
   viel platz"). Volle Breite, damit die Fahne gut lesbar wird. */
.djview-table.djview-schmal .djview-row-wish:has(.erk-ecke){
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas:
    "links mitte rechtsoben"
    "links mitte rechtsunten"
    "warnung warnung warnung";
  padding-bottom: 5px;
}
.djview-table.djview-schmal .erk-ecke{
  justify-self: stretch; justify-content: flex-start;
  max-width: none; height: 19px;
}
.djview-table.djview-schmal .erk-p{ margin-left: auto; }

/* --------------------------------------------------------------------------
   SEHR SCHMALE SCHIRME (unter 380px)

   BEIM PRUEFEN GEMESSEN: In der DJ-Anzeige blieben dem Titel bei 320px nur
   6 bis 12 Pixel. GEGENPROBE mit ausgeblendeter Fahne: exakt dieselben
   Werte - es liegt also NICHT an der neuen Fahne, sondern daran, dass die
   Zeile dort ohnehin schon zu eng ist (Kategorie, Cover, Wuenscher-Name,
   Position und drei Knoepfe nebeneinander).

   Die App schaltet erst bei 460px auf ihre schmalste Stufe. Darunter
   bekommt der Text jetzt eine eigene Reihe ueber die volle Breite, statt
   sich den Platz mit Name und Knoepfen zu teilen.
   -------------------------------------------------------------------------- */
@media (max-width: 380px){
  .djview-row-wish{
    grid-template-columns: auto minmax(0, 1fr) !important;
    grid-template-areas:
      "links rechtsoben"
      "links rechtsunten"
      "mitte mitte"
      "warnung warnung" !important;
    row-gap: 3px;
  }
  .djr-mitte{ padding-left: 2px; }
  .djview-row-wish .row-title{ white-space: normal; }
}

/* Unter 620px hat auch die "breite" DJ-Zeile nicht mehr genug Platz fuer
   fuenf Spalten nebeneinander - beim Pruefen quetschte sie Titel und Chip
   ineinander. Dort bricht sie ebenfalls um. */
@media (max-width: 620px){
  .djview-table:not(.djview-schmal) .djview-row-wish:has(.erk-ecke){
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas:
      "links mitte rechtsoben"
      "links mitte rechtsunten"
      "warnung warnung warnung";
    padding-bottom: 5px;
  }
  .djview-table:not(.djview-schmal) .erk-ecke{
    justify-self: stretch; justify-content: flex-start;
    max-width: none; height: 19px;
  }
  .djview-table:not(.djview-schmal) .erk-p{ margin-left: auto; }
}

/* ==========================================================================
   v1.25.0 - DIE DOPPEL-WARNUNG

   Nutzer-Vorgabe zur Farbe: "mach es nicht in rot das chip und den text...
   das sieht immer gefaehrlich aus mach es auch in dem turkis oder so aber
   das Dreieck darf gern das Warndreieck sein in gelb oder rot."
   ========================================================================== */
.doppel-badge{
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 9px; border-radius: 6px;
  font-size: 11px; font-weight: 650; white-space: nowrap;
  background: linear-gradient(135deg, rgba(45,212,191,.18), rgba(20,184,166,.08));
  color: #99f6e4; border: 1px solid rgba(45,212,191,.42);
}
/* Das Warndreieck ist das EINZIGE Signal - gelb, damit es auffaellt,
   ohne dass die ganze Zeile nach Gefahr aussieht. */
.doppel-badge .dz{ color: #fbbf24; font-size: 12px; line-height: 1; }
.doppel-p{
  font-variant-numeric: tabular-nums; font-weight: 800; opacity: .85;
  padding-left: 7px; border-left: 1px solid rgba(45,212,191,.3);
}
.doppel-badge.compact{ padding: 2px 5px; gap: 0; }

/* Der reine Interpreten-Hinweis: bewusst grau und ohne Leuchten.
   Nutzer: "wenn es wirklich nur der Interpret war, sollte das nicht so
   deutlich leuchten ... das kann deutlich dezenter als in diesem
   leuchtenden turkisen sein." */
/* ALLE Eigenschaften nennen, nicht nur die geaenderten: weiter oben in
   dieser Datei (Zeile ~1667) steht die alte Fassung in Blau mit
   border-radius:999px und font-weight:800. Wuerde ich hier nur die Farbe
   ueberschreiben, blieben Form und Fettung der alten Regel stehen. */
.already-played-badge{
  display: inline-flex; align-items: center; gap: 5px;
  flex: 0 0 auto;
  padding: 2px 8px; border-radius: 6px;
  font-size: 11px; font-weight: 550;
  white-space: nowrap;
  background: rgba(255,255,255,.05);
  color: var(--text-dim);
  border: 1px solid var(--border);
  box-shadow: none;
}
.already-played-badge.compact{ padding: 1px 4px; font-size: 11px; opacity: .55; }

html[data-theme="light"] .doppel-badge{
  background: linear-gradient(135deg, rgba(13,148,136,.14), rgba(13,148,136,.05));
  color: #0f766e; border-color: rgba(13,148,136,.38);
}
html[data-theme="light"] .doppel-badge .dz{ color: #b45309; }
html[data-theme="light"] .doppel-p{ border-left-color: rgba(13,148,136,.3); }
@media (prefers-color-scheme: light){
  html:not([data-theme]) .doppel-badge{
    background: linear-gradient(135deg, rgba(13,148,136,.14), rgba(13,148,136,.05));
    color: #0f766e; border-color: rgba(13,148,136,.38);
  }
  html:not([data-theme]) .doppel-badge .dz{ color: #b45309; }
  html:not([data-theme]) .doppel-p{ border-left-color: rgba(13,148,136,.3); }
}

/* --------------------------------------------------------------------------
   DIE ECKE IN DER KARTENANSICHT

   Beim Pruefen aufgefallen: Die Kartenansicht (.wish-card) zeigte die
   Erkennung bisher gar nicht - dort fehlte auch die gruene Zeilen-
   markierung. Jetzt bekommt sie beides.

   Oben RECHTS sitzt in der Karte der Status-Chip ("offen"/"gespielt"),
   deshalb sitzt die Fahne hier oben LINKS. Verdeckt wird dabei nichts:
   der Karteninhalt beginnt darunter.
   -------------------------------------------------------------------------- */
.wish-card{ position: relative; }
.wish-card:has(.erk-ecke){ padding-top: 26px; }
.wish-card .erk-ecke{
  left: 0; right: auto;
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 10px;
  /* Links KEINE Rundung: die Karte hat dort einen 4px-Kategoriestreifen
     (border-left), an dem die Fahne buendig anliegen soll. Gemessen sitzt
     sie 1px innerhalb der Karte - sie ragt also nicht heraus. */
  border-top-left-radius: 0;
  padding: 0 14px 0 12px;
  max-width: calc(100% - 90px);   /* der Status-Chip rechts bleibt frei */
}

/* --------------------------------------------------------------------------
   v1.25.0 - STEUERUNG DES FREMD-PLAYERS (TIDAL / SPOTIFY)

   Nutzer-Befund: "bei Tidal klappt das mit der mediensteuerung und der
   lautstaerke irgendwie nicht."

   IM QUELLCODE DES TIDAL-PLAYERS NACHGESEHEN
   (github.com/tidal-music/embed-player, src/client/js/message-bridge.js):
   Er nimmt ueber postMessage genau ZWEI Befehle an - 'play' und 'pause'.
   Lautstaerke und Spulen gibt es dort schlicht nicht. Was moeglich ist,
   wird jetzt genutzt; der Rest wird ehrlich benannt statt verschwiegen.
   -------------------------------------------------------------------------- */
.hoer-fremd{
  display: flex; align-items: center; gap: 9px;
  padding: 7px 10px;
  border-top: 1px solid var(--border);
}
.hoer-fremd-knopf{
  width: 30px; height: 30px; flex: 0 0 auto;
  border-radius: 7px; cursor: pointer;
  display: grid; place-items: center;
  font-size: 12px; line-height: 1;
  color: #86c9a4;
  border: 1px solid rgba(134,201,164,.32);
  background: rgba(134,201,164,.10);
}
.hoer-fremd-knopf:hover{
  color: #a5dbbe;
  border-color: rgba(134,201,164,.58);
  background: rgba(134,201,164,.17);
}
.hoer-fremd-zeit{
  font-size: 11.5px; font-variant-numeric: tabular-nums;
  color: var(--text-dim); flex: 0 0 auto; min-width: 72px;
}
/* .hoer-fremd-txt ist mit v1.25.1 entfallen - der Erklaertext im Fenster
   ist ersatzlos weg (Nutzer: "Der Text ist absolut ueberfluessig").
   Die Regel bleibt NICHT stehen, damit niemand sie spaeter fuer aktiv haelt. */
html[data-theme="light"] .hoer-fremd-knopf{
  color: #2f7d55; border-color: rgba(47,125,85,.32); background: rgba(47,125,85,.09);
}
@media (prefers-color-scheme: light){
  html:not([data-theme]) .hoer-fremd-knopf{
    color: #2f7d55; border-color: rgba(47,125,85,.32); background: rgba(47,125,85,.09);
  }
}

/* --------------------------------------------------------------------------
   v1.25.1 - DIE LEISTE UNTER DEM FREMD-PLAYER

   Nutzer-Befund: "Der Text ist absolut ueberfluessig und der Playbutton ist
   irgendwie nicht ok wenn er nicht richtig funktioniert. was soll dieses
   -:--- daneben eigentlich?"

   Der vierzeilige Erklaertext ist ersatzlos weg. Uebrig bleibt eine Zeile:
   Start/Pause, die Zeit (die jetzt wirklich laeuft) und ein kleiner
   Hinweis, dass es hier keinen Regler gibt - mit der Begruendung im
   Tooltip statt als Textwand.
   -------------------------------------------------------------------------- */
.hoer-fremd{
  display: flex; align-items: center; gap: 9px;
  padding: 8px 11px;
  border-top: 1px solid var(--border);
}
.hoer-fremd-knopf{
  width: 30px; height: 30px; flex: 0 0 auto;
  border-radius: 50%; cursor: pointer;
  display: grid; place-items: center;
  font-size: 11px; line-height: 1;
  color: #86c9a4;
  border: 1px solid rgba(134,201,164,.34);
  background: rgba(134,201,164,.11);
  transition: color .15s, border-color .15s, background .15s;
}
.hoer-fremd-knopf:hover{
  color: #a5dbbe; border-color: rgba(134,201,164,.6); background: rgba(134,201,164,.18);
}
/* Laeuft gerade: rot, wie beim Vorhoer-Knopf in der Pruefliste auch. */
.hoer-fremd-knopf.laeuft{
  color: #e0a0a0; border-color: rgba(224,160,160,.38); background: rgba(224,160,160,.12);
}
.hoer-fremd-knopf.laeuft:hover{
  color: #f0b8b8; border-color: rgba(224,160,160,.62); background: rgba(224,160,160,.19);
}
.hoer-fremd-zeit{
  font-size: 11.5px; font-variant-numeric: tabular-nums;
  color: var(--text-dim); flex: 1 1 auto; min-width: 0;
}
/* Klein und beilaeufig - die Begruendung steht im Tooltip, nicht im Fenster. */
.hoer-fremd-rest{
  font-size: 10px; color: var(--text-dim); opacity: .6;
  flex: 0 0 auto; cursor: help; white-space: nowrap;
}
.hoer-fremd-rest:hover{ opacity: .95; }

html[data-theme="light"] .hoer-fremd-knopf{
  color: #2f7d55; border-color: rgba(47,125,85,.34); background: rgba(47,125,85,.10);
}
html[data-theme="light"] .hoer-fremd-knopf.laeuft{
  color: #a8443f; border-color: rgba(168,68,63,.36); background: rgba(168,68,63,.10);
}
@media (prefers-color-scheme: light){
  html:not([data-theme]) .hoer-fremd-knopf{
    color: #2f7d55; border-color: rgba(47,125,85,.34); background: rgba(47,125,85,.10);
  }
  html:not([data-theme]) .hoer-fremd-knopf.laeuft{
    color: #a8443f; border-color: rgba(168,68,63,.36); background: rgba(168,68,63,.10);
  }
}

/* v1.25.3 - Die Regeln fuer den Tab-Lautstaerkeregler (.hoer-tablaut, .tl-*)
   sind entfernt. Der Regler funktionierte nicht: suppressLocalAudioPlayback
   schaltet den ganzen Tab stumm, also auch die zurueckgespielte Fassung.
   Ausfuehrliche Begruendung in public/js/app.js, Abschnitt v1.25.3. */

/* v1.25.4 - Woher kommt der Ton? Steht ueber dem Lautstaerkeregler, damit
   niemand raten muss, welche Aufnahme gerade laeuft. */
.hoer-quelle{
  display: flex; align-items: center; gap: 7px;
  padding: 6px 11px;
  font-size: 10.5px; color: var(--text-dim);
  border-top: 1px solid var(--border);
}
.hq-punkt{
  width: 6px; height: 6px; border-radius: 50%; flex: 0 0 auto;
  background: #86c9a4; box-shadow: 0 0 5px rgba(134,201,164,.6);
}

/* ==========================================================================
   v1.25.7 - DIE REGLERLEISTE OBEN IN DER PRUEFLISTE IST WEG

   Nutzer-Befund: "der Text neben dem pegelregler ist aber momentan fuer die
   grad ausgelieferte version quatsch weil der gar nix regelt."

   Das stimmt. Der Weg dorthin, kurz festgehalten, damit ihn niemand noch
   einmal geht:
     - Eine Webseite kann den Player eines fremden Anbieters (TIDAL, Spotify)
       nicht leiser drehen. Der laeuft in einem fremden iframe, und die
       Anbieter nehmen ueber postMessage nur 'play' und 'pause' entgegen.
     - Die Tab-Freigabe (getDisplayMedia) nahm den Ton zwar auf - der Pegel
       schlug aus -, aber suppressLocalAudioPlayback schaltet dabei die
       Tonausgabe des GANZEN Tabs stumm. Gemessen, nicht vermutet.
     - Blieb also nur noch die Apple-Hoerprobe, und die laeuft ohnehin im
       eigenen Hoerfenster, wo der Regler schon steht.

   Damit hatte die Leiste hier oben nichts mehr zu regeln. Entfallen sind:
   .laut-leiste, .ll-sym, .ll-wert, .ll-trenn, .ll-tab, .ll-an, .ll-pegel,
   .ll-pegel-fuell, .ll-tab-txt, .ll-aus, .ll-grau, .ll-hinw.

   Der Regler im Hoerfenster (.hoer-regler, siehe weiter oben) bleibt - er
   wirkt auf eine echte Tondatei und funktioniert zuverlaessig.
   ========================================================================== */

/* ==========================================================================
   v1.26.0 - DER HERKUNFTSBLOCK IM HOERFENSTER

   Nutzer-Wunsch, woertlich:
     "100% nur bei echten isrc oder wie das heisst und ansonsten soll die
      100% etc. in dem vorschaufenster gelb statt gruen sein oder so damit
      man weiss OK es ging ueber den Namen"

   Genau zwei Zustaende, sofort unterscheidbar:
     .sicher      GRUEN  - ueber die Kennung der Aufnahme gefunden.
                           Das ist bewiesen dieselbe Aufnahme, immer 100 %.
     .geschaetzt  GELB   - ueber Titel und Interpret gefunden.
                           Sehr wahrscheinlich richtig, aber nicht bewiesen -
                           auch dann, wenn dort 100 % steht.

   Die Farbe sitzt auf dem Prozentwert UND auf dem linken Rand, damit sie
   auch beim schnellen Blick auffaellt.
   ========================================================================== */
.hoer-herkunft{
  padding: 7px 10px;
  border-top: 1px solid var(--border);
  border-left: 3px solid transparent;
  display: flex; flex-direction: column; gap: 2px;
  font-size: 11px; line-height: 1.35;
}
.hoer-herkunft.sicher{
  border-left-color: #5ec98e;
  background: rgba(94,201,142,.07);
}
.hoer-herkunft.geschaetzt{
  border-left-color: #e0a45e;
  background: rgba(224,164,94,.07);
}

.hh-kopf{ display: flex; align-items: center; gap: 7px; min-width: 0; }

.hh-wert{
  flex: 0 0 auto; font-weight: 700; font-size: 12px;
  font-variant-numeric: tabular-nums; cursor: help;
}
.hoer-herkunft.sicher .hh-wert{ color: #5ec98e; }
.hoer-herkunft.geschaetzt .hh-wert{ color: #e0a45e; }

/* Die Suchart - der Satz, der sagt, WIE gefunden wurde. Er darf kuerzen,
   der volle Text steht im Tooltip des Prozentwerts. */
.hh-art{
  flex: 1 1 auto; min-width: 0; color: var(--text-dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.hh-quelle{
  flex: 0 0 auto; font-weight: 600; color: var(--text);
  padding: 1px 6px; border-radius: 5px;
  background: rgba(255,255,255,.07); border: 1px solid var(--border);
  font-size: 10px;
}

/* Der Link zum Nachpruefen. Nutzer: "am besten mit dem Link zu dem Song
   das man das manuell pruefen kann". */
.hh-link{
  flex: 0 0 auto; text-decoration: none; font-size: 12px;
  color: var(--text-dim); padding: 0 2px; border-radius: 4px;
}
.hh-link:hover{ color: var(--accent, #8b5cf6); background: rgba(139,92,246,.12); }

/* Titel und Interpret der HOERPROBE - voll ausgeschrieben, damit man sieht,
   was da wirklich spielt. Nutzer: "sowie natuerlich Titel und Interpreten
   ALLE voll ausgeschrieben". Deshalb KEIN Abschneiden, sondern Umbruch. */
.hh-titel{
  color: var(--text); font-weight: 600;
  overflow-wrap: anywhere;
}
.hh-interpret{
  color: var(--text-dim);
  overflow-wrap: anywhere;
}
.hh-isrc{
  color: var(--text-dim); opacity: .65;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 10px; letter-spacing: .3px; cursor: help;
}

html[data-theme="light"] .hoer-herkunft.sicher{ background: rgba(94,201,142,.10); }
html[data-theme="light"] .hoer-herkunft.geschaetzt{ background: rgba(224,164,94,.12); }
html[data-theme="light"] .hh-quelle{ background: rgba(0,0,0,.04); }
@media (prefers-color-scheme: light){
  html:not([data-theme]) .hoer-herkunft.sicher{ background: rgba(94,201,142,.10); }
  html:not([data-theme]) .hoer-herkunft.geschaetzt{ background: rgba(224,164,94,.12); }
  html:not([data-theme]) .hh-quelle{ background: rgba(0,0,0,.04); }
}

/* ==========================================================================
   v1.27.1 - KOMPRESSION. Nutzer-Vorgabe, woertlich:
   "maximale (aber noch lesbare) kompression ... es muss nicht 100pxl
   zwischen den buttons sein, 10 oder 20 reichen ... alles rueckt an die
   Raender, mehr leere Flaeche in der MITTE - wenn man den Browser
   zusammenschiebt wird nicht gleich der ganze text gekuerzt."
   Und: "die Knopfzeilen unter den Treffern und das Ablehnen-Menue klauen
   an Stellen Platz, die nicht not tun."
   Die AUSNAHME steht auch fest: die Erkannt-Fahne (.erk-ecke) bleibt so
   breit, wie sie fuer Lesbarkeit braucht - sie wird hier NICHT angefasst.
   Die Aenderungen unten ueberschreiben gezielt die Abstands-Werte der
   Ursprungsregeln weiter oben; die Begruendungen dort gelten weiter
   (feste Spaltenbreiten fuers Fluchten bleiben fest, nur kleiner).
   ========================================================================== */

/* --- DJ-Zeile: links alles an den Rand ---------------------------------- */
.djview-row.djview-row-wish{ column-gap: 6px; padding: 4px 6px 4px 2px; }
.djr-links{ gap: 3px; }
.drag-handle{ padding: 2px 1px; }
.djview-row .row-cat{ flex-basis: 80px; width: 80px; }

/* --- DJ-Zeile: rechts die Luecken halbieren ------------------------------ */
.djr-oben{ gap: 3px; }
.djr-unten{ gap: 3px; }
.djview-from{ flex-basis: 104px; width: 104px; gap: 3px; }
.djview-actions{ gap: 3px; flex-basis: 118px; width: 118px; }
.pos-badge-lg, .djview-row .pos-badge-lg{ padding: 3px 7px; min-width: 30px; margin-left: 0; font-size: 13px; }

/* --- Pruefen-Tab: Knopfzeile, Selbst-Suche, Einordnung ------------------- */
.tw-acts{ padding: 8px 12px 10px; gap: 6px; margin-top: 8px; }
.tw-own{ padding: 5px 12px 0; }
.tw-cands{ padding: 8px 12px 4px; gap: 6px; }
.tw-einordnung{ gap: 8px; margin-top: 6px; padding: 6px 8px; }

/* --- Pruefen-Tab: das Ablehnen-Menue ------------------------------------- */
.tw-rej{ margin: 0 12px 10px; padding: 9px 10px; }
.tw-rej-t{ margin-bottom: 6px; }
.tw-rej input{ padding: 7px 10px; }
.tw-rej-msg{ margin: 8px 0; }
.tw-rej-note{ margin-top: 6px; }

/* ==========================================================================
   v1.27.2 - NACHSCHLIFF KOMPRESSION (Nutzer-Screenshots mit X und Balken)
   ========================================================================== */

/* --- DJ breit: Der Totraum steckte IN der Knopfgruppe -------------------
   Gemessen: Kasten 118 px fest, Knoepfe nur ~87 px - der Rest stand als
   Leerraum LINKS der Knoepfe, genau unter Saschas rotem X. Die feste
   Breite stammte aus dem Spalten-Fluchten (v1.3.4.1); Wunschzeilen haben
   aber ohnehin gleich viele Knoepfe, und Sonderfaelle (Alarm) hatten schon
   immer Ausnahmen. Jetzt: so breit wie der Inhalt, buendig rechts -
   Nummer und Chip ruecken dicht an die Knoepfe, die Leere liegt in der
   Mitte der Zeile. */
.djview-actions, .djview-row .djview-actions{
  flex: 0 0 auto; width: auto; min-width: 0;
}

/* --- DJ schmal: EINE Zeile rechts statt zwei, Anfasser ans Cover --------
   Nutzer: "rechts die beiden zeilen zusammenfuehren so dass eine zeile in
   der hoehe gespart wird ... links die zeile vom verschiebemechanismus
   weg, der direkt (sehr dicht) beim songcover." */
.djview-table.djview-schmal .djview-row-wish{
  grid-template-columns: auto minmax(0, 1fr) auto auto !important;
  grid-template-areas:
    "links . rechtsoben rechtsunten"
    "mitte mitte mitte mitte"
    "warnung warnung warnung warnung" !important;
  row-gap: 2px;
  padding: 3px 6px 3px 2px;
}
/* Die linke Spalte war seit v1.14.x eine SAEULE (Griff/Chip/Cover
   untereinander). Nutzer-Wunsch jetzt: eine flache REIHE, der Anfasser
   "direkt (sehr dicht) beim songcover" - [STD][anfasser][cover]. Die
   Saeulen-Regel weiter oben wird hier gezielt zurueckgedreht. */
.djview-table.djview-schmal .djr-links{
  flex-direction: row;
  align-items: center;
  align-self: center;
  gap: 2px;
  padding-top: 0;
  width: auto;
}
.djview-table.djview-schmal .djr-links .row-cat{ order: -1; width: 46px; }

/* --- Pruefen: "Keiner passt" + Uebernommen-Hinweis in EINER Zeile ------- */
.tw-fussleiste{
  display: flex; align-items: stretch; gap: 8px;
  padding: 5px 12px 0; flex-wrap: wrap;
}
.tw-fussleiste .tw-own{ padding: 0; flex: 0 1 auto; }
.tw-fussleiste .tw-carry{ flex: 1 1 260px; margin: 0; }
/* Bei offener Live-Suche braucht das Suchfeld die volle Breite. */
.tw-fussleiste.offen{ display: block; }
.tw-fussleiste.offen .tw-carry{ margin-top: 8px; }

/* Der Selbst-Suche-Knopf: halb so hoch, nicht mehr vollbreit. */
.tw-own-btn{
  width: auto; padding: 6px 14px; font-size: 12px;
}
/* Der Hinweiskasten ebenfalls kompakt (Innenabstand halbiert; die alte
   Aussenmarge gilt nur noch, wenn er mal ohne Fussleiste steht). */
.tw-carry, .tw-carry.duenn{ padding: 6px 10px; font-size: 11.5px; margin: 8px 12px 0; }
.tw-fussleiste .tw-carry, .tw-fussleiste .tw-carry.duenn{ margin: 0; }

/* Die grossen Aktionsknoepfe im Pruefen-Tab: halbe Hoehe reicht. */
.tw-acts .btn{ padding: 6px 12px; font-size: 12.5px; }

/* ===========================================================================
   v1.27.5 - ABGESTUFTE DUPLIKAT-CHIPS ("chip heller oder dunkler
   hervorheben je nach staerke der uebereinstimmung" - Sascha, 30.08.).
   Grundfarbe bleibt das Tuerkis von v1.25.0; nur die Intensitaet wandert:
   stark (ISRC, sicher) leuchtet am kraeftigsten, mittel (andere Version)
   wie bisher etwas ruhiger, leicht (nur Songname) ist bewusst gedimmt -
   ein Hinweis, keine Warnung. "inliste" (Treffer in der OFFENEN Liste
   statt bei den gespielten) bekommt eine gestrichelte Kante, damit man
   die beiden Faelle auf einen Blick auseinanderhaelt.
   ========================================================================= */
.doppel-badge.stark{
  background: linear-gradient(135deg, rgba(45,212,191,.34), rgba(20,184,166,.18));
  border-color: rgba(45,212,191,.75);
  color: #ccfbf1;
}
.doppel-badge.stark .dz{ color: #34d399; }
.doppel-badge.mittel{
  background: linear-gradient(135deg, rgba(45,212,191,.13), rgba(20,184,166,.06));
  border-color: rgba(45,212,191,.32);
  color: #7edcd0;
}
.doppel-badge.leicht{
  background: rgba(148,163,184,.08);
  border-color: rgba(148,163,184,.28);
  color: #94a3b8;
  font-weight: 550;
}
.doppel-badge.leicht .dz{ color: #94a3b8; }
.doppel-badge.leicht .doppel-p{ border-left-color: rgba(148,163,184,.3); opacity: .7; }
.doppel-badge.inliste{ border-style: dashed; }
html[data-theme="light"] .doppel-badge.stark{
  background: rgba(13,148,136,.16); border-color: rgba(13,148,136,.55); color: #0f766e;
}
html[data-theme="light"] .doppel-badge.stark .dz{ color: #059669; }
html[data-theme="light"] .doppel-badge.leicht{
  background: rgba(100,116,139,.08); border-color: rgba(100,116,139,.3); color: #64748b;
}
html[data-theme="light"] .doppel-badge.leicht .dz{ color: #64748b; }
@media (prefers-color-scheme: light){
  html:not([data-theme]) .doppel-badge.stark{
    background: rgba(13,148,136,.16); border-color: rgba(13,148,136,.55); color: #0f766e;
  }
  html:not([data-theme]) .doppel-badge.stark .dz{ color: #059669; }
  html:not([data-theme]) .doppel-badge.leicht{
    background: rgba(100,116,139,.08); border-color: rgba(100,116,139,.3); color: #64748b;
  }
  html:not([data-theme]) .doppel-badge.leicht .dz{ color: #64748b; }
}

/* ===========================================================================
   v1.28.0 - Pruefliste unter "Chat-Rechte speichern": wen hat Twitch
   wirklich gefunden (Bild + Anzeigename + Anmeldename), wen nicht.
   ========================================================================= */
.eb-pruefung-liste{ display: flex; flex-wrap: wrap; gap: 6px; margin-top: 2px; }
.eb-pruefung-konto{
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 10px 3px 4px; border-radius: 999px;
  background: rgba(45,212,191,.1); border: 1px solid rgba(45,212,191,.3);
  font-size: 12px;
}
.eb-pruefung-konto img{ width: 20px; height: 20px; border-radius: 50%; }

/* ===========================================================================
   v1.28.3 - VARIANTE A (Saschas Wahl): farbige Stufen-Chips, Zugsymbol,
   Stream-Gruppen im Support-Verlauf.
   ========================================================================= */
.tier-chip{
  display: inline-flex; align-items: center; vertical-align: 1px;
  border-radius: 5px; padding: 1px 6px;
  font-size: 10.5px; font-weight: 800; letter-spacing: .4px; line-height: 1.5;
}
.tier-chip.t2{ background: rgba(167,139,250,.16); color: #c4b5fd; border: 1px solid rgba(167,139,250,.45); }
.tier-chip.t3{ background: rgba(251,191,36,.14); color: #fcd34d; border: 1px solid rgba(251,191,36,.5); }
.tier-chip.voraus{ background: rgba(45,212,191,.13); color: #7edcd0; border: 1px solid rgba(45,212,191,.4); font-weight: 700; }
html[data-theme="light"] .tier-chip.t2{ background: rgba(124,58,237,.1); color: #6d28d9; border-color: rgba(124,58,237,.4); }
html[data-theme="light"] .tier-chip.t3{ background: rgba(180,83,9,.1); color: #b45309; border-color: rgba(180,83,9,.45); }
html[data-theme="light"] .tier-chip.voraus{ background: rgba(13,148,136,.1); color: #0f766e; border-color: rgba(13,148,136,.4); }
@media (prefers-color-scheme: light){
  html:not([data-theme]) .tier-chip.t2{ background: rgba(124,58,237,.1); color: #6d28d9; border-color: rgba(124,58,237,.4); }
  html:not([data-theme]) .tier-chip.t3{ background: rgba(180,83,9,.1); color: #b45309; border-color: rgba(180,83,9,.45); }
  html:not([data-theme]) .tier-chip.voraus{ background: rgba(13,148,136,.1); color: #0f766e; border-color: rgba(13,148,136,.4); }
}
.sup-zug{ font-size: 12px; opacity: .9; cursor: help; }
.sup-stream{ margin-top: 6px; }
.sup-stream-kopf{ cursor: pointer; user-select: none; list-style: none; }
.sup-stream-kopf::-webkit-details-marker{ display: none; }
.sup-stream-kopf::before{ content: '▸ '; opacity: .7; }
.sup-stream[open] > .sup-stream-kopf::before{ content: '▾ '; }

/* ============================================================================
   v1.28.17 - Auto-Nachrichten (Notizen-Tab unten)
   ============================================================================ */
.an-block .notiz-kopf{ display:flex; align-items:flex-start; gap:10px; }
.an-zeile{ background: var(--bg-elevated); border:1px solid var(--border); border-radius:10px; padding:8px 12px; margin:8px 0; }
.an-zeile.an-aus{ opacity:.62; }
/* --- v1.31.18: Die Kopfzeile hat ZWEI Spalten, nicht eine Reihe ---------
   Saschas Befund nach v1.31.17: "der Zeilenumbruch ist immernoch da!"

   Er hatte recht. v1.31.17 hat nur verhindert, dass ein einzelner Knopf
   allein umbricht - die GANZE Knopfgruppe rutschte weiterhin unter die
   Chips, sobald Titel und Chips die Zeile fuellten. Bei einem kurzen Titel
   fiel es nicht auf, bei "Stream-Zusammenfassung" schon.

   Eine Reihe mit flex-wrap kann das gar nicht loesen: Sie kennt nur eine
   Sorte Umbruchstelle, und die Knoepfe sind die letzte davon. Zwei feste
   Spalten koennen es: links waechst nach unten, rechts steht still.
   Deshalb Grid statt Flex. */
.an-kopfzeile{ display:grid; grid-template-columns: minmax(0,1fr) auto;
  align-items:start; gap:6px 10px; }
.an-kopflinks{ display:flex; align-items:center; gap:8px; flex-wrap:wrap;
  min-width:0; }
.an-kopflinks .z-fuell{ flex:1 1 auto; }
/* Die Knoepfe: eigene Spalte, rechtsbuendig, und nie umbrechend. Dadurch
   stehen sie ueber alle Zeilen hinweg buendig untereinander. */
.an-knopfgruppe{ display:flex; align-items:center; gap:6px; flex:none;
  justify-self:end; flex-wrap:nowrap; }
.an-pfeile{ display:flex; align-items:center; gap:2px; flex:none; }
.an-pfeile .icon-btn{ width:22px; }
.an-werkzeuge{ display:flex; align-items:center; gap:3px; flex:none; }

@media (max-width: 640px){
  /* Am Handy ist fuer zwei Spalten kein Platz: die Knoepfe bekommen eine
     eigene Zeile unter der Kopfzeile, rechtsbuendig. Das ist dann eine
     Entscheidung und sieht auch so aus - nicht wie ein verrutschter Knopf. */
  .an-kopfzeile{ grid-template-columns: 1fr; }
  .an-knopfgruppe{ justify-self:end; }
}
.an-nr{ color: var(--accent); white-space:nowrap; }
.an-titel{ font-weight:600; cursor:pointer; }
.an-titel:hover{ text-decoration:underline dotted; }
.an-cmd{ background: var(--bg); border:1px solid var(--border); border-radius:6px; padding:1px 6px; font-size:12px; }
.an-warn{ color: var(--warn); font-size:12px; white-space:nowrap; }
.an-badge{ font-size:11px; border:1px solid var(--border); border-radius:999px; padding:2px 8px; color: var(--text-dim); white-space:nowrap; }
.an-badge.an{ border-color: color-mix(in srgb, var(--success) 55%, var(--border)); color: var(--success); }
.an-schalter{ width:34px; height:19px; border-radius:999px; border:none; background:#4a4761; position:relative; cursor:pointer; flex:none; padding:0; }
.an-schalter::after{ content:''; position:absolute; top:2px; left:2px; width:15px; height:15px; border-radius:50%; background:#fff; transition:left .15s; }
.an-schalter.an{ background: var(--accent); }
.an-schalter.an::after{ left:17px; }
.an-split{ display:inline-flex; gap:6px; }
.an-vorschauzeile{ color: var(--text-dim); font-size:13px; margin:4px 0 0 42px; cursor:pointer;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.an-zuletzt{ color: var(--text-dim); font-size:11px; margin:2px 0 0 42px; }
/* v1.28.21: sichtbarer Sperrgrund einer faelligen, aber zurueckgehaltenen
   Auto-Nachricht ("wartet: zu wenig Chat-Aktivitaet") */
.an-wartet{ color:#eab308; font-size:11px; margin:2px 0 0 42px; }
/* v1.28.21: Bits-Messlauf - Zeilen mit positiver Differenz leuchten */
.bits-diff td{ color:#eab308; }
/* v1.28.21: echter Baustein-Wert im Baustein-Fenster; "leer" bleibt blass,
   damit man sieht, wo im Kanal gerade nichts steht */
.an-popup-wert{ font-size:11px; color:var(--accent); margin-left:auto; white-space:nowrap; }
.an-popup-wert.leer{ color:var(--text-dim); font-style:italic; }
.rt-leerhinweis{ font-size:11px; color:#eab308; margin:4px 0 0; }
.rt-leerhinweis code{ background:none; color:#eab308; }
.an-editor{ background: var(--bg-card); border:1px solid color-mix(in srgb, var(--accent) 40%, var(--border));
  border-radius:10px; padding:12px; margin:6px 0 12px; }
.an-editor-zeile{ display:flex; gap:14px; flex-wrap:wrap; align-items:flex-end; margin:8px 0; }
.an-feld{ display:flex; flex-direction:column; gap:4px; }
.an-feld > span{ font-size:12px; color: var(--text-dim); }
.an-feld input[type=text], .an-feld input[type=number], .an-feld textarea{
  background: var(--bg); border:1px solid var(--border); border-radius:8px; color: var(--text);
  padding:7px 9px; font:inherit; font-size:14px; }
.an-feld textarea{ resize:vertical; min-height:64px; }
.an-cmd-eingabe{ display:flex; align-items:center; gap:4px; }
.an-cmd-eingabe b{ color: var(--accent); }
.an-cmd-eingabe input{ flex:1; min-width:90px; }
.an-haken{ display:flex; align-items:center; gap:6px; font-size:13px; cursor:pointer; padding-bottom:6px; }
.an-seg{ display:inline-flex; border:1px solid var(--border); border-radius:8px; overflow:hidden; }
.an-seg-knopf{ background: var(--bg); color: var(--text-dim); border:none; padding:6px 12px; cursor:pointer; font:inherit; font-size:13px; }
.an-seg-knopf + .an-seg-knopf{ border-left:1px solid var(--border); }
.an-seg-knopf.an{ background: var(--accent); color: var(--accent-text); }
.an-iv{ display:inline-flex; align-items:center; gap:5px; }
.an-iv input{ width:58px; background: var(--bg); border:1px solid var(--border); border-radius:8px;
  color: var(--text); padding:7px 6px; font:inherit; font-size:14px; text-align:center; }
.an-iv b{ font-size:12px; color: var(--text-dim); font-weight:600; }
.an-iv-warnung{ color: var(--warn); font-size:12px; margin-top:3px; }
.an-minuten{ display:flex; flex-wrap:wrap; gap:5px; }
.an-minute{ background: var(--bg); border:1px solid var(--border); border-radius:8px; color: var(--text-dim);
  padding:4px 8px; cursor:pointer; font:inherit; font-size:13px; font-variant-numeric: tabular-nums; }
.an-minute.an{ background: var(--accent); border-color: var(--accent); color: var(--accent-text); }
.an-chips{ display:flex; flex-wrap:wrap; gap:6px; align-items:center; margin:6px 0; }
.an-chip{ background: color-mix(in srgb, var(--accent) 18%, var(--bg)); border:1px solid color-mix(in srgb, var(--accent) 55%, var(--border));
  color: color-mix(in srgb, var(--accent) 60%, var(--text)); border-radius:6px; padding:3px 8px; cursor:pointer;
  font-family:ui-monospace, Consolas, monospace; font-size:12px; }
.an-chip-eigen{ background: color-mix(in srgb, var(--success) 15%, var(--bg)); border-color: color-mix(in srgb, var(--success) 50%, var(--border));
  color: color-mix(in srgb, var(--success) 60%, var(--text)); }
.an-editor-fuss{ display:flex; gap:8px; align-items:center; margin-top:10px; }
.an-plan{ background: var(--bg-elevated); border:1px solid var(--border); border-radius:10px; padding:7px 12px; margin:8px 0; }
.an-plan summary{ cursor:pointer; font-size:13px; color: var(--text-dim); list-style:none; }
.an-plan summary::-webkit-details-marker{ display:none; }
.an-plan summary::before{ content:'▸ '; opacity:.7; }
.an-plan[open] summary::before{ content:'▾ '; }
.an-plan-eintrag{ white-space:nowrap; }
.an-plan-eintrag b{ color: var(--accent); font-variant-numeric: tabular-nums; }
.an-plan-liste{ display:flex; flex-direction:column; gap:4px; margin-top:8px; max-height:220px; overflow-y:auto; font-size:13px; }
.an-var-zeile{ display:flex; align-items:center; gap:6px; margin:5px 0; }
.an-var-zeile input{ background: var(--bg); border:1px solid var(--border); border-radius:8px; color: var(--text);
  padding:6px 8px; font:inherit; font-size:13px; width:140px; }
.an-var-zeile .an-var-wert{ flex:1; width:auto; }
.an-var-zeile code{ color: var(--accent); }
.an-einst{ display:flex; flex-direction:column; gap:8px; align-items:flex-start; }
.an-einst label{ display:flex; align-items:center; gap:8px; font-size:13px; }
.an-einst input[type=number]{ width:70px; background: var(--bg); border:1px solid var(--border);
  border-radius:8px; color: var(--text); padding:6px; font:inherit; text-align:center; }
.an-notiz-auto-an{ color: var(--success); border-color: color-mix(in srgb, var(--success) 55%, var(--border)) !important; }

/* v1.28.18 - Baustein-Fenster + Emote-Auswahl im Auto-Nachrichten-Editor */
.an-popup-hinter{ position:fixed; inset:0; background:rgba(0,0,0,.55); z-index:900;
  display:flex; align-items:center; justify-content:center; padding:24px; }
.an-popup{ background: var(--bg-card); border:1px solid var(--border); border-radius:12px;
  max-width:660px; width:100%; max-height:80vh; overflow-y:auto; padding:14px 16px; }
.an-popup-kopf{ display:flex; align-items:center; gap:10px; margin-bottom:8px; flex-wrap:wrap; }
.an-popup-gruppe{ margin:10px 0; }
.an-popup-gruppenname{ font-size:11px; letter-spacing:.06em; text-transform:uppercase;
  color: var(--text-dim); margin-bottom:4px; }
.an-popup-zeile{ display:flex; align-items:center; gap:10px; padding:3px 0; flex-wrap:wrap; }
.an-popup-hinweis{ color: var(--text-dim); font-size:12.5px; flex:1; min-width:180px; }
.an-popup-tw input{ background: var(--bg); border:1px solid var(--border); border-radius:8px;
  color: var(--text); padding:5px 8px; font:inherit; font-size:13px; width:160px; }
.an-emotes{ background: var(--bg-elevated); border:1px solid var(--border); border-radius:10px;
  padding:7px 12px; margin:8px 0; }
.an-emotes summary{ cursor:pointer; font-size:13px; color: var(--text-dim); list-style:none; }
.an-emotes summary::-webkit-details-marker{ display:none; }
.an-emotes summary::before{ content:'▸ '; opacity:.7; }
.an-emotes[open] summary::before{ content:'▾ '; }
.an-emotes .rt-emote-kopf{ margin-top:8px; }

/* v1.28.19 - Event-Zeilen (angelegt = Lese-Ansicht) */
.an-event-zeile{ display:flex; align-items:center; gap:10px; background: var(--bg-elevated);
  border:1px solid var(--border); border-radius:10px; padding:7px 12px; margin:5px 0; flex-wrap:wrap; }
.an-event-zeile.an-aus{ opacity:.55; }
.an-event-datum{ font-variant-numeric: tabular-nums; color: var(--accent); white-space:nowrap; }
.an-event-formular{ margin-top:8px; }

/* v1.28.20 - Event-Werbe-Stopp-Marke */
.an-badge-event{ border-color: rgba(245,158,11,.6); color: var(--warn); }


/* ==========================================================================
   v1.28.25 - KOMPAKTER UND RUHIGER

   Saschas Befund 11.09. zum Screenshot "Medienwiedergabe": "schaue bitte
   auch, ob du solche texte und eingabefelder wie im screenshot so gross
   brauchst und so ausfuehrlich ... auch die editfelder sind streckenweise
   sehr gross und weiss ... das sieht einfach unschoen aus."
   ========================================================================== */

/* Zahlenfelder brauchen keine halbe Fensterbreite. Sekunden, Prozente und
   Schwellen sind hoechstens dreistellig. */
.einst-inhalt input[type="number"] { max-width: 96px; }
.einst-inhalt input[type="time"], .einst-inhalt input[type="date"] { max-width: 170px; }
/* Textfelder duerfen breit sein, aber nicht endlos - ab einer gewissen
   Breite liest niemand mehr mit, und der weisse Balken dominiert die Seite. */
.einst-inhalt input[type="text"], .einst-inhalt input[type="url"],
.einst-inhalt input[type="search"], .einst-inhalt input[type="email"],
.einst-inhalt input[type="password"] { max-width: 460px; }
.einst-inhalt textarea { max-width: 720px; min-height: 58px; font-size: 13px; }
.einst-inhalt select { max-width: 320px; }
.einst-inhalt .field { margin-bottom: 10px; }
.einst-inhalt .field > label { margin-bottom: 4px; font-size: 12.5px; }

/* Die Erklaertexte: kleiner, ruhiger - und lange werden eingeklappt (siehe
   hinweiseKuerzen() in app.js). Der Text geht NICHT verloren, er wartet
   hinter "mehr". */
.einst-inhalt .hint { font-size: 12px; line-height: 1.5; opacity: .85; }
.einst-inhalt .hint-lang.zu {
  max-height: 3.1em; overflow: hidden; position: relative;
}
.einst-inhalt .hint-lang.zu::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 1.3em;
  background: linear-gradient(transparent, var(--bg-card, var(--bg-elevated)));
  pointer-events: none;
}
/* v1.28.26 - mittig unter dem Text, mit Strichen: er gehoert zum Absatz
   darueber, nicht zur Beschriftung darunter (genau das war verwirrend). */
/* Hoehere Spezifitaet als die Abstandsregel ".card > .hint + button"
   (12 px) - genau die hat den Knopf vom Text weggeschoben, obwohl er dort
   hingehoert. Gemessen und gezielt ueberstimmt statt mit !important. */
.hint-mehr, .card > .hint + .hint-mehr, .einst-inhalt .hint + .hint-mehr {
  display: block; width: 100%; margin: 0 0 10px; padding: 2px 0;
  background: none; border: 0; cursor: pointer; text-align: center;
  color: var(--accent); font: inherit; font-size: 11.5px; font-weight: 600;
  letter-spacing: .02em;
}
.hint-mehr:hover { text-decoration: underline; }

/* --------------------------------------------------------------------------
   SCHIEBESCHALTER STATT HAEKCHEN - aber nur dort, wo wirklich etwas ein-
   oder ausgeschaltet wird.

   Sascha: "die checkboxen die direkt etwas ein oder ausschalten gern mit dem
   neuen schiebeschalter ausser es sind absichtliche 'check' funktionen also
   sowas wie 'was darf einer sehen' und dann kommen da drunter aufzaehlungen,
   dann kann man es erstmal als checkbox lassen."

   v1.29.0 - WER umgestellt wird, entscheidet jetzt AUCH das Stylesheet.

   Saschas Befund: "manchmal zucken diese schalter irgendwie von der farbe
   als wenn sie kurz ausgeschaltet sind. NUR diese und UEBERALL auf der
   seite ... das dauert nur eine halbe sekunde oder sowas als wenn die seite
   refresht aber NUR bei den schaltern".

   Genau das war es: die Klasse .kipp setzte app.js NACH dem Rendern per JS.
   Zwischen "neu gezeichnet" und "Klasse gesetzt" lag ein Bildaufbau - in dem
   stand kurz das graue Kaestchen da. Deshalb steht die Regel jetzt im
   Stylesheet und greift im selben Moment, in dem das Element entsteht.
   Es kann nicht mehr zucken, weil es nie einen Zustand ohne Schalter gibt.

   .kipp bleibt zusaetzlich erlaubt (Markup ausserhalb der Einstellungen).
   Die Ausnahmen - Rechte-Raster und alles mit data-kasten="1" - holen sich
   weiter unten mit hoeherer Spezifitaet ihr Kaestchen zurueck.
   -------------------------------------------------------------------------- */
.einst-inhalt .checkbox-row > input[type="checkbox"],
input[type="checkbox"].kipp {
  appearance: none; -webkit-appearance: none;
  width: 34px; height: 19px; flex: 0 0 auto;
  border-radius: 999px; border: 1px solid var(--border);
  background: var(--bg-elevated);
  position: relative; cursor: pointer; margin: 0;
  transition: background .18s ease, border-color .18s ease, box-shadow .18s ease;
}
.einst-inhalt .checkbox-row > input[type="checkbox"]::after,
input[type="checkbox"].kipp::after {
  content: ''; position: absolute; top: 2px; left: 2px;
  width: 13px; height: 13px; border-radius: 50%;
  background: #F5F3F9; box-shadow: 0 1px 3px rgba(0,0,0,.35);
  transition: left .18s ease;
}
.einst-inhalt .checkbox-row > input[type="checkbox"]:checked,
input[type="checkbox"].kipp:checked {
  background: rgba(34,197,94,.22); border-color: var(--success);
  box-shadow: inset 0 0 8px rgba(34,197,94,.25);
}
.einst-inhalt .checkbox-row > input[type="checkbox"]:checked::after,
input[type="checkbox"].kipp:checked::after { left: 17px; }
.einst-inhalt .checkbox-row > input[type="checkbox"]:disabled,
input[type="checkbox"].kipp:disabled { opacity: .45; cursor: not-allowed; }
.einst-inhalt .checkbox-row > input[type="checkbox"]:focus-visible,
input[type="checkbox"].kipp:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
  .einst-inhalt .checkbox-row > input[type="checkbox"],
  .einst-inhalt .checkbox-row > input[type="checkbox"]::after,
  input[type="checkbox"].kipp, input[type="checkbox"].kipp::after { transition: none; }
}
/* Die Zeile darf dadurch nicht hoeher werden als vorher. */
.einst-inhalt .checkbox-row { margin-bottom: 6px; font-size: 13.5px; }

/* AUSNAHMEN - zurueck zum Kaestchen. Saschas Vorgabe woertlich: "ausser es
   sind absichtliche 'check' funktionen also sowas wie 'was darf einer sehen'
   und dann kommen da drunter aufzaehlungen, dann kann man es erstmal als
   checkbox lassen." Diese Selektoren sind absichtlich laenger als die Regel
   oben - nur so gewinnen sie in der Kaskade. */
.einst-inhalt fieldset .checkbox-row > input[type="checkbox"],
.einst-inhalt .perm-group .checkbox-row > input[type="checkbox"],
.einst-inhalt .rv-rechte .checkbox-row > input[type="checkbox"],
.einst-inhalt .checkbox-row > input[type="checkbox"][data-kasten="1"],
.einst-inhalt [data-kasten="1"] .checkbox-row > input[type="checkbox"] {
  appearance: auto; -webkit-appearance: auto;
  width: 17px; height: 17px; border-radius: 3px;
  background: none; border: none; position: static;
  box-shadow: none; cursor: pointer;
}
.einst-inhalt fieldset .checkbox-row > input[type="checkbox"]::after,
.einst-inhalt .perm-group .checkbox-row > input[type="checkbox"]::after,
.einst-inhalt .rv-rechte .checkbox-row > input[type="checkbox"]::after,
.einst-inhalt .checkbox-row > input[type="checkbox"][data-kasten="1"]::after,
.einst-inhalt [data-kasten="1"] .checkbox-row > input[type="checkbox"]::after { content: none; }

/* AUSRICHTUNG - Saschas Befund: "irgendwie sind die buttons immer ein stueck
   hoeher als der schriftsatz daneben". Gemessen waren es 5,5 px.

   Ursache war eine Spezifitaets-Falle (dieselbe wie in v1.25.0 bei .swa):
   .field > label { display: block } hat (0,2,1) und schlug .checkbox-row mit
   (0,1,0) - das Flex-Layout der Zeile war damit ausgehebelt und der Schalter
   stand als Inline-Element auf der Grundlinie statt in der Zeilenmitte.
   Deshalb stehen hier drei Selektoren, die zusammen jede Bauform treffen.
   Der Schalter ist 19 px hoch - line-height: 19px legt den Text exakt
   darauf. Restversatz nach der Messung: 0,5 px. */
.einst-inhalt .checkbox-row,
.einst-inhalt .field > label.checkbox-row,
.einst-inhalt .field label.checkbox-row {
  display: flex; align-items: flex-start; gap: 8px; line-height: 19px;
}
.einst-inhalt .checkbox-row > input[type="checkbox"] { margin-top: 0; }

/* TEXTFARBE - Sascha: "wenn es optionen sind, nicht so 'extrem' dezent
   sondern dann darf die Option selbst die man damit schaltet auch in weiss
   statt dunkel grau sein". Die Beschriftung des Schalters ist hell, der
   Erklaersatz darunter bleibt dezent. */
.einst-inhalt .checkbox-row,
.einst-inhalt .checkbox-row > label,
.einst-inhalt .checkbox-row > span,
.einst-inhalt .swa-text b { color: var(--text); font-weight: 600; }
.einst-inhalt .swa-text span { color: var(--text-dim); font-weight: 400; }

/* v1.29.0 - WARNKARTE "Mein Konto". Wird nur gebaut, wenn der eigene
   Ruecckanal wirklich tot ist (meinKontoWarnungHtml in app.js). */
.konto-warnung {
  border-color: rgba(234,179,8,.55);
  background: linear-gradient(180deg, rgba(234,179,8,.10), transparent 60%);
}
.konto-warnung h3 { margin-top: 0; }
/* Das kleine Warnzeichen am Menuepunkt der Einstellungsleiste. */
.sl-warnung { font-size: 11px; line-height: 1; margin: 8px 0 0 2px; flex: 0 0 auto; }

/* Flietext in den Einstellungen: er erklaert, er ist nicht die Hauptsache.
   Vorher stand er in 16-17 px und war groesser als die Bedienelemente. */
.einst-inhalt .card > p, .einst-inhalt .card > div > p { font-size: 13.5px; line-height: 1.5; }
.einst-inhalt h2 { font-size: 17px; }
.einst-inhalt h3 { font-size: 15px; }
/* Die Karten duerfen enger stehen - es sind Einstellungen, keine Plakate. */
.einst-inhalt .card { padding: 14px 16px; margin-bottom: 12px; }
/* Der Kartentitel, der nur den Seitentitel wiederholt, wird von
   doppelteUeberschriftAusblenden() markiert statt geloescht. */
.einst-inhalt .titel-doppelt { display: none; }

/* ==========================================================================
   v1.28.25 - ROLLBALKEN IM STIL DER APP

   Sascha: "bitte drauf achten, Scrollbalken wie besprochen nicht in diesem
   haesslichen alten win89 grauen stil". Einzelne Bereiche hatten schon
   eigene (DJ-Spalten, Team-Chat, Vorschlagslisten) - die Seite selbst, der
   neue Einstellungs-Inhalt und der Menuebaum aber nicht. Jetzt gilt es
   ueberall, in beiden Farbschemata.
   ========================================================================== */
:root {
  --roll-daumen: rgba(148, 163, 184, .34);
  --roll-daumen-hell: rgba(148, 163, 184, .60);
}
html[data-theme="light"] {
  --roll-daumen: rgba(90, 86, 110, .30);
  --roll-daumen-hell: rgba(90, 86, 110, .52);
}
@media (prefers-color-scheme: light) {
  html:not([data-theme]) {
    --roll-daumen: rgba(90, 86, 110, .30);
    --roll-daumen-hell: rgba(90, 86, 110, .52);
  }
}
* { scrollbar-width: thin; scrollbar-color: var(--roll-daumen) transparent; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--roll-daumen);
  border-radius: 99px;
  border: 2px solid transparent;
  background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover {
  background: var(--roll-daumen-hell);
  background-clip: padding-box;
}
::-webkit-scrollbar-corner { background: transparent; }

/* Die kleine Probe-Leuchte neben dem Staerke-Regler: derselbe Balken wie im
   Ernstfall, nur kurz - damit man beim Schieben sieht, was man bekommt. */
.alarm-probe {
  display: inline-flex; align-items: stretch;
  height: 26px; width: 22px; flex: 0 0 auto;
  border-radius: 6px; padding: 3px;
  background: color-mix(in srgb, var(--bg) 60%, var(--bg-elevated));
  border: 1px solid var(--border);
}
.alarm-probe .alarm-lightbar { margin: 0; width: 100%; }

/* ---- Kategorie-Zeile: die drei schmaleren Ansichten -------------------- */
/* Normal/schmal: Knoepfe und Haken rutschen in eine zweite Rasterzeile,
   die Felder behalten oben ihr Mass. */
@media (max-width: 1150px) {
  .cat-row {
    grid-template-columns: auto minmax(120px, 1fr) 6.5rem 4.2rem;
    row-gap: 6px;
  }
  .cat-row > label.checkbox-row { grid-column: 1 / 3; }
  .cat-row > .btn { grid-row: 2; }
}
/* Handy: Farbe und Name in einer Zeile, darunter Kuerzel und Zahl, darunter
   die Knoepfe - nichts wird gequetscht, nichts laeuft aus dem Bild. */
@media (max-width: 620px) {
  .cat-row {
    /* Dritte Spalte fest: dort steht die Reihenfolge, und die braucht Platz
       fuer 3-4 Ziffern - nicht mehr, aber auch nicht weniger. Mit einer
       auto-Spalte schrumpfte sie auf 30 px zusammen (gemessen). */
    grid-template-columns: auto 1fr 5.5rem;
    row-gap: 7px;
  }
  /* Auf dem Handy zaehlt jedes Pixel: hier gilt das feste Mass, nicht das
     mitwachsende Feld. */
  .cat-row .cat-order, .cat-row .cat-kuerzel { field-sizing: auto; }
  .cat-row > .cat-name { grid-column: 2 / 4; }
  /* Kuerzel und Zahl NEBENEINANDER unter dem Namen - beide mit Deckel:
     ein Kuerzel hat hoechstens 10 Zeichen, eine Reihenfolge 3-4 Ziffern. */
  .cat-row > .cat-kuerzel { grid-column: 2; width: 100%; max-width: 9rem; justify-self: start; }
  .cat-row > .cat-order { grid-column: 3; width: 100%; max-width: none; }
  .cat-row > label.checkbox-row { grid-column: 1 / 4; }
  .cat-row > .btn { grid-row: auto; }
}

/* v1.28.26 - Der Bot-Bereich fuer Team-Mitglieder, die ihn nicht verwalten
   duerfen: Status, Pruefen und Testnachricht in voller Farbe, die
   Einrichtungs- und Umzugsanleitungen daneben gedaempft. Lesbar bleibt
   alles - es ist nur sichtbar nicht ihre Baustelle. */
.bot-nur-lesen > details.settings-page-section { opacity: .55; }
.bot-nur-lesen > details.settings-page-section:hover,
.bot-nur-lesen > details.settings-page-section[open] { opacity: .85; }
.bot-nur-lesen .zd-aktionen .btn[disabled] { opacity: .4; cursor: not-allowed; }

/* ==========================================================================
   v1.28.26 - BELOHNUNGS-SETS ALS TABELLE

   Sascha: "so wie in exel spalten und zeilen die exakt zueinander passen.
   wenn es mehr werden, muss man ggf. scrollen koennen."

   Also eine echte <table>: Spaltenkopf = Set-Name (einmal, statt in jedem
   Knopf), Zeilenkopf = Belohnung. Beide bleiben beim Scrollen stehen, die
   Huelle scrollt waagerecht, sobald mehr Sets da sind als Platz.
   ========================================================================== */
.set-tabelle-huelle {
  overflow: auto;
  max-height: 58vh;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--bg) 45%, var(--bg-elevated));
}
.set-tabelle {
  border-collapse: separate;
  border-spacing: 0;
  width: 100%;
  font-size: 13px;
}
.set-tabelle th, .set-tabelle td {
  border-bottom: 1px solid var(--border);
  padding: 7px 9px;
  text-align: center;
  vertical-align: middle;
}
.set-tabelle tbody tr:last-child th,
.set-tabelle tbody tr:last-child td { border-bottom: 0; }

/* Kopfzeile bleibt oben stehen. */
.set-tabelle thead th {
  position: sticky; top: 0; z-index: 3;
  background: var(--bg-elevated);
  border-bottom: 2px solid var(--border);
  font-size: 12px; font-weight: 700; white-space: nowrap;
}
/* Die erste Spalte bleibt links stehen - sonst weiss man beim Scrollen
   nicht mehr, in welcher Zeile man ist. */
.set-tabelle .set-sp-name {
  position: sticky; left: 0; z-index: 2;
  background: var(--bg-elevated);
  text-align: left; min-width: 190px; max-width: 320px;
  font-weight: 600;
  box-shadow: 1px 0 0 var(--border);
}
.set-tabelle thead .set-sp-name { z-index: 4; }
.set-tabelle .set-zeile-name { display: block; line-height: 1.25; }
.set-tabelle .set-zeile-kosten {
  display: block; font-size: 11px; color: var(--text-dim); font-weight: 400;
}
.set-sp-set { min-width: 104px; }
.set-sp-titel { display: block; max-width: 150px; overflow: hidden; text-overflow: ellipsis; }
.set-sp-knoepfe { display: inline-flex; gap: 2px; margin-top: 2px; opacity: .7; }
.set-sp-knoepfe .icon-btn { width: 22px; height: 20px; font-size: 11px; }
.set-tabelle thead th:hover .set-sp-knoepfe { opacity: 1; }
.set-sp-aus { min-width: 92px; font-size: 11px !important; color: var(--text-dim); }

/* Die Haken: gross genug zum Treffen, ruhig genug zum Lesen. */
.set-haken {
  border: 0; background: none; cursor: pointer;
  font-size: 17px; line-height: 1; padding: 3px 7px; border-radius: 6px;
  color: var(--text-dim);
  transition: background .15s, color .15s;
}
.set-haken:hover { background: rgba(148,163,184,.16); color: var(--text); }
.set-haken.drin { color: var(--accent); }
.set-haken:disabled { opacity: .3; cursor: not-allowed; }
/* Die gesetzte Spalte bekommt einen leichten Grund - so sieht man die
   Zugehoerigkeit auch dann, wenn man quer ueber die Tabelle schaut. */
.set-zelle.drin { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.set-tabelle tbody tr:hover td, .set-tabelle tbody tr:hover th { background-color: rgba(148,163,184,.07); }
.set-tabelle tr.ausgenommen th, .set-tabelle tr.ausgenommen td { opacity: .5; }
.set-zelle-aus input[type="checkbox"] { width: 17px; height: 17px; }

@media (max-width: 620px) {
  .set-tabelle { font-size: 12px; }
  .set-tabelle .set-sp-name { min-width: 130px; }
  .set-sp-set { min-width: 78px; }
}

/* ==========================================================================
   v1.31.1 - DISCORD
   --------------------------------------------------------------------------
   Zwei Dinge stecken hier drin, die man auseinanderhalten muss:

   1. DIE BEDIENUNG (dc-ziel, dc-setup, dc-felder ...). Sie folgt der Optik
      der Auto-Nachrichten - dieselben Schalter, dieselben Chips, dieselbe
      Editor-Kante. Wer das eine kann, kann das andere.
   2. DIE VORSCHAU (dc-vorschau, dc-karte ...). Sie bildet DISCORD nach,
      nicht diese App. Deshalb stehen dort feste Farben statt der
      App-Variablen: Die Vorschau soll zeigen, wie es im Discord-Kanal
      aussieht, und nicht mit der Akzentfarbe der App mitwandern.

   Saschas Grundregel gilt auch hier: hell ist, was benennt oder schaltet,
   gedaempft ist, was erklaert. Und alles muss in vier Breiten stehen -
   volle Breite, breit, schmal, Handy - ohne waagerechten Ueberlauf.
   ========================================================================== */

/* --- 1. Der grosse Schalter --- */
.dc-hauptschalter { display: flex; align-items: center; gap: 14px; margin: 10px 0 6px; }
.dc-hauptschalter .an-schalter { width: 48px; height: 26px; }
.dc-hauptschalter .an-schalter::after { width: 22px; height: 22px; }
.dc-hauptschalter .an-schalter.an::after { left: 24px; }
.dc-hauptschalter-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
/* Der Zustand BENENNT - also hell. Der Satz darunter ERKLAERT - also gedaempft. */
.dc-stand { color: var(--text); font-size: 15px; }
.dc-stand.an { color: var(--success); }
.dc-fehlerkasten { border: 1px solid rgba(239,68,68,.5); background: rgba(239,68,68,.08);
  border-radius: 10px; padding: 8px 12px; margin-top: 10px; font-size: 13px; color: var(--text); }
.dc-fehlerkasten ul { margin: 4px 0 0; padding-left: 18px; }

/* --- 2. Kanaele und Einrichtungs-Assistent --- */
.dc-ziele { display: flex; flex-direction: column; gap: 8px; margin: 10px 0; }
.dc-ziel { display: flex; align-items: flex-start; gap: 10px; padding: 8px 12px;
  background: var(--bg-elevated); border: 1px solid var(--border); border-radius: 10px; }
.dc-ziel.dc-aus { opacity: .62; }
.dc-ziel-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 auto; }
.dc-ziel-name { color: var(--text); }
.dc-ziel-url { font-size: 11px; color: var(--text-dim); word-break: break-all; background: none; padding: 0; }
.dc-ziel-fehler { font-size: 12px; color: var(--danger, #ef4444); }

.dc-setup { border: 1px dashed color-mix(in srgb, var(--accent) 50%, var(--border));
  border-radius: 12px; padding: 12px 14px; margin-top: 12px; }
.dc-setup h3 { margin: 0 0 8px; font-size: 15px; color: var(--text); }
.dc-schritte { margin: 0; padding-left: 22px; display: flex; flex-direction: column; gap: 10px; }
.dc-schritte li { color: var(--text); font-size: 14px; }
.dc-schritte .hint { display: block; margin-top: 2px; }
.dc-setup-eingabe { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 6px; }
.dc-setup-eingabe input { flex: 1 1 260px; min-width: 0; background: var(--bg); color: var(--text);
  border: 1px solid var(--border); border-radius: 8px; padding: 8px 10px; font: inherit; font-size: 13px; }
.dc-pruefung { margin-top: 12px; border-radius: 10px; padding: 10px 14px; font-size: 13px; }
.dc-pruefung.ok { border: 1px solid color-mix(in srgb, var(--success) 55%, var(--border));
  background: color-mix(in srgb, var(--success) 10%, transparent); }
.dc-pruefung.fehler { border: 1px solid rgba(239,68,68,.55); background: rgba(239,68,68,.08); }
.dc-pruefung ul { margin: 6px 0; padding-left: 18px; }
.dc-pruefung code { word-break: break-all; }

/* --- 3. Nachrichtenliste --- */
.dc-block { margin-top: 18px; }
.dc-block-kopf { margin: 0 0 6px; font-size: 15px; color: var(--text); display: flex;
  flex-wrap: wrap; align-items: baseline; gap: 10px; }
.dc-gruppe { margin: 8px 0; }
/* Verkettete Nachrichten gehen als EIN Beitrag raus - die Klammer links
   zeigt das, ohne dass man es lesen muss. */
.dc-gruppe.dc-verkettet { border-left: 3px solid color-mix(in srgb, var(--accent) 60%, var(--border));
  border-radius: 0 10px 10px 0; padding-left: 10px; background: color-mix(in srgb, var(--accent) 5%, transparent); }
.dc-gruppe-marke { font-size: 11px; color: var(--accent); padding: 4px 0 0; }
.dc-art-karte { border-color: color-mix(in srgb, var(--accent) 55%, var(--border)); color: var(--accent); }
.dc-ausloeser-hinweis { margin: 2px 0 8px; }

.dc-karte-felder { margin-top: 6px; }
.dc-felder { display: flex; flex-direction: column; gap: 6px; margin-top: 10px; }
.dc-feld-zeile { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.dc-feld-zeile input[type=text] { flex: 1 1 160px; min-width: 0; background: var(--bg); color: var(--text);
  border: 1px solid var(--border); border-radius: 8px; padding: 6px 9px; font: inherit; font-size: 13px; }

/* v1.31.5 - Die Pfeile zum Umsortieren (Saschas Wunsch: "Ich wuerde z.b. auch
   gern die Reihenfolge der Felder jederzeit anpassen wollen").
   Sie stehen SENKRECHT uebereinander und schmal - so nehmen sie der Zeile
   kaum Platz weg, und die Richtung ist ohne Nachdenken klar. */
.dc-feld-sortieren { display: flex; flex-direction: column; gap: 1px; flex: none; }
.dc-feld-sortieren .icon-btn {
  padding: 0 4px; line-height: 1.1; font-size: 10px; min-width: 20px;
}
/* Ein Pfeil, der nicht weiter kann, verschwindet nicht - sonst huepft die
   ganze Zeile, sobald ein Feld nach oben wandert. Er wird nur blass. */
.dc-feld-sortieren .icon-btn[disabled] { opacity: .25; cursor: default; }

/* Die Nummer erklaert, WORAN man sortiert - ohne sie sind zwei Pfeile an
   einer Zeile ohne Bezug. Gedaempft, weil sie erklaert und nicht benennt. */
.dc-feld-nr {
  flex: none; min-width: 1.3em; text-align: right;
  color: var(--text-dim); font-size: 12px; font-variant-numeric: tabular-nums;
}

.dc-bildhinweis { margin: 2px 0 8px; }
/* Ein Baustein ohne Wert bleibt sichtbar, aber gedaempft - er waere beim
   Senden schlicht leer, und das soll man vorher sehen. */
.dc-chip-leer { opacity: .55; border-style: dashed; }
.dc-chip-bild { background: color-mix(in srgb, #38bdf8 15%, var(--bg)); border-color: rgba(56,189,248,.5); }

/* --- v1.31.17: Die Vorschau sagt, warum ein Bild fehlt ----------------
   Saschas Wunsch: "Es waere super, wenn die Vorschau schon quasi echt
   aussieht..." - Dazu gehoert die ehrliche Auskunft, warum ein Fleck leer
   ist. Gedaempft, gestrichelt, nicht im Weg: Es ist eine Erklaerung, keine
   Warnung. Im Kanal steht davon nichts. */
.dc-avatar.leer { border: 1px dashed #4e5058; background: #404249; }

/* --- v1.31.18: Beispielwerte - gefaerbt, und EINE Zeile darunter ---------
   Saschas Entscheid: "am besten beides. Also einen Beispielwert anzeigen
   aber faerben und unter dem Feld oder so zeigen das es nur ein Beispiel
   ist weil noch keine Daten da sind (nur knapper und uebersichtlicher)"

   Die Farbe muss zwei Dinge gleichzeitig koennen: auffallen genug, dass man
   Echtes von Beispiel unterscheidet - und wenig genug, dass die Vorschau
   noch aussieht wie ein Discord-Beitrag. Deshalb nicht die Warnfarbe,
   sondern ein warmer Ton mit ganz leichtem Untergrund. Er benennt nicht und
   schaltet nicht, er kennzeichnet. */
.dc-bsp { color: #e8a33d; background: rgba(232,163,61,.10);
  border-radius: 3px; padding: 0 2px; }
.dc-karte-titel .dc-bsp { color: #e8a33d; }

/* Die eine Zeile. Sie gehoert zur ganzen Vorschau, nicht zu einem Feld -
   deshalb steht sie unter dem Beitrag und nicht in der Karte. */
.dc-bspzeile { margin-top: 6px; font-size: 11.5px; color: #e8a33d;
  display: flex; flex-wrap: wrap; gap: 6px; align-items: baseline; }
.dc-bspzeile::before { content: '●'; font-size: 9px; line-height: 1.6; }
.dc-bspnamen { color: #949ba4; overflow-wrap: anywhere; min-width: 0; }

/* Ein Bildfeld, das (noch) nichts liefert: der Fleck bleibt in Groesse und
   Form stehen, damit die Karte spaeter nicht springt - nur eben gefaerbt. */
.dc-bildtot.bsp { color: #e8a33d; border-color: rgba(232,163,61,.45);
  background: rgba(232,163,61,.07); }

/* --- v1.31.18: Die fertigen Sätze im geführten Weg --------------------
   Saschas Befund: "Wenn ich schon die geführten nehme, erwarte ich auch ein
   Standardset an vorausgefuellten (aenderbaren) Feldern..." - Die Saetze
   stehen direkt unter den Ausloeser-Kacheln, in derselben Optik. Sie sind
   eine Wahl, also hell; die Hinweise darunter erklaeren, also gedaempft. */
.dc-saetze { margin-top: 12px; }
.dc-satz-eigen, .dc-satz-hinweis { margin-top: 8px; }
.dc-satz-hinweis { color: var(--text-dim); }

/* "Form und Absender aendern" - zugeklappt, weil der Satz das schon
   entschieden hat. Wer es anders will, klappt auf. */
.dc-mehr { margin-top: 14px; border-top: 1px solid var(--border); padding-top: 10px; }
.dc-mehr > summary { cursor: pointer; color: var(--text-dim); font-size: 13px;
  list-style: none; display: inline-flex; align-items: center; gap: 6px; }
.dc-mehr > summary::before { content: '▸'; }
.dc-mehr[open] > summary::before { content: '▾'; }
.dc-mehr > summary:hover { color: var(--text); }

/* v1.31.18: Ein Spoiler ist in Discord ein schwarzer Balken, der sich beim
   Anklicken aufdeckt. In der Vorschau reicht der Balken - hier wird nichts
   angeklickt, hier wird geschaut, wie es im Kanal ankommt. */
.dc-spoiler { background: #202225; color: transparent; border-radius: 3px;
  padding: 0 2px; cursor: pointer; }
.dc-spoiler:hover { color: #dbdee1; }

/* --- v1.31.17: Eine tote Bildadresse statt des kaputten Browser-Symbols ---
   Der Browser malt bei einem Bild, das nicht laedt, sein eigenes Symbol mit
   weissem Rahmen hin - im Kanal bliebe die Stelle dagegen leer. Der
   Platzhalter nimmt genau den Platz des Bildes ein und bleibt still:
   gedaempft, ohne Rahmenfarbe, ohne Warnton. Er erklaert, er schaltet
   nichts. */
.dc-bildtot { display: flex; align-items: center; justify-content: center;
  width: 100%; height: 100%; background: #404249; color: #80848e;
  font-size: 15px; line-height: 1; border: 1px dashed #4e5058;
  box-sizing: border-box; }
.dc-bildtot.rund { border-radius: 50%; }
.dc-bildtot.klein { width: 24px; height: 24px; font-size: 11px; flex: none; }
.dc-bildtot.winzig { width: 18px; height: 18px; font-size: 9px; flex: none; }
.dc-bildtot.eckig { border-radius: 6px; }
.dc-bildtot.breit { height: 120px; border-radius: 6px; }

/* --- v1.31.15: Abbrechen, Kacheln mit Skizze, Leiste mit Zustand -------
   Saschas Befunde: "Der Abbrechenbutton oben bitte in einem sehr dezenten
   ROT unten kann man nur vor und zurueck und oben uebersieht man das
   Abbrechen irgendwie komplett..." und "momentan steht da nur text karte
   und das auch noch in einem groessenveraendernden Chip und es ist nicht so
   klar was welcher button 'bewirkt'".

   Dezentes Rot heisst hier: der Rahmen traegt die Farbe, nicht die Flaeche.
   Erst beim Zeigen wird es deutlicher. Ein voll rot gefuellter Knopf neben
   "weiter" saehe aus wie die Hauptsache - er ist der Ausweg, nicht das Ziel. */
.dc-abbrechen { border: 1px solid color-mix(in srgb, var(--danger) 45%, var(--border));
  background: color-mix(in srgb, var(--danger) 10%, transparent);
  color: color-mix(in srgb, var(--danger) 78%, var(--text));
  border-radius: 8px; padding: 5px 12px; font: inherit; font-size: 13px;
  cursor: pointer; white-space: nowrap; }
.dc-abbrechen:hover { background: color-mix(in srgb, var(--danger) 20%, transparent);
  border-color: var(--danger); color: #ffd7d7; }

/* Die Wahlkacheln: Symbol + Name + Skizze + Folge im Klartext. */
.dc-kachel { gap: 8px; }
.dc-kachel-kopf { display: flex; align-items: center; gap: 8px; width: 100%; }
.dc-kachel-kopf b { font-size: 14px; }
.dc-kachel-haken { margin-left: auto; color: var(--accent); font-size: 11px; opacity: 0;
  white-space: nowrap; }
.dc-wahl-knopf.gewaehlt .dc-kachel-haken { opacity: 1; }
.dc-kachel-text { font-size: 12px; color: var(--text-dim); }
.dc-wahl-knopf.gewaehlt { box-shadow: 0 0 0 1px var(--accent) inset; }

/* Die Skizze - grob das, was Discord daraus macht. Reine Deko, deshalb
   nimmt sie keine Klicks an; der ganze Knopf ist die Schaltflaeche. */
.dc-sk { border-radius: 8px; background: #313338; padding: 8px; display: flex;
  gap: 6px; align-items: flex-start; pointer-events: none; width: 100%; }
.dc-sk.hoch { flex-direction: column; gap: 8px; }
.dc-sk-reihe { display: flex; gap: 6px; width: 100%; }
.dc-sk-pkt { width: 18px; height: 18px; border-radius: 50%; background: #5865f2; flex: none; }
.dc-sk-zeilen { display: flex; flex-direction: column; gap: 4px; flex: 1 1 auto; min-width: 0; }
.dc-sk-zl { height: 6px; border-radius: 3px; background: #4e5058; display: block; }
.dc-sk-zl.kurz { width: 45%; }
.dc-sk-zl.mittel { width: 72%; }
.dc-sk-zl.hell { background: #6b7280; }
.dc-sk-karte { border-left: 3px solid var(--accent); background: #2b2d31; border-radius: 4px;
  padding: 6px 8px; display: flex; flex-direction: column; gap: 4px; }
.dc-sk-kaesten { display: flex; gap: 4px; }
.dc-sk-kaesten span { height: 12px; border-radius: 3px; background: #404249; flex: 1; }
.dc-sk-trenn { height: 1px; background: #3f4147; width: 100%; }

/* Die Leiste zeigt jetzt auch den ZUSTAND, nicht nur die Position. */
.dc-assi-punkt.z-fertig > button::after { content: "✓"; color: var(--success); font-size: 11px; }
.dc-assi-punkt.z-fehlt > button::after { content: "○"; color: var(--warn); font-size: 11px; }
.dc-assi-punkt.z-fehlt > button { border-color: color-mix(in srgb, var(--warn) 45%, var(--border)); }
.dc-assi-punkt.jetzt.z-fehlt > button { border-color: var(--accent); }
.dc-assi-punkt.jetzt > button::after { color: var(--accent-text); }

/* Der Satz unter dem Schritt - er laeuft beim Tippen mit. */
.dc-assi-passt { margin: 0; font-size: 13px; color: var(--success);
  white-space: normal; overflow-wrap: anywhere; }

/* --- v1.31.14: Die Rueckfrage beim Einfuegen eines Listen-Bausteins ---
   Sie steht direkt unter den Bausteinen, wo der Klick herkam, und ist
   deutlich, ohne zu schreien: Rahmen in der Akzentfarbe, Text gedaempft,
   die Knoepfe hell. Kein waagerechter Ueberlauf - die Listennamen brechen
   um, statt die Karte zu sprengen. */
.dc-bfrage { margin: 10px 0; padding: 12px 14px; border-radius: 10px;
  border: 1px solid var(--accent);
  background: color-mix(in srgb, var(--accent) 10%, var(--bg-elevated));
  display: flex; flex-direction: column; gap: 6px; }
.dc-bfrage p { margin: 0; overflow-wrap: anywhere; }
.dc-bfrage-wahl, .dc-bfrage-fuss { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
.dc-bfrage-fuss { border-top: 1px solid var(--border); padding-top: 8px; }

/* --- v1.31.14: Der gefuehrte Assistent --------------------------------
   Grundregel des Hauses: hell ist, was benennt oder schaltet - gedaempft
   ist, was erklaert. Also: die Frage des Schritts und die Wahlknoepfe hell,
   die Erklaerungen darunter gedaempft, die noch nicht erreichten Schritte
   in der Leiste am schwaechsten.

   Vier Breiten: Die Leiste laeuft am Rechner in einer Zeile und bricht am
   Handy um; die Wahlknoepfe sind am Rechner ein Raster, am Handy
   untereinander. Kein waagerechter Ueberlauf an keiner Stelle. */
.dc-neu-knoepfe { display: flex; flex-wrap: wrap; gap: 8px; }

.dc-assi { border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg-elevated); padding: 14px 16px; margin: 10px 0;
  display: flex; flex-direction: column; gap: 12px; }
.dc-assi-kopf { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.dc-assi-kopf b { font-size: 15px; }

.dc-assi-leiste { list-style: none; margin: 0; padding: 0; display: flex;
  flex-wrap: wrap; gap: 6px; }
.dc-assi-punkt { display: flex; align-items: center; }
.dc-assi-punkt > button, .dc-assi-spaeter { display: inline-flex; align-items: center;
  gap: 6px; padding: 5px 10px; border-radius: 999px; font: inherit; font-size: 12px;
  border: 1px solid var(--border); background: var(--bg-card); color: var(--text-dim);
  cursor: pointer; white-space: nowrap; }
.dc-assi-spaeter { cursor: default; opacity: .5; }
.dc-assi-punkt > button:hover { border-color: var(--accent); color: var(--text); }
.dc-assi-punkt.erledigt > button { color: var(--text); }
.dc-assi-punkt.jetzt > button { background: var(--accent); border-color: var(--accent);
  color: var(--accent-text); font-weight: 600; }

.dc-assi-frage { margin: 2px 0 0; font-size: 17px; }
.dc-assi-unter { margin: 14px 0 6px; font-size: 13px; color: var(--text-dim);
  text-transform: uppercase; letter-spacing: .05em; }
.dc-assi-inhalt { min-width: 0; }
/* GEMESSEN am Handy (390 px): Der Satz lief 199 px aus der Karte heraus.
   .an-warn ist auf kurze Marken ausgelegt und bricht nicht um - ein ganzer
   Satz darin braucht die Erlaubnis dazu ausdruecklich. */
.dc-assi-fehlt { margin: 0; white-space: normal; overflow-wrap: anywhere; }
.dc-assi-fuss { display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  border-top: 1px solid var(--border); padding-top: 12px; }

/* Die grossen Wahlknoepfe - Ausloeser, Kanal, Weg. */
/* min(240px, 100%) statt 240px: GEMESSEN am Handy (390 px) lief die Karte
   sonst 316 px waagerecht ueber - eine feste Mindestbreite ist bei
   auto-fit keine Mindestbreite, sondern eine Untergrenze, die das Raster
   auch dann haelt, wenn gar kein Platz dafuer da ist. */
.dc-wahl { display: grid; gap: 8px;
  grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); }
.dc-wahl-knopf { display: flex; gap: 10px; align-items: flex-start; text-align: left;
  padding: 10px 12px; border-radius: 10px; border: 1px solid var(--border);
  background: var(--bg-card); color: var(--text); font: inherit; cursor: pointer;
  min-width: 0; }
.dc-wahl-knopf:hover { border-color: var(--accent); }
.dc-wahl-knopf.gewaehlt { border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, var(--bg-card)); }
.dc-wahl-symbol { font-size: 20px; line-height: 1.2; flex: none; }
.dc-wahl-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.dc-wahl-text b { font-size: 14px; }
.dc-wahl-text span { font-size: 12px; color: var(--text-dim); overflow-wrap: anywhere; }

/* Die Zusammenfassung am Ende. */
.dc-assi-uebersicht dl { margin: 0; display: flex; flex-direction: column; gap: 6px; }
.dc-assi-zeile { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px;
  padding: 6px 0; border-bottom: 1px solid var(--border); }
.dc-assi-zeile:last-child { border-bottom: none; }
.dc-assi-zeile dt { flex: 0 0 120px; font-size: 12px; color: var(--text-dim); }
.dc-assi-zeile dd { margin: 0; flex: 1 1 140px; min-width: 0; overflow-wrap: anywhere; }

/* Der gemerkte Zwischenstand. */
.dc-assi-merker { display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  margin-top: 8px; padding: 8px 12px; border-radius: 10px;
  border: 1px dashed var(--border); background: var(--bg-elevated);
  font-size: 13px; color: var(--text-dim); }

@media (max-width: 560px) {
  .dc-assi { padding: 12px; }
  .dc-assi-zeile dt { flex-basis: 100%; }
  .dc-assi-fuss .btn { flex: 1 1 auto; }
}

/* --- v1.31.13: Platz und Grenzen -------------------------------------
   Saschas Befund: "Es sollte auch Klarer werden, welche Beschraenkung z.b.
   eine Karte hat usw..."

   Grundregel des Hauses: hell ist, was benennt oder schaltet - gedaempft
   ist, was erklaert. Der Balken und die Zahl sind die Aussage, der Satz
   darunter die Erklaerung. Eine Warnung darf heller sein, sie benennt ja
   etwas, das gleich passiert.
   Kein waagerechter Ueberlauf: der Balken waechst mit, der Text bricht um. */
.dc-grenzen { margin-top: 12px; padding: 10px 12px; border: 1px solid var(--border);
  border-radius: 10px; background: var(--bg-elevated); display: flex;
  flex-direction: column; gap: 8px; }
.dc-grenzen-kopf { font-size: 12px; color: var(--text-dim); display: flex;
  flex-wrap: wrap; gap: 8px; align-items: baseline; }
.dc-grenze-mass { display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  font-size: 13px; color: var(--text); font-variant-numeric: tabular-nums; }
.dc-grenze-balken { flex: 1 1 160px; min-width: 120px; max-width: 100%; height: 8px;
  border-radius: 999px; background: var(--border); overflow: hidden; }
.dc-grenze-balken > span { display: block; height: 100%; border-radius: 999px;
  background: var(--accent); transition: width .2s ease; }
.dc-grenze-hinweis { font-size: 12px; color: var(--text-dim); overflow-wrap: anywhere; }
.dc-grenze-warn { font-size: 13px; color: var(--warn); overflow-wrap: anywhere; }
.dc-grenze-warn::before { content: "\26A0 "; }

@media (prefers-reduced-motion: reduce) {
  .dc-grenze-balken > span { transition: none; }
}

@media (max-width: 560px) {
  .dc-grenze-mass { gap: 6px; }
  .dc-grenze-balken { flex-basis: 100%; }
}

/* --- Die Vorschau: DISCORD nachgebaut, bewusst mit festen Farben --- */
.dc-vorschau-rahmen { margin-top: 14px; }
.dc-vorschau-kopf { font-size: 12px; color: var(--text-dim); margin-bottom: 6px;
  display: flex; flex-wrap: wrap; gap: 8px; align-items: baseline; }
.dc-vorschau { display: flex; gap: 12px; padding: 12px 14px; border-radius: 10px;
  background: #313338; color: #dbdee1; font-size: 14px; line-height: 1.4; overflow-wrap: anywhere; }
.dc-vorschau.leer { display: block; color: #949ba4; font-style: italic; }
.dc-avatar { width: 40px; height: 40px; border-radius: 50%; flex: none; overflow: hidden;
  background: #5865f2; display: flex; align-items: center; justify-content: center; font-size: 18px; }
.dc-avatar img { width: 100%; height: 100%; object-fit: cover; }
.dc-inhalt { min-width: 0; flex: 1 1 auto; }
.dc-namenszeile { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; }
.dc-namenszeile b { color: #f2f3f5; font-size: 15px; }
.dc-bot { background: #5865f2; color: #fff; font-size: 10px; border-radius: 4px; padding: 1px 5px; letter-spacing: .04em; }
.dc-zeit { color: #949ba4; font-size: 11px; }
.dc-text { margin-top: 2px; }
.dc-text code, .dc-karte code { background: #1e1f22; border-radius: 4px; padding: 1px 4px; font-size: 13px; color: #dbdee1; }

.dc-karte { margin-top: 8px; background: #2b2d31; border-left: 4px solid #9146ff;
  border-radius: 4px; padding: 10px 14px 12px; max-width: 520px; }
.dc-karte-haupt { display: flex; gap: 12px; align-items: flex-start; }
.dc-karte-text { min-width: 0; flex: 1 1 auto; }
.dc-karte-autor { display: flex; align-items: center; gap: 8px; font-size: 13px; color: #f2f3f5; margin-bottom: 4px; }
.dc-karte-autor img { width: 24px; height: 24px; border-radius: 50%; object-fit: cover; }
.dc-karte-titel { color: #00a8fc; font-size: 15px; font-weight: 600; margin-bottom: 4px; }
.dc-karte-beschreibung { color: #dbdee1; font-size: 13px; }
.dc-karte-felder-vorschau { display: flex; flex-wrap: wrap; gap: 10px 16px; margin-top: 8px; }
.dc-karte-feld { flex: 1 1 100%; min-width: 0; font-size: 13px; }
.dc-karte-feld.inline { flex: 1 1 30%; }
.dc-karte-feld b { display: block; color: #f2f3f5; font-size: 13px; }
.dc-karte-feld span { color: #dbdee1; }
.dc-karte-thumb { flex: none; width: 72px; height: 72px; border-radius: 6px; overflow: hidden; }
.dc-karte-thumb img { width: 100%; height: 100%; object-fit: cover; }
.dc-karte-bild { margin-top: 10px; border-radius: 6px; overflow: hidden; max-width: 100%; }
.dc-karte-bild img { width: 100%; height: auto; display: block; }
.dc-karte-fuss { display: flex; align-items: center; gap: 6px; margin-top: 10px; font-size: 11px; color: #949ba4; flex-wrap: wrap; }
.dc-karte-fuss img { width: 18px; height: 18px; border-radius: 50%; object-fit: cover; }
.dc-punkt { opacity: .7; }

/* --- Was geht, was nicht --- */
.dc-wissen-spalten { display: flex; flex-wrap: wrap; gap: 10px 32px; }
.dc-wissen-spalten > div { flex: 1 1 220px; min-width: 0; }
.dc-wissen-spalten h3 { margin: 0 0 4px; font-size: 14px; color: var(--text); }
.dc-wissen-spalten ul { margin: 0; padding-left: 20px; font-size: 13px; color: var(--text-dim); }

/* Handy: die Vorschau darf nicht schieben, die Karte nicht ueberstehen. */
@media (max-width: 620px) {
  .dc-vorschau { padding: 10px; gap: 8px; font-size: 13px; }
  .dc-avatar { width: 32px; height: 32px; }
  .dc-karte { padding: 8px 10px 10px; }
  .dc-karte-thumb { width: 52px; height: 52px; }
  .dc-karte-feld.inline { flex: 1 1 100%; }
  .dc-schritte { padding-left: 18px; }
  .dc-hauptschalter { gap: 10px; }
}

/* --- v1.31.2: Absender je Nachricht, Listen-Bloecke, Vorlagen -------------- */
.dc-absender { align-items: flex-end; }
.dc-absender-hinweis { flex: 1 1 100%; margin-top: 2px; }
.dc-mitwachsen { margin: 6px 0 10px; align-items: flex-start; padding-bottom: 0; }
.dc-mitwachsen b { color: var(--text); }
.dc-badge-wachsend { border-color: color-mix(in srgb, var(--success) 55%, var(--border)); color: var(--success); }
/* Listen-Chips heben sich von den Einzel-Bausteinen ab: sie fuegen keinen
   Wert ein, sondern einen ganzen Block, der sich wiederholt. */
.dc-chip-liste { background: color-mix(in srgb, var(--success) 14%, var(--bg));
  border-color: color-mix(in srgb, var(--success) 50%, var(--border)); }
.dc-chip-liste b { color: var(--success); }
.dc-listen { margin-top: 2px; }

.dc-vorlagen { border: 1px dashed color-mix(in srgb, var(--accent) 50%, var(--border));
  border-radius: 12px; padding: 14px; margin: 10px 0; }
.dc-vorlagen h3 { margin: 0 0 4px; font-size: 15px; color: var(--text); }
.dc-vorlagen-liste { display: flex; flex-wrap: wrap; gap: 10px; margin: 12px 0; }
.dc-vorlage { flex: 1 1 260px; min-width: 0; display: flex; gap: 12px; align-items: flex-start;
  text-align: left; background: var(--bg-elevated); border: 1px solid var(--border);
  border-radius: 10px; padding: 10px 12px; cursor: pointer; font: inherit; color: var(--text);
  transition: border-color .15s, background .15s; }
.dc-vorlage:hover { border-color: color-mix(in srgb, var(--accent) 60%, var(--border));
  background: color-mix(in srgb, var(--accent) 8%, var(--bg-elevated)); }
.dc-vorlage-symbol { font-size: 20px; line-height: 1.2; flex: none; }
.dc-vorlage-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.dc-vorlage-text b { color: var(--text); font-size: 14px; }

@media (max-width: 620px) {
  .dc-vorlage { flex: 1 1 100%; }
}

/* --- v1.31.3: Ablehn-Verlauf (frueher "Was zuletzt geaendert wurde") ------- */
/* Hell ist, was benennt: der Wunschtext. Gedaempft ist, was erklaert:
   Uhrzeit, Name, Punkte, Grund. */
.tw-verlauf-zeile { padding: 3px 0; border-bottom: 1px solid color-mix(in srgb, var(--border) 45%, transparent); }
.tw-verlauf-zeile:last-child { border-bottom: 0; }
.tw-verlauf-haupt { display: flex; flex-wrap: wrap; gap: 8px; align-items: baseline; min-width: 0; flex: 1 1 auto; }
.tw-verlauf-haupt b { color: var(--text); font-weight: 600; }
.tw-verlauf-punkte { color: var(--text-dim); font-variant-numeric: tabular-nums; }
.tw-verlauf-grund { color: var(--text-dim); font-style: italic; overflow-wrap: anywhere; }
.tw-verlauf-marke { font-size: 10.5px; border: 1px solid var(--border); border-radius: 999px;
  padding: 1px 7px; color: var(--text-dim); white-space: nowrap; }
.tw-verlauf-marke.gut { border-color: color-mix(in srgb, var(--success) 55%, var(--border)); color: var(--success); }
.tw-verlauf-fuss { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }

@media (max-width: 620px) {
  .tw-verlauf-zeile { flex-direction: column; gap: 2px; }
}

/* v1.31.3: TIDAL-Treffer, der hier nicht abspielbar ist. Bewusst GELB und
   nicht rot: Es ist ein Hinweis, kein Fehler - der Titel existiert ja. */
.tw-cand-warn.sanft { border-color: rgba(234,179,8,.45); background: rgba(234,179,8,.08); color: #eab308; }

/* ==========================================================================
   v1.31.4 - IST DAS RAIDZIEL NOCH LIVE?
   v1.31.5 - ZWEI LAGEN STATT DREI, DAFUER EINE UHRZEIT
   --------------------------------------------------------------------------
   Ein Punkt und zwei Woerter, mehr nicht - die Zeile ist schon voll genug.
   Der gruene Punkt PULSIERT (Saschas Wunsch): "live" ist die einzige Lage,
   die sich jede Sekunde aendern kann, und genau darauf soll das Auge im
   Vorbeigehen anspringen. Rot steht still - ein Kanal, der aus ist,
   bleibt aus.

   DAS GELB IST WEG (v1.31.5). Saschas Ansage woertlich: "Gelb brauche ich
   nicht... die aktuelle Anzeige Rot Offline und gruen Online ist
   ausreichend... beim Offline kannst du vielleicht da drunter die Uhrzeit
   schreiben seit wann." Die Handlungsanweisung war bei Gelb und Rot ohnehin
   dieselbe; was wirklich half, war die ZEIT, und die steht jetzt als
   Klartext da statt in einer Farbe verschluesselt.

   ZWEI ZEILEN STATT EINER: Die Ampel wird deshalb zu einem kleinen Block -
   Punkt und "Offline" nebeneinander, die Uhrzeit darunter und zum Punkt
   ausgerichtet. Nach der Projektregel "Hell ist, was benennt oder schaltet,
   gedaempft ist, was erklaert": "Offline" benennt und bleibt farbig, die
   Uhrzeit erklaert und ist gedaempft - ausser sie ist frisch, dann ist sie
   die eigentliche Warnung und darf mitreden.
   ========================================================================== */
.rv-ampel { display: inline-flex; align-items: center; gap: 5px; flex: none;
  font-size: 11px; line-height: 1; white-space: nowrap; cursor: help; }
.rv-ampel-punkt { width: 8px; height: 8px; border-radius: 50%; flex: none; }

/* Mit Uhrzeit wird aus der Zeile ein zweizeiliger Block. Der Punkt bleibt
   dabei oben neben dem Wort stehen und rutscht nicht in die Mitte. */
.rv-ampel:has(.rv-ampel-seit) {
  display: inline-grid;
  grid-template-columns: auto auto;
  align-items: center;
  gap: 2px 5px;
  white-space: normal;
}
.rv-ampel-seit {
  grid-column: 2;
  font-size: 10px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}
/* Frisch offline: koennte gerade geraidet haben - das ist keine Fussnote. */
.rv-ampel-seit.frisch { color: var(--warn); font-weight: 600; }

.rv-ampel-online { color: var(--success); }
.rv-ampel-online .rv-ampel-punkt { background: var(--success); animation: rv-puls 2s ease-in-out infinite; }
.rv-ampel-offline { color: #ef4444; }
.rv-ampel-offline .rv-ampel-punkt { background: #ef4444; }

@keyframes rv-puls {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--success) 70%, transparent); }
  70%      { box-shadow: 0 0 0 5px color-mix(in srgb, var(--success) 0%, transparent); }
}
/* Wer das nicht sehen will oder kann, bekommt denselben Punkt ohne Bewegung. */
@media (prefers-reduced-motion: reduce) {
  .rv-ampel-online .rv-ampel-punkt { animation: none; }
}

/* Nicht mehr live: grau hinterlegt, aber NICHT weg (Saschas Vorgabe -
   "nicht aus den vorschlaegen automatisch rausnehmen aber vielleicht so grau
   hinterlegt"). Anklickbar bleibt alles.

   WICHTIG, und beim ersten Versuch falsch gemacht: Ein filter auf der ZEILE
   laesst sich im Kind NICHT zuruecknehmen - er wirkt auf das fertig
   gezeichnete Ergebnis samt aller Kinder. Die Ampel waere damit ausgerechnet
   das, was man nicht mehr sieht. Deshalb wird hier NICHTS auf der Zeile
   gedaempft, sondern gezielt an den Textteilen; der Punkt behaelt seine
   Farbe. */
.rv-zeile.rv-nicht-live, .djrz-ziel.rv-nicht-live {
  background: color-mix(in srgb, var(--text-dim) 10%, transparent);
}
.rv-zeile.rv-nicht-live .rv-name a,
.rv-zeile.rv-nicht-live .rv-titelzeile,
.rv-zeile.rv-nicht-live .rv-von,
.rv-zeile.rv-nicht-live .rv-kommentar,
.rv-zeile.rv-nicht-live .rv-zahl,
.djrz-ziel.rv-nicht-live .djrz-kanal,
.djrz-ziel.rv-nicht-live .djrz-titel { opacity: .5; }
/* Der Balken der Stimmenanzeige wuerde sonst weiter kraeftig leuchten. */
.rv-zeile.rv-nicht-live .rv-balken { opacity: .25; }

@media (max-width: 620px) {
  .rv-ampel-text { display: none; }   /* am Handy reicht der Punkt */

  /* v1.31.5: Die Uhrzeit bleibt aber - sie ist am Handy sogar wichtiger,
     weil das Wort "Offline" hier wegfaellt und der Punkt allein nicht sagt,
     ob der Kanal vor fuenf Minuten oder heute frueh abgeschaltet hat.
     Ohne den Text ist Platz, also darf sie NEBEN den Punkt statt darunter -
     zwei Zeilen fuer einen Punkt und drei Woerter waeren am Handy
     verschwendet. Das Raster von oben wird dafuer wieder aufgehoben. */
  .rv-ampel:has(.rv-ampel-seit) {
    display: inline-flex;
    gap: 4px;
  }
  .rv-ampel-seit { grid-column: auto; }
}

/* ==========================================================================
   v1.31.5 - DIE ZERTIFIKATSKARTE (Einstellungen -> Server-Einstellungen)
   ==========================================================================
   Anlass: Sascha und zwei weitere kamen am Handy nicht mehr in die App. Das
   Zertifikat stand auf "CN = localhost" ohne subjectAltName - sichtbar war
   das nirgends in der App, nur per openssl auf dem Linux-Rechner.

   GESTALTUNG nach der Projektregel "Hell ist, was benennt oder schaltet.
   Gedaempft ist, was erklaert.": Die Lage-Zeile und die Werte (Ablaufdatum,
   Adressen) sind hell, die Erklaerungen darunter gedaempft.

   Vier Breiten geprueft - die Beschreibungsliste wird am Handy einspaltig,
   damit die Adressliste nicht seitlich hinauslaeuft.
   ========================================================================== */
.zert-inhalt { display: flex; flex-direction: column; gap: 12px; }

/* Die Lage in einem Satz, ganz oben. Der Rahmen links traegt die Farbe -
   nicht der Text selbst, der bliebe sonst auf hellem Grund schlecht lesbar. */
.zert-lage {
  margin: 0;
  padding: 10px 12px;
  border-left: 3px solid var(--border);
  border-radius: 0 var(--radius) var(--radius) 0;
  background: var(--bg-elevated);
  font-weight: 600;
  color: var(--text);
}
.zert-gut     { border-left-color: var(--success); }
.zert-mittel  { border-left-color: var(--warn); }
.zert-schlecht{ border-left-color: var(--danger); }
.zert-aus     { border-left-color: var(--border); font-weight: 400; color: var(--text-dim); }

.zert-daten {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 6px 16px;
  margin: 0;
  align-items: baseline;
}
.zert-daten dt { color: var(--text-dim); font-size: 0.9em; }
.zert-daten dd { margin: 0; color: var(--text); }
/* Datum und Zahlen nebeneinander: gleiche Ziffernbreite, sonst zappelt es. */
.zert-daten dd { font-variant-numeric: tabular-nums; }
.zert-neben { color: var(--text-dim); font-weight: 400; font-size: 0.9em; }

/* Die Adressen sind Werte, keine Fliesstext-Erklaerung - deshalb hell und
   als Chips. Sie umbrechen, statt die Karte breit zu ziehen. */
.zert-adr {
  display: inline-block;
  padding: 1px 7px;
  margin: 2px 0;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg-elevated);
  font-size: 0.88em;
  word-break: break-all;
}

.zert-warn {
  margin: 0;
  padding: 10px 12px;
  border-left: 3px solid var(--warn);
  border-radius: 0 var(--radius) var(--radius) 0;
  background: color-mix(in srgb, var(--warn) 8%, transparent);
  color: var(--text);
}

.zert-knopfreihe {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  padding-top: 4px;
  border-top: 1px solid var(--border);
}
/* Der Hinweis neben dem Knopf darf schmal werden, aber nicht verschwinden. */
.zert-knopfreihe .hint { flex: 1 1 260px; margin: 0; }

@media (max-width: 560px) {
  .zert-daten { grid-template-columns: 1fr; gap: 2px 0; }
  .zert-daten dt { margin-top: 6px; }
  .zert-knopfreihe .hint { flex-basis: 100%; }
}

/* ==========================================================================
   v1.31.6 - DIE BAUSTEIN-AUSWAHL NACH KATEGORIEN
   --------------------------------------------------------------------------
   Saschas Auftrag: "mach es so, dass man ALLE Variablen auch (am besten in
   Kategorien geordnet) zusammen klicken kann." Sein Anlass war {raid.titel} -
   den gab es in der fertigen Raid-Uebersicht, aber wer die Liste selbst
   bauen wollte, fand ihn nirgends. Gemessen: 19 solcher Felder.

   Nach der Projektregel "Hell ist, was benennt oder schaltet. Gedaempft ist,
   was erklaert.": Ein Baustein MIT Wert ist hell - er schaltet etwas. Einer
   ohne Wert bleibt sichtbar, aber gedaempft; er ist kein Fehler, im Kanal ist
   nur gerade nichts davon da. Die Kategorie-Ueberschriften und der Fusstext
   erklaeren und sind deshalb ebenfalls gedaempft.
   ========================================================================== */
.dc-bausteine-kopf {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  margin-bottom: 6px;
}
.dc-bausteine-kopf .hint { margin: 0; flex: 1 1 200px; }

.dc-bsuche {
  flex: 0 1 200px; min-width: 0;
  background: var(--bg); color: var(--text);
  border: 1px solid var(--border); border-radius: 8px;
  padding: 5px 9px; font: inherit; font-size: 13px;
}

/* Jede Kategorie ein eigener Block. Zu ist der Normalfall - offen waeren es
   60 Chips auf einmal, und das ist genau die Wand, die vorher da stand. */
.dc-bgruppe { border: 1px solid var(--border); border-radius: var(--radius); margin: 4px 0; }
.dc-bgruppe > summary {
  cursor: pointer; padding: 6px 10px; font-size: 13px; font-weight: 600;
  display: flex; align-items: center; gap: 8px; list-style: none;
}
.dc-bgruppe > summary::-webkit-details-marker { display: none; }
.dc-bgruppe > summary::before { content: '▸'; color: var(--text-dim); flex: none; }
.dc-bgruppe[open] > summary::before { content: '▾'; }
.dc-bgruppe > .an-chips { padding: 0 10px 8px; }

/* "3/11" - wie viele in dieser Kategorie gerade wirklich etwas liefern.
   Das ist die Angabe, die einem das Durchklicken erspart. */
.dc-bgruppe-zahl {
  margin-left: auto; color: var(--text-dim); font-weight: 400; font-size: 12px;
  font-variant-numeric: tabular-nums;
}

/* Gilt nur innerhalb eines {fuer-jeden}-Blocks. Ohne diese Kennzeichnung
   waere die Gesamtliste eine Falle: Man klickt {person.name} in eine Karte
   und wundert sich, warum nichts kommt. */
.dc-chip-liste-nur { border-style: dashed; }
.dc-chip-marke { color: var(--text-dim); font-size: 0.9em; }

.dc-bausteine-fuss { margin: 6px 0 0; font-size: 12px; }

/* Ein Baustein, den es NICHT gibt (Tippfehler). Er verschwindet beim Senden -
   deshalb MUSS er hier auffallen, sonst sucht man ewig, warum eine Zeile
   leer bleibt. */
.dc-unbekannt {
  border-left: 3px solid var(--danger);
  background: color-mix(in srgb, var(--danger) 8%, transparent);
}

@media (max-width: 620px) {
  .dc-bsuche { flex-basis: 100%; }
}

/* v1.31.6 - "Wann soll sie kommen?" - zwei benannte Moeglichkeiten statt
   eines Hakens mit Fachbegriff. Saschas Wunsch: "die Nachrichten sollten
   natuerlich einstellbar sein ob sie SOFORT kommen und waehrend des streams
   'aktualisiert' werden oder gesammelt 'nach' dem stream". Die Funktion gab
   es schon - sie war nur nicht zu finden. */
.dc-wann-wahl { display: flex; flex-wrap: wrap; gap: 8px; }
.dc-wann-knopf {
  flex: 1 1 260px; min-width: 0;
  display: flex; flex-direction: column; gap: 3px;
  text-align: left; cursor: pointer;
  padding: 9px 11px;
  background: var(--bg); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius);
  font: inherit;
}
.dc-wann-knopf b { font-size: 13px; }
/* Die Folge der Wahl erklaert - also gedaempft (Projektregel). */
.dc-wann-knopf span { color: var(--text-dim); font-size: 12px; line-height: 1.35; }
.dc-wann-knopf:hover { border-color: var(--accent); }
.dc-wann-knopf.gewaehlt {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, var(--bg));
}

/* v1.31.7 - ADRESSEN VON AUSSEN (in der Zertifikatskarte).
   Anlass: Saschas Log zeigte Geraete, die unter einer Adresse anklopften,
   die nicht im Zertifikat stand - der Rechner kann eine DynDNS-Adresse
   nicht von sich aus kennen. Die abgelehnten Adressen stehen jetzt als
   anklickbare Vorschlaege da: Man muss nicht herausfinden, was fehlt. */
.zert-adressen { border: 1px solid var(--border); border-radius: var(--radius); }
.zert-adressen > summary {
  cursor: pointer; padding: 8px 11px; font-weight: 600; font-size: 13px;
  display: flex; align-items: center; gap: 8px; list-style: none; flex-wrap: wrap;
}
.zert-adressen > summary::-webkit-details-marker { display: none; }
.zert-adressen > summary::before { content: '▸'; color: var(--text-dim); flex: none; }
.zert-adressen[open] > summary::before { content: '▾'; }
.zert-adressen-inhalt { display: flex; flex-direction: column; gap: 10px; padding: 0 11px 11px; }
.zert-adressen-inhalt textarea {
  width: 100%; box-sizing: border-box;
  background: var(--bg); color: var(--text);
  border: 1px solid var(--border); border-radius: 8px;
  padding: 7px 9px; font: inherit; font-size: 13px; resize: vertical;
}

/* Die Zahl der abgelehnten benennt ein Problem - also NICHT gedaempft. */
.zert-warnmarke {
  color: var(--warn); font-weight: 600; font-size: 12px;
  font-variant-numeric: tabular-nums;
}

.zert-abgelehnt {
  padding: 9px 11px;
  border-left: 3px solid var(--warn);
  border-radius: 0 var(--radius) var(--radius) 0;
  background: color-mix(in srgb, var(--warn) 8%, transparent);
}
.zert-abgelehnt .hint { margin: 2px 0 7px; }
.zert-chips { display: flex; flex-wrap: wrap; gap: 6px; }
/* Ein Vorschlag SCHALTET etwas - deshalb hell (Projektregel). */
.zert-vorschlag { font-weight: 600; }
.zert-vorschlag[disabled] { opacity: .45; cursor: default; }
.zert-chip-zahl { color: var(--text-dim); font-weight: 400; font-size: .9em; }

/* v1.31.8 - Der Grund ueber einer komplett leeren Baustein-Gruppe.
   Saschas Frage war "Warum sind die Variablen leer?" - er sah elf graue
   Chips und keine Erklaerung. Der Satz erklaert, ist also gedaempft
   (Projektregel), steht aber VOR den Chips, weil man ihn sonst erst beim
   Draufzeigen faende. */
.dc-bgruppe-grund {
  margin: 0 11px 6px;
  padding-left: 9px;
  border-left: 2px solid var(--border);
  font-size: 12px;
}

/* v1.31.10 - "App neu starten" (Einstellungen -> Server-Einstellungen).
   Anlass: Sascha wollte die App aus dem Urlaub neu starten koennen, mit nur
   dem Handy dabei. Der Knopf erscheint nur mit Waechter; ohne ihn steht hier
   die Einrichtung. */
.dienst-anleitung { border: 1px solid var(--border); border-radius: var(--radius); }
.dienst-anleitung > summary {
  cursor: pointer; padding: 8px 11px; font-size: 13px; font-weight: 600;
  display: flex; align-items: center; gap: 8px; list-style: none;
}
.dienst-anleitung > summary::-webkit-details-marker { display: none; }
.dienst-anleitung > summary::before { content: '▸'; color: var(--text-dim); flex: none; }
.dienst-anleitung[open] > summary::before { content: '▾'; }
.dienst-anleitung-inhalt { padding: 0 11px 11px; }

/* Die Befehle duerfen NICHT umbrechen - ein umgebrochener Shell-Befehl, den
   jemand kopiert, geht schief. Also scrollt der Block in sich, statt die
   Seite breit zu ziehen. Am Handy war das gemessen 262 px Ueberlauf. */
.dienst-anleitung-inhalt pre {
  margin: 8px 0; padding: 9px 11px;
  max-width: 100%; box-sizing: border-box;
  overflow-x: auto;
  background: var(--bg); border: 1px solid var(--border); border-radius: 8px;
}
.dienst-anleitung-inhalt pre code {
  white-space: pre; font-size: 12px; line-height: 1.5;
}


/* ---------------------------------------------------------------------------
   v1.31.24 - Die Befehls-Mitschrift in den Chat-Befehl-Einstellungen.

   Sie beantwortet EINE Frage: "warum hat der Bot nicht reagiert?" Deshalb
   trägt die Spalte GANZ RECHTS die Farbe - dort steht, was passiert ist.
   Grün = ausgeführt, orange = an etwas Abstellbarem gescheitert, gedämpft =
   den Befehl gibt es schlicht nicht (das ist kein Fehler, nur ein Vertipper).
   --------------------------------------------------------------------------- */
.bef-mitschrift { margin: 10px 0; }
.bef-mitschrift > summary {
  cursor: pointer; font-weight: 600; padding: 4px 0;
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
}
.bef-mit-zahl {
  font-size: 10.5px; font-weight: 700; padding: 1px 8px; border-radius: 999px;
  background: rgba(245,158,11,.18); color: #fbbf24;
  border: 1px solid rgba(245,158,11,.45);
}
.bef-mit-liste {
  display: flex; flex-direction: column; gap: 1px;
  margin: 6px 0 0 0; max-height: 46vh; overflow-y: auto;
}
.bef-mit-zeile {
  display: grid; grid-template-columns: auto auto minmax(0,1fr) auto;
  gap: 4px 10px; align-items: baseline;
  padding: 4px 6px; border-radius: 5px; font-size: 12px;
  background: rgba(255,255,255,.03);
}
.bef-mit-zeit {
  opacity: .5; font-size: 11px; font-variant-numeric: tabular-nums;
}
.bef-mit-wer { font-weight: 600; }
.bef-mit-text {
  min-width: 0; overflow-wrap: anywhere; opacity: .75;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11.5px;
}
.bef-mit-was { font-size: 11px; text-align: right; white-space: nowrap; }
.bef-mit-zeile.ok    .bef-mit-was { color: #86efac; }
.bef-mit-zeile.warn  .bef-mit-was { color: #fbbf24; }
.bef-mit-zeile.leise .bef-mit-was { color: var(--muted, #9aa0aa); }

@media (max-width: 640px) {
  .bef-mit-zeile { grid-template-columns: auto minmax(0,1fr); }
  .bef-mit-was { grid-column: 1 / -1; text-align: left; }
  .bef-mit-text { grid-column: 1 / -1; }
}


/* ---------------------------------------------------------------------------
   v1.31.26 - Die Warteschlange für Chat-Befehle.

   Der Panikknopf ist das lauteste Element der Karte - er ist das einzige,
   das man mitten in einer Bot-Welle in einer Sekunde finden muss. Alles
   andere ist Auskunft und bleibt ruhig. Hell ist, was benennt oder schaltet;
   gedämpft ist, was erklärt.
   --------------------------------------------------------------------------- */
.bq-karte { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border, rgba(255,255,255,.1)); }
.bq-karte > h3 { margin: 0 0 4px 0; font-size: 1rem; }

.bq-panik {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 10px 12px; margin: 10px 0; border-radius: 8px;
  background: rgba(255,255,255,.04); border: 1px solid var(--border, rgba(255,255,255,.12));
}
.bq-panik.an {
  background: rgba(239,68,68,.12); border-color: rgba(239,68,68,.55);
}
.bq-panik-text { display: flex; flex-direction: column; gap: 2px; flex: 1 1 260px; min-width: 0; }
.bq-panik-text .hint { margin: 0; }

.bq-zahlen { display: flex; flex-wrap: wrap; gap: 8px; margin: 10px 0; }
.bq-kachel {
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  padding: 6px 14px; border-radius: 8px; min-width: 74px;
  background: rgba(255,255,255,.04); border: 1px solid var(--border, rgba(255,255,255,.1));
}
.bq-kachel b { font-size: 1.15rem; font-variant-numeric: tabular-nums; }
.bq-kachel span { font-size: 10.5px; opacity: .65; text-align: center; }

.bq-grenzen { display: flex; flex-wrap: wrap; gap: 10px 16px; margin: 10px 0 6px 0; }
.bq-grenzen label { display: flex; flex-direction: column; gap: 3px; font-size: 12px; }
/* Genauer als die allgemeine Feldregel - sonst stehen hier vier Zahlenfelder
   in voller Breite nebeneinander und die Karte wirkt wie ein Formular. */
.bq-grenzen label input[type="number"] { width: 92px; max-width: 92px; }

.bq-gesperrt { margin: 10px 0; }
.bq-gesperrt > summary { cursor: pointer; font-weight: 600; padding: 3px 0; }
.bq-gesperrt-zeile {
  display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: baseline;
  padding: 4px 6px; font-size: 12px; border-radius: 5px;
  background: rgba(239,68,68,.06);
}
.bq-gesperrt-zeile .hint { margin: 0; font-size: 11px; }

.bq-leiste { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 10px 0 6px 0; }
.bq-leiste .hint { margin: 0; }

.bq-liste { display: flex; flex-direction: column; gap: 1px; max-height: 44vh; overflow-y: auto; }
.bq-zeile {
  display: grid; grid-template-columns: auto auto minmax(0,1fr) auto auto;
  gap: 4px 10px; align-items: baseline;
  padding: 4px 6px; border-radius: 5px; font-size: 12px;
  background: rgba(255,255,255,.03);
}
.bq-zeit { opacity: .5; font-size: 11px; font-variant-numeric: tabular-nums; }
.bq-wer { font-weight: 600; justify-self: start; }
.bq-text {
  min-width: 0; overflow-wrap: anywhere; opacity: .8;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11.5px;
}
.bq-mal { color: #fbbf24; }
.bq-zustand { font-size: 11px; text-align: right; white-space: nowrap; opacity: .75; }
.bq-knoepfe { display: flex; gap: 4px; }
.bq-zeile.z-gesperrt { background: rgba(239,68,68,.08); }
.bq-zeile.z-gesperrt .bq-zustand { color: #fca5a5; opacity: 1; }
.bq-zeile.z-erledigt { opacity: .5; }
.bq-zeile.z-fehler .bq-zustand { color: #fbbf24; opacity: 1; }

@media (max-width: 720px) {
  /* Auf dem Handy untereinander statt gequetscht: Zeit und Name in einer
     Zeile, darunter der Text, der Zustand und zuletzt die Knöpfe - alle
     linksbündig, damit die Spalte eine Kante hat. */
  .bq-zeile { grid-template-columns: auto minmax(0,1fr); }
  .bq-text { grid-column: 1 / -1; }
  .bq-zustand { grid-column: 1 / -1; text-align: left; }
  .bq-knoepfe { grid-column: 1 / -1; justify-content: flex-start; }
  .bq-grenzen label input[type="number"] { width: 100%; max-width: none; }
}


/* ---------------------------------------------------------------------------
   v1.31.29 - Die eigene Zeile in "Wer hat einen eigenen Bot?".

   Man selbst steht oben und ist als "du" gekennzeichnet - sonst sucht man
   in einer Liste von vier Namen erst, welcher der eigene ist.
   --------------------------------------------------------------------------- */
.pbot-zeile.ich { background: rgba(139,92,246,.09); border-radius: 6px; }
.pbot-ich {
  font-size: 10px; font-weight: 700; letter-spacing: .4px;
  padding: 1px 7px; border-radius: 999px;
  background: rgba(139,92,246,.22); color: #c4b5fd;
  border: 1px solid rgba(139,92,246,.45);
}
/* Alles erledigt: die Zahl darf ruhig grün sein - sie meldet keinen Rest. */
.bot-schritte-zahl.fertig {
  background: rgba(34,197,94,.16); color: #86efac;
  border-color: rgba(34,197,94,.45);
}
