/* B420 - Design-System (modern, ruhig, professionell) */

:root {
  --bg: #0a0d12;
  --bg-soft: #0d1119;
  --surface: #131826;
  --surface-2: #171e2f;
  --surface-hover: #1c2437;
  --border: rgba(255, 255, 255, 0.08);
  --border-strong: rgba(255, 255, 255, 0.16);
  --text: #eef1f7;
  --text-dim: #9aa4b8;
  --text-faint: #626c82;
  --accent: #5b8cff;
  --accent-hover: #7aa0ff;
  --accent-soft: rgba(91, 140, 255, 0.14);
  --accent-text: #dbe6ff;
  --teal: #34d1c4;
  --teal-soft: rgba(52, 209, 196, 0.16);
  /* Kraeftiges, deutlich von --teal (Radio-Player-Akzent) unterscheidbares
     Gruen speziell fuer den "Musik ist hoerbar"-Ring am Mute-Button. */
  --ring-green: #2de685;
  --ring-green-soft: rgba(45, 230, 133, 0.18);
  --video-accent: #f5a623;
  --danger: #f2495c;
  --danger-soft: rgba(242, 73, 92, 0.14);
  --success: #34d399;
  --new-badge: #f59e0b;
  --new-badge-text: #201400;
  --radius-sm: 8px;
  --radius: 12px;
  --radius-lg: 18px;
  --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.25);
  --shadow: 0 8px 28px rgba(0, 0, 0, 0.35);
  --font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
}

* { box-sizing: border-box; }
html, body { height: 100%; }

body {
  margin: 0;
  min-height: 100vh;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  position: relative;
}

/* ---------- eigene Scrollbar (passend zum dunklen Design) ---------- */
* { scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
  background: linear-gradient(180deg, rgba(91, 140, 255, 0.35), rgba(52, 209, 196, 0.35));
  border-radius: 999px;
  border: 2px solid var(--bg);
  background-clip: padding-box;
}
*::-webkit-scrollbar-thumb:hover {
  background: linear-gradient(180deg, var(--accent), var(--teal));
  background-clip: padding-box;
}

/* ---------- animierter Hintergrund: Aurora-Blobs + Partikel + Grid ---------- */
.bg-glow {
  position: fixed;
  inset: -10%;
  overflow: hidden;
  filter: blur(70px);
  z-index: 0;
  pointer-events: none;
}

.aurora {
  position: absolute;
  border-radius: 50%;
  opacity: 0.4;
  will-change: transform;
  mix-blend-mode: screen;
}
.aurora.a1 {
  width: 46vw; height: 46vw; max-width: 620px; max-height: 620px;
  top: -8%; left: 2%;
  background: radial-gradient(circle, var(--accent), transparent 70%);
  animation: auroraDrift1 24s ease-in-out infinite;
}
.aurora.a2 {
  width: 38vw; height: 38vw; max-width: 520px; max-height: 520px;
  bottom: -10%; right: 4%;
  background: radial-gradient(circle, var(--teal), transparent 70%);
  animation: auroraDrift2 28s ease-in-out infinite;
}
.aurora.a3 {
  width: 34vw; height: 34vw; max-width: 460px; max-height: 460px;
  top: 32%; left: 58%;
  background: radial-gradient(circle, #8b6bff, transparent 70%);
  animation: auroraDrift3 32s ease-in-out infinite;
}
@keyframes auroraDrift1 {
  0%, 100% { transform: translate(0, 0) scale(1); }
  50% { transform: translate(7vw, 5vh) scale(1.15); }
}
@keyframes auroraDrift2 {
  0%, 100% { transform: translate(0, 0) scale(1); }
  50% { transform: translate(-6vw, -4vh) scale(1.1); }
}
@keyframes auroraDrift3 {
  0%, 100% { transform: translate(0, 0) scale(1); }
  50% { transform: translate(-5vw, 6vh) scale(0.9); }
}

.bg-grid {
  position: fixed;
  inset: -48px 0 0 -48px;
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.025) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.025) 1px, transparent 1px);
  background-size: 48px 48px;
  mask-image: radial-gradient(ellipse 70% 55% at 50% 0%, #000 30%, transparent 85%);
  z-index: 0;
  pointer-events: none;
  animation: gridDrift 50s linear infinite;
}
@keyframes gridDrift {
  from { background-position: 0 0, 0 0; }
  to { background-position: 48px 48px, 48px 48px; }
}

/* schwebende Partikel */
.particles {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}
/* Feuerwerk: Canvas fuer aufsteigende, explodierende Raketen (siehe app.js) */
.fireworks-canvas {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 1;
  transition: opacity 0.3s ease;
}

.particle {
  position: absolute;
  bottom: -12px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(122, 160, 255, 0.9), rgba(91, 140, 255, 0));
  box-shadow: 0 0 6px rgba(91, 140, 255, 0.6);
  opacity: 0;
  animation-name: particleRise;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}
@keyframes particleRise {
  0% { transform: translate(0, 0); opacity: 0; }
  8% { opacity: 0.8; }
  85% { opacity: 0.5; }
  100% { transform: translate(14px, -104vh); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .aurora, .bg-grid, .particle, .login-glow-ring, .login-box,
  .topbar, .topbar::after, .page, .brand-badge::after, .flask-bubble,
  .login-box .btn-primary, .mascot-bob {
    animation: none !important;
  }
  .btn::before { transition: none !important; }
}

a { color: inherit; }

/* ---------- Topbar ---------- */
.topbar {
  /* Fixiert/angepinnt: bleibt beim Scrollen immer oben sichtbar (Datum/
     Uhrzeit, Musik-Player, angemeldeter Benutzer) - auf JEDER Seite mit
     dieser Kopfzeile (Startseite, Event, Einstellungen, Adminbereich,
     Anleitung). Die dadurch entstehende Luecke im normalen Seitenfluss
     wird per JS (wireStickyTopbar() in assets/app.js) als padding-top auf
     body ausgeglichen, da die tatsaechliche Hoehe variiert (Umbruch bei
     schmalen Fenstern, Musik-Player nur wenn vorhanden usw.). Ausnahme:
     der Handy-"App"-Modus der Startseite (body.has-app, siehe Media Query
     weiter unten) hat davon unabhaengig schon immer eine eigene, bewusst
     transparente/schwebende Fixierung. */
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  row-gap: 8px;
  gap: 16px;
  padding: 14px 28px;
  border-bottom: 1px solid var(--border);
  background: rgba(13, 17, 25, 0.75);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  animation: topbarEnter 0.6s cubic-bezier(.16, 1, .3, 1) both;
}
@keyframes topbarEnter {
  from { opacity: 0; transform: translateY(-10px); }
  to { opacity: 1; transform: translateY(0); }
}
/* dezente Verlaufslinie am unteren Rand der Topbar */
.topbar::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -1px;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--accent) 20%, var(--teal) 50%, var(--accent) 80%, transparent);
  opacity: 0.55;
  animation: topbarLineFlow 8s ease-in-out infinite;
  background-size: 200% 100%;
}
@keyframes topbarLineFlow {
  0%, 100% { background-position: 0% 0; }
  50% { background-position: 100% 0; }
}

.topbar-left {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  row-gap: 8px;
  gap: 12px;
}

.brand-mark {
  display: flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  color: var(--text);
}

.brand-badge {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 10px;
  background: linear-gradient(155deg, var(--accent), var(--teal));
  color: #06090f;
  flex-shrink: 0;
  position: relative;
  overflow: hidden;
  box-shadow: 0 2px 10px rgba(91, 140, 255, 0.25);
}
/* dezenter, dauerhafter Glanzschimmer ueber dem Marken-Icon */
.brand-badge::after {
  content: "";
  position: absolute;
  inset: -60% -60%;
  background: linear-gradient(115deg, transparent 40%, rgba(255, 255, 255, 0.35) 50%, transparent 60%);
  animation: badgeSheen 5s ease-in-out infinite;
}
@keyframes badgeSheen {
  0%, 30% { transform: translateX(-60%); }
  70%, 100% { transform: translateX(60%); }
}

.flask-icon { width: 60%; height: 60%; position: relative; z-index: 1; }
.flask-bubble { animation: bubbleRise 2.6s ease-in-out infinite; transform-origin: center; }
.flask-bubble.fb2 { animation-delay: 0.5s; }
.flask-bubble.fb3 { animation-delay: 1.1s; }
@keyframes bubbleRise {
  0% { transform: translateY(0); opacity: 0; }
  25% { opacity: 0.8; }
  100% { transform: translateY(-3px); opacity: 0; }
}

.brand-word {
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: -0.01em;
}

/* ---------- Maskottchen in der Kopfzeile (optional, per Einstellung) ----------
   Bleibt bei jeder Fensterbreite sichtbar (kein display:none mehr in der
   Media Query); auf schmalen Fenstern rutscht die Gruppe per flex-wrap in
   eine zweite Zeile statt zu verschwinden. Groesse an den Kolben-Badge
   (34x34px) angeglichen, damit die Figur ihn nie ueberragt. */
.mascot-topbar-link,
.tekken-topbar-link, 
.slotracer-topbar-figure{
  display: inline-flex;
  align-items: center;
  margin-left: 16px;
  flex-shrink: 0;
  text-decoration: none;
}
.mascot-topbar-figure,
.tekken-topbar-figure,
.slotracer-topbar-figure {
  width: auto;
  height: 30px;
  max-width: 30px;
  display: block;
  image-rendering: pixelated;
  image-rendering: -moz-crisp-edges;
  filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.4));
  transition: transform 0.25s cubic-bezier(.34, 1.56, .64, 1), filter 0.25s ease;
  cursor: pointer;
}
.mascot-topbar-link:hover .mascot-topbar-figure,
.tekken-topbar-link:hover .tekken-topbar-figure, 
.slotracer-topbar-link:hover .slotracer-topbar-figure{
  transform: translateY(-2px) scale(1.12);
  filter: drop-shadow(0 6px 14px rgba(91, 140, 255, 0.5));
}
.mascot-topbar-link:active .mascot-topbar-figure,
.tekken-topbar-link:active .tekken-topbar-figure,
.slotracer-topbar-link:active .slotracer-topbar-figure {
  transform: translateY(0) scale(0.96);
}

.topbar-right {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  row-gap: 10px;
}

/* ---------- Kopfzeile: klar abgegrenzte Gruppen ----------
   Wunsch: "die aufteilung in der headline, passt nicht und ist
   unübersichtlich [...] professionell aufteilen." Vorher standen
   Leistungsmodus, Uhrzeit, Radio-Player, Stumm-Knopf, MP3-Player,
   Admin-Schalter und Benutzermenue als sieben gleichrangige, direkt
   aneinandergereihte Elemente in einer Reihe - kein erkennbarer
   Zusammenhang, welche Bedienelemente zusammengehoeren. Jetzt bilden
   inhaltlich zusammengehoerige Elemente eine ".topbar-group" (enger
   Abstand dazwischen) und zwischen den Gruppen selbst steht ein duenner,
   klar sichtbarer ".topbar-divider" (Leistungsmodus+Uhrzeit | Radio-/MP3-
   Player+Stumm-Knopf | Admin-Schalter+Benutzermenue). Auf schmalen
   Fenstern darf die Kopfzeile jetzt ausserdem umbrechen (flex-wrap oben),
   damit bei vielen aktiven Elementen (z. B. alle Musik-Funktionen
   gleichzeitig aktiv) nichts abgeschnitten wird oder übereinander
   liegt. */
.topbar-group {
  display: flex;
  align-items: center;
  gap: 10px;
}
.topbar-divider {
  width: 1px;
  height: 22px;
  background: var(--border-strong);
  flex-shrink: 0;
}

.topbar-clock {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--text-dim);
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.topbar-clock strong { color: var(--text); font-weight: 600; }
.topbar-clock .dot { opacity: 0.4; }

/* Nur fuer Screenreader, visuell unsichtbar (siehe .admin-mode-toggle
   unten - der Schieberegler soll KEINEN sichtbaren Text haben, siehe
   Wunsch: "bei schieberegler soll kein text enthalten/stehen"). */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- Adminmodus-Schieberegler (Kopfzeile) ----------
   Wunsch: "die bearbeiter und der admin sollen in der headline rechts
   neben dem radioplayer einen schieberegler haben mit adminmodus an aus,
   sobald man auf aus stellt, sieht die oberflaeche von dienstplan, events,
   galerie so aus wie bei einem betrachter [...] bei schieberegler soll
   kein text enthalten/stehen, nur wenn man mit der mouse drüber fährt
   soll kommen 'Adminmodus on/off'" - reiner iOS-Stil-Schalter ohne
   sichtbaren Text, Tooltip kommt vom title-Attribut auf dem <label>
   selbst (siehe render_admin_mode_toggle() in includes/functions.php).
   Bedienung/Neuladen siehe assets/app.js (wireAdminModeToggle). */
.admin-mode-toggle {
  position: relative;
  display: inline-flex;
  align-items: center;
  width: 40px;
  height: 22px;
  flex-shrink: 0;
  cursor: pointer;
}
.admin-mode-toggle input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}
.admin-mode-slider {
  position: absolute;
  inset: 0;
  background: var(--border-strong);
  border-radius: 999px;
  transition: background 0.2s ease;
}
.admin-mode-slider::before {
  content: "";
  position: absolute;
  width: 16px;
  height: 16px;
  left: 3px;
  top: 3px;
  background: #fff;
  border-radius: 50%;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
  transition: transform 0.2s ease;
}
.admin-mode-toggle input:checked + .admin-mode-slider {
  background: var(--accent);
}
.admin-mode-toggle input:checked + .admin-mode-slider::before {
  transform: translateX(18px);
}
.admin-mode-toggle input:focus-visible + .admin-mode-slider {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.admin-mode-toggle input:disabled + .admin-mode-slider {
  opacity: 0.6;
  cursor: default;
}

/* ---------- Admin/Bearbeiter-Modus-Schieberegler (Kopfzeile, NUR Admin) ----------
   Wunsch: "Nur beim Admin einpflegen, nicht beim Bearbeiter: oben neben dem
   Schieberegler für Admin/Betrachter-Modus einen weiteren Schalter [...]
   der Admin soll somit umschalten können, um zu sehen wie die Oberfläche
   beim Bearbeiter aussieht [...], so dass er nicht immer ummelden muss. [...]
   der schieberegler von Admin zu Bearbeiter soll rot sein." Technisch
   identischer Schalter wie .admin-mode-toggle oben (siehe
   render_admin_bearbeiter_mode_toggle() in includes/functions.php) - in
   --danger (rot) eingefaerbt statt in --accent, damit auf den ersten Blick
   klar ist, WELCHER der beiden Schieberegler gerade aktiv ist (blau =
   Betrachter-Vorschau, rot = Bearbeiter-Vorschau), und mit etwas Abstand zum
   ersten Schalter, da beide nun nebeneinander in derselben Kopfzeilen-Gruppe
   stehen. Bedienung siehe assets/app.js (wireAdminBearbeiterModeToggle) und
   toggle-admin-bearbeiter-mode.php. */
.admin-bearbeiter-mode-toggle {
  margin-left: 8px;
}
.admin-bearbeiter-mode-toggle input:checked + .admin-mode-slider {
  background: var(--danger);
}

/* ---------- User-Menu (Dropdown) ---------- */
.user-menu { position: relative; }

.user-menu-btn {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 6px 10px 6px 6px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
  font-family: var(--font);
  font-size: 0.85rem;
}
.user-menu-btn:hover { border-color: var(--border-strong); background: var(--surface-hover); }

.avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent-text);
  font-weight: 600;
  font-size: 0.78rem;
  flex-shrink: 0;
}

.user-menu-btn .chev {
  width: 14px;
  height: 14px;
  color: var(--text-faint);
  transition: transform 0.15s ease;
}
.user-menu.open .chev { transform: rotate(180deg); }

.user-menu-panel {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  min-width: 220px;
  background: #ffffff;
  border: 1px solid #e2e4ea;
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 8px;
  display: none;
  z-index: 30;
}
.user-menu.open .user-menu-panel { display: block; }

.user-menu-info {
  padding: 8px 10px 10px;
  border-bottom: 1px solid #e2e4ea;
  margin-bottom: 6px;
}
.user-menu-info .name { font-weight: 600; font-size: 0.9rem; color: #14161c; }
.user-menu-info .role { font-size: 0.78rem; color: #5b6272; margin-top: 2px; }

.user-menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 10px;
  border-radius: var(--radius-sm);
  font-size: 0.86rem;
  text-decoration: none;
  color: #14161c;
  cursor: pointer;
  border: none;
  background: none;
  width: 100%;
  text-align: left;
  font-family: var(--font);
}
.user-menu-item:hover { background: #f1f2f6; }
.user-menu-item svg { width: 16px; height: 16px; color: #5b6272; flex-shrink: 0; }
.user-menu-item.danger { color: #c22a3e; }
.user-menu-item.danger svg { color: #c22a3e; }
/* Kleiner Zaehl-Punkt im Konto-Menue, z. B. "Events" -> Anzahl der gerade
   fuer einen freigegebenen Events (siehe index.php $eventsSharedCount). */
.user-menu-item-badge {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--accent, #5b8cff);
  color: #fff;
  font-size: 0.72rem;
  font-weight: 700;
  line-height: 1;
  flex-shrink: 0;
}

/* ---------- Layout ---------- */
.page {
  position: relative;
  z-index: 1;
  max-width: 1160px;
  margin: 0 auto;
  padding: 40px 24px 72px;
  display: flex;
  flex-direction: column;
  gap: 28px;
  animation: pageEnter 0.55s cubic-bezier(.16, 1, .3, 1) both;
  animation-delay: 0.08s;
}
@keyframes pageEnter {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: translateY(0); }
}

.page-narrow { max-width: 760px; }

/* "Bild anzeigen von" - fuer Administratoren (alle Benutzer) und
   Bearbeiter (nur ihre zugeordneten Betrachter) sichtbare Auswahl, mit der
   man sich gezielt das Bild eines anderen Benutzers anzeigen lassen kann
   (siehe index.php). Bewusst kompakt/schmal, damit sie sich klar von der
   eigentlichen Anzeige absetzt. */
.view-user-picker {
  max-width: 280px;
}
/* "Anpinnen"/"Anpinnen aufheben" (nur Bearbeiter, siehe canPinViewer in
   index.php sowie set-pinned-viewer.php) - direkt unter der Auswahl. */
.view-user-pin-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 8px;
}
.view-user-pin-row .page-subtitle {
  margin: 0;
  font-size: 0.76rem;
}

.page-header {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}

.page-title {
  margin: 0;
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.page-subtitle {
  margin: 4px 0 0;
  color: var(--text-dim);
  font-size: 0.9rem;
}

.back-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--text-dim);
  text-decoration: none;
  font-size: 0.85rem;
}
.back-link:hover { color: var(--text); }
.back-link svg { width: 15px; height: 15px; }

/* ---------- Schichtcounter ---------- */
.shift-panel {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  padding: 18px 24px;
  border-radius: var(--radius-lg);
  background: var(--surface);
  border: 1px solid var(--border);
  transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}
.shift-panel:hover {
  transform: translateY(-3px);
  border-color: rgba(91, 140, 255, 0.3);
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.4), 0 0 0 1px var(--accent-soft);
}

.shift-label {
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.shift-label .eyebrow {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-faint);
  font-weight: 600;
}
.shift-label .sub { font-size: 0.8rem; color: var(--text-dim); }
.shift-label .credit {
  margin-top: 3px;
  font-size: 0.68rem;
  font-style: italic;
  letter-spacing: 0.02em;
  color: var(--text-faint);
  opacity: 0.8;
}
.shift-label .credit-name {
  font-style: normal;
  font-weight: 600;
  letter-spacing: 0.01em;
  background: linear-gradient(135deg, var(--accent), var(--teal));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* Button zwischen Schichtcounter-Block (links) und Flip-Uhr (rechts),
   oeffnet die eigene Event-Seite mit den selbst angelegten Event-Buttons
   und deren Bildergalerien (siehe events.php). */
.event-launch-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 10px 26px;
  border-radius: var(--radius);
  background: linear-gradient(135deg, var(--accent), var(--teal));
  color: #0b1020;
  font-family: var(--font);
  font-size: 30px;
  font-weight: 800;
  letter-spacing: 0.03em;
  text-decoration: none;
  white-space: nowrap;
  position: relative;
  /* 3D-Optik: ein heller Bevel-Rand oben (inset), ein dunkler "Sockel"
     darunter (die massive, nicht verwischte 0-Blur-Schicht simuliert eine
     Kante/Dicke wie bei einem echten gedrueckten Knopf) plus ein weicher,
     pulsierender Leucht-Schein aussenrum (siehe eventBtnGlow unten). */
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.55),
    inset 0 -3px 5px rgba(8, 14, 30, 0.35),
    0 6px 0 #1f3a82,
    0 10px 20px rgba(8, 14, 30, 0.45),
    0 0 22px rgba(91, 140, 255, 0.55),
    0 0 44px rgba(52, 209, 196, 0.28);
  transition: transform 0.15s ease, box-shadow 0.15s ease, filter 0.2s ease;
  animation: eventBtnGlowPulse 2.6s ease-in-out infinite;
}
.event-launch-btn:hover {
  transform: translateY(-3px);
  filter: brightness(1.06);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.6),
    inset 0 -3px 5px rgba(8, 14, 30, 0.3),
    0 9px 0 #1f3a82,
    0 18px 30px rgba(8, 14, 30, 0.5),
    0 0 32px rgba(91, 140, 255, 0.75),
    0 0 60px rgba(52, 209, 196, 0.4);
}
/* Gedrueckt: der Knopf "sinkt" fast bis auf den Sockel ab (translateY nahe
   der Sockelhoehe, Sockel-Schatten fast verschwunden) - der klassische
   3D-Tastendruck-Effekt. */
.event-launch-btn:active {
  transform: translateY(5px);
  filter: brightness(0.97);
  box-shadow:
    inset 0 2px 5px rgba(8, 14, 30, 0.45),
    0 1px 0 #1f3a82,
    0 4px 10px rgba(8, 14, 30, 0.4),
    0 0 16px rgba(91, 140, 255, 0.45);
}
@keyframes eventBtnGlowPulse {
  0%, 100% {
    box-shadow:
      inset 0 1px 0 rgba(255, 255, 255, 0.55),
      inset 0 -3px 5px rgba(8, 14, 30, 0.35),
      0 6px 0 #1f3a82,
      0 10px 20px rgba(8, 14, 30, 0.45),
      0 0 22px rgba(91, 140, 255, 0.55),
      0 0 44px rgba(52, 209, 196, 0.28);
  }
  50% {
    box-shadow:
      inset 0 1px 0 rgba(255, 255, 255, 0.55),
      inset 0 -3px 5px rgba(8, 14, 30, 0.35),
      0 6px 0 #1f3a82,
      0 10px 20px rgba(8, 14, 30, 0.45),
      0 0 34px rgba(91, 140, 255, 0.8),
      0 0 64px rgba(52, 209, 196, 0.45);
  }
}
@media (prefers-reduced-motion: reduce) {
  .event-launch-btn { animation: none; }
}

.flip-clock { display: flex; align-items: center; gap: 9px; }
.flip-group { display: flex; gap: 6px; }

.flip-colon {
  font-size: 2.1rem;
  font-weight: 700;
  color: var(--text-faint);
  padding-bottom: 4px;
  align-self: center;
}

.flip-digit {
  position: relative;
  width: 52px;
  height: 70px;
  perspective: 380px;
}

.flip-digit .digit-half,
.flip-digit .leaf {
  position: absolute;
  left: 0;
  width: 100%;
  height: 50%;
  overflow: hidden;
}

.flip-digit .digit-half {
  background: linear-gradient(180deg, #0c0c0e, #000000);
  border: 1px solid rgba(255, 255, 255, 0.06);
}
.flip-digit .top { top: 0; border-radius: 10px 10px 0 0; border-bottom: 1px solid rgba(0, 0, 0, 0.8); }
.flip-digit .bottom { bottom: 0; border-radius: 0 0 10px 10px; }

.flip-digit .digit-num {
  position: absolute;
  left: 0;
  width: 100%;
  height: 200%;
  line-height: 2;
  text-align: center;
  font-size: 2.3rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--accent-text);
  text-shadow: 0 0 16px rgba(91, 140, 255, 0.4);
}
.flip-digit .top .digit-num { top: 0; }
.flip-digit .bottom .digit-num { top: -100%; }

.flip-digit::after {
  content: "";
  position: absolute;
  left: 4px; right: 4px; top: calc(50% - 0.5px);
  height: 1px;
  background: rgba(0, 0, 0, 0.7);
  z-index: 3;
  pointer-events: none;
}

.flip-digit .leaf { z-index: 2; backface-visibility: hidden; transform-style: preserve-3d; }
.flip-digit .leaf-front {
  top: 0;
  transform-origin: bottom center;
  border-radius: 10px 10px 0 0;
  background: linear-gradient(180deg, #141416, #000000);
  border-bottom: 1px solid rgba(0, 0, 0, 0.8);
  transform: rotateX(-90deg);
}
.flip-digit .leaf-front .digit-num { top: 0; }
.flip-digit .leaf-back {
  bottom: 0;
  transform-origin: top center;
  border-radius: 0 0 10px 10px;
  background: linear-gradient(180deg, #000000, #141416);
  transform: rotateX(90deg);
}
.flip-digit .leaf-back .digit-num { top: -100%; }

.flip-digit.flipping .leaf-front { animation: flipTopLeaf 0.09s cubic-bezier(.4,0,.6,1) both; }
.flip-digit.flipping .leaf-back { animation: flipBottomLeaf 0.09s cubic-bezier(.4,0,.6,1) both; animation-delay: 0.09s; }
@keyframes flipTopLeaf { from { transform: rotateX(0deg); } to { transform: rotateX(-90deg); } }
@keyframes flipBottomLeaf { from { transform: rotateX(90deg); } to { transform: rotateX(0deg); } }

/* ---------- Bild-Bühne ---------- */
.image-stage-wrap {
  position: relative;
  width: 100%;
}
/* Tanzendes Strichmaennchen: sitzt ausserhalb von .image-stage (die selbst
   overflow:hidden hat und damit alles ausserhalb ihrer eigenen Kante
   abschneiden wuerde), rechts daneben. Nur auf ausreichend breiten
   Bildschirmen sichtbar (siehe Medienabfrage weiter unten) - auf
   schmaleren Fenstern/Tablets/Handys ist rechts neben der Buehne kein
   Platz dafuer, dort bleibt es unsichtbar.
   Wunsch: "die noten sollen von ganz oben bis ganz unten fallen. also
   ueber die komplette hintergrund hoehe" - dafuer bewusst
   "position:fixed" statt (wie zuvor) "position:absolute" innerhalb von
   .image-stage-wrap: nur so spannt sich die "Buehne" ueber die komplette
   Bildschirmhoehe (100vh) auf, unabhaengig davon, wie weit
   .image-stage-wrap selbst vom oberen Seitenrand entfernt ist. Die dadurch
   verlorene bequeme horizontale Positionierung ueber "left:100%" (rechter
   Rand des relativ positionierten .image-stage-wrap) uebernimmt jetzt
   app.js (positionDanceFigureWrap()), das den tatsaechlichen rechten Rand
   von #image-stage misst und hier als Pixelwert einsetzt. */
.dance-figure-wrap {
  display: none;
  position: fixed;
  left: 0; /* Platzhalter - wird von app.js (positionDanceFigureWrap) auf den echten Wert gesetzt */
  top: 0;
  width: 100px; /* nur noch die "Buehne" fuer die Noten (kein Strichmaennchen mehr) */
  height: 100vh;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.6s ease;
}
@media (min-width: 1500px) {
  /* display:block stellt nur sicher, dass auf breiten Bildschirmen ueberhaupt
     Platz/Layout dafuer existiert - ob die Noten tatsaechlich zu sehen
     sind, steuert ausschliesslich .dancing ueber die opacity oben/unten
     (Ein-/Ausblenden synchron zum Start/Stop der Musik). */
  .dance-figure-wrap { display: block; }
  .dance-figure-wrap.dancing { opacity: 1; }
}

/* Musiknoten rechts neben der Bild-Buehne (Ersatz fuer das fruehere
   tanzende Strichmaennchen): mehrere kleine Notenzeichen in verschiedenen
   "Spuren" (--nx), die wie eine Dusche von oben nach unten durch die
   Buehne fallen, dabei ein-/ausblenden und sich danach in Dauerschleife
   wiederholen (Wunsch: "die noten [...] wie eine dusche von oben nach
   unten durchlaufen lassen und zum bass pulsieren lassen") - laufen NUR
   waehrend tatsaechlich Musik spielt (siehe .dancing), sonst bleiben sie
   unsichtbar/pausiert. Zusaetzlich setzt
   app.js (wireReactiveNotes) laufend die CSS-Variable --pulse auf jeder
   Note (>1 wenn gerade Bass/ein Beat erkannt wird, sonst zurueck auf 1) -
   sowohl die Groesse innerhalb der Fall-Animation als auch ein
   Leucht-Effekt (filter) reagieren live darauf, dadurch "pulsieren" die
   Noten hoerbar zum Bass statt nur gleichfoermig durchzufallen. Jede Note
   traegt zusaetzlich einen eigenen Farbton ueber --nhue, den app.js
   (wireReactiveNotes) bei jedem Fall-Durchlauf neu zufaellig wuerfelt
   (Wunsch: "in regenbogenfarben [...] zufaellig durchrotieren, so dass
   nicht immer die selbe note an der selben stelle die selbe farbe hat") -
   dadurch wechseln die Farben der Noten mit der Zeit durch, statt fest an
   ihre Position gebunden zu sein. */
.dance-notes {
  position: absolute;
  inset: 0;
  overflow: visible;
  pointer-events: none;
}
.dance-notes .note {
  position: absolute;
  left: var(--nx, 50%);
  top: var(--ny, 50%);
  font-size: var(--nsize, 18px);
  line-height: 1;
  /* Regenbogenfarben (Wunsch: "in regenbogenfarben angezeigt werden") -
     jede Note bekommt ueber --nhue (siehe index.php) einen eigenen,
     ueber alle Noten verteilten Farbton statt der frueheren einheitlichen
     blauen Farbe. Leuchtrand (text-shadow) und der Bass-Leuchteffekt
     (filter weiter unten) ziehen denselben Farbton mit, damit Farbe und
     Glueheffekt zusammenpassen. */
  color: hsl(var(--nhue, 210) 90% 72%);
  text-shadow: 0 0 6px hsl(var(--nhue, 210) 90% 60% / 0.55);
  opacity: 0;
  transform: translate(-50%, 0) scale(0.7);
  /* "ease-in" statt einer gleichfoermigen Bewegung passt zu einer
     Dusche/einem Regen aus Noten - die Note faellt zu Beginn (nahe --ny
     oben in der Buehne) langsamer und wird zum unteren Rand hin
     schneller, wie im freien Fall. */
  animation: danceNoteShower var(--ndur, 2.6s) ease-in infinite;
  animation-delay: var(--ndelay, 0s);
  animation-play-state: paused;
  /* Live-Leuchteffekt zum Bass: --pulse wird pro Frame von app.js gesetzt
     (Grundwert 1, kurzzeitig hoeher bei Bass/Beat) - direkt als Filter
     verwendet, unabhaengig von der Schwebe-Keyframe-Animation, reagiert
     also sofort statt erst beim naechsten Animationsdurchlauf. */
  filter: brightness(calc(0.9 + var(--pulse, 1) * 0.3)) drop-shadow(0 0 calc(3px + var(--pulse, 1) * 5px) hsl(var(--nhue, 210) 90% 60% / 0.65));
}
.dance-figure-wrap.dancing .note { animation-play-state: running; }
@keyframes danceNoteShower {
  /* var(--pulse, 1) skaliert die Note zusaetzlich zur normalen Fall-
     Groesse - da custom properties live ausgewertet werden, wirkt sich
     eine Aenderung von --pulse durch app.js sofort auf die laufende
     Animation aus, nicht erst beim naechsten Durchlauf. Die Note startet
     nahe ihrer --ny-Position ganz oben in der Buehne (.dance-figure-wrap
     ist jetzt "position:fixed" mit "height:100vh" - siehe weiter oben,
     Wunsch: "von ganz oben bis ganz unten [...] ueber die komplette
     hintergrund hoehe") und faellt ueber (fast) die komplette
     Bildschirmhoehe bis ganz nach unten durch, bevor sie ausblendet und
     der naechste Durchlauf oben neu beginnt - dadurch wirkt es wie eine
     durchlaufende Dusche aus Noten statt eines kleinen Schwebens auf der
     Stelle. */
  0%   { opacity: 0; transform: translate(-50%, 0) scale(calc(var(--pulse, 1) * 0.7)); }
  10%  { opacity: 1; }
  75%  { opacity: 0.9; }
  100% { opacity: 0; transform: translate(-50%, calc(100vh - 24px)) scale(calc(var(--pulse, 1) * 1.05)); }
}
/* ---------- Notizzettel ---------- */
/* Wunsch: "soll proportional zur fenstergroesse ... genau an der seite wie
   auf dem screenshot angezeigt werden".
   BUGFIX (Ruecklmeldung des Auftraggebers mit Vergleichsbildern: "der
   zettel ist viel zu gross und sitzt an der falschen position"): die
   ERSTE Umsetzung hatte Groesse/Abstand als Anteil der Breite der
   Bild-Buehne (#image-stage) berechnet - die Buehne ist aber responsiv
   und war beim tatsaechlichen Browserfenster des Auftraggebers deutlich
   breiter als beim Referenz-Screenshot, wodurch auch der Zettel viel zu
   gross wurde und viel zu weit rechts landete (teils ausserhalb des
   sichtbaren Bereichs). Jetzt werden Groesse und Abstaende stattdessen
   als Anteil der FENSTERBREITE (vw) berechnet - das ist zugleich naeher
   am woertlichen Wunsch "proportional zur Fenstergroesse" und bleibt bei
   jeder Fensterbreite angemessen klein, unabhaengig davon, wie breit die
   Buehne gerade responsiv gerendert wird. Nur die POSITION (an welcher
   Stelle auf der Seite der Anker fuer "rechts neben der Buehne" liegt)
   wird weiterhin per JS anhand der tatsaechlichen Buehnen-Position
   gemessen (app.js: positionNotizNotes(), analog zu
   positionDanceFigureWrap() oben) - nur eben nicht mehr deren BREITE als
   Bemessungsgrundlage fuer die Zettelgroesse selbst verwendet. */
.notiz-note {
  position: absolute;
  /* clamp(): waechst mit der Fensterbreite (11vw, aus dem Referenz-
     Screenshot vermessen: Zettelbreite / Gesamtbreite des Screenshots),
     aber nie kleiner als 130px (noch lesbar) und nie groesser als 260px
     (auch auf sehr breiten/4K-Bildschirmen kein "riesiger" Zettel mehr). */
  width: clamp(130px, 11vw, 260px);
  aspect-ratio: 1 / 1;
  pointer-events: none;
  z-index: 4;
  display: none; /* wird von app.js (positionNotizNotes) auf "block" gesetzt, sobald die Position berechnet ist - verhindert ein kurzes Aufblitzen an der falschen Stelle */
}
.notiz-note-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  filter: drop-shadow(0 6px 12px rgba(0, 0, 0, 0.35));
}
/* Textbereich innerhalb des gelben Papiers - bewusst deutlich eingerueckt
   (nicht bis an die Bildkante), damit der Text auch bei der leichten
   Drehung/den abgerundeten Ecken des Zettel-Fotos sowie der roten
   Pinnnadel oben sicher NICHT ueber den gelben Rand hinausragt (Wunsch:
   "ohne ueber den gelben rand zu kommen ... muss immer im gelben feld
   bleiben"). Die tatsaechliche Schriftgroesse wird inline per app.js
   (fitNotizText) gesetzt und bei Bedarf automatisch verkleinert, bis der
   Text hineinpasst. */
.notiz-note-text {
  position: absolute;
  top: 24%;
  left: 20%;
  right: 20%;
  bottom: 14%;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  overflow: hidden;
  line-height: 1.15;
  word-break: break-word;
  overflow-wrap: break-word;
  white-space: pre-wrap;
  /* Schriftart/-schnitt werden inline von index.php gesetzt (siehe
     get_notiz_settings()/NOTIZ_ALLOWED_FONTS - Wunsch: "die Schriftarten
     (Arial, Bahnschrift, Dreaming Outloud Pro)" sowie "auch Fettschrift
     (bold)"); Arial als Fallback, falls beides ausbleibt. */
  font-family: Arial, Helvetica, sans-serif;
}
/* Hinweis: Das Ein-/Ausblenden je nach Fensterbreite (auf schmalen
   Bildschirmen ist rechts neben der Buehne kein Platz mehr, siehe auch
   .dance-figure-wrap oben mit derselben 1500px-Grenze) uebernimmt bewusst
   NICHT eine CSS-Medienabfrage, sondern app.js (positionNotizNotes()) -
   da dieselbe Funktion ohnehin bei jeder Fenstergroessenaenderung laeuft
   und "display" dort per Inline-Style setzt, wuerde eine zusaetzliche
   CSS-Regel (niedrigere Spezifitaet als ein Inline-Style) hier ohnehin
   nichts bewirken. */

/* ---------- Tanzende Silhouette links neben der Bild-Buehne ----------
   Wunsch: "kannst du die mp4 datei ganz links neben den rahmen in dem der
   dienstplan eingebettet ist einbetten, aber ohne weissen hintergrund,
   sondern nur die tanzende silhouette, die so wie die noten nur angezeigt
   und abgespielt wird, wenn musik laeuft" - das Gegenstueck zu den
   Musiknoten rechts (.dance-figure-wrap weiter oben), nur eben links
   neben der Buehne statt rechts daneben.
   BUGFIX (Ruecklmeldung mit Screenshot: "der schwarze hintergrund ist
   nicht transparent, man sieht das schwarze rechteck"): die ERSTE
   Umsetzung hat dieses Element per "position:absolute" INNERHALB von
   .image-stage-wrap (also als Nachfahre von <main class="page">)
   platziert, um ganz ohne JavaScript auszukommen. Das Problem dabei:
   .page hat eine Eintritts-Animation (@keyframes pageEnter, "animation:
   ... both"), die dauerhaft einen (wenn auch optisch wirkungslosen)
   transform-Wert auf .page stehen laesst - und jeder CSS-transform-Wert
   erzeugt einen neuen Stacking-Context. "mix-blend-mode" blendet aber
   AUSSCHLIESSLICH mit Inhalten INNERHALB desselben Stacking-Contexts -
   das Video hat sein "screen" also nur gegen eine leere/transparente
   Flaeche INNERHALB von .page berechnet (Ergebnis: ein ganz normales,
   undurchsichtiges schwarzes Rechteck), statt gegen den eigentlich
   gewuenschten Hintergrund (--bg / .bg-glow / .music-bg-rainbow), der in
   einem GANZ ANDEREN Stacking-Context liegt (ausserhalb von .page).
   Genau aus diesem Grund (dort allerdings wegen "position:fixed" statt
   wegen mix-blend-mode) sitzt auch #dance-figure-wrap bereits ausserhalb
   von <main class="page"> - siehe der ausfuehrliche Kommentar dort.
   Jetzt behoben, indem dieses Element ebenfalls AUSSERHALB von .page (in
   index.php direkt neben #dance-figure-wrap) UND mit "position:fixed"
   (statt "position:absolute" relativ zu .image-stage-wrap) platziert
   wird - dadurch liegt es im selben obersten Stacking-Context wie
   .bg-glow/.music-bg-rainbow und kann korrekt gegen sie/den Seiten-
   hintergrund blenden. Die Kehrseite: "position:fixed" positioniert
   relativ zum Bildschirm, nicht mehr zu .image-stage-wrap - die
   Positionierung (top/left) uebernimmt deshalb jetzt app.js
   (positionDanceVideoWrap(), analog zu positionDanceFigureWrap()), das
   die tatsaechliche Position von #image-stage misst und bei Resize/Scroll
   aktuell haelt.
   Transparenter Hintergrund OHNE Video mit Alphakanal (den <video> in
   keinem gaengigen Browser sowieso darstellen wuerde): das Ausgangsvideo
   zeigt eine leuchtende, helle Zeichnung auf reinem Schwarz - ein
   Idealfall fuer "mix-blend-mode: screen". Bei diesem Mischmodus aendert
   ein schwarzer Pixel (0) den darunterliegenden Hintergrund rechnerisch
   ueberhaupt nicht (screen(0, x) = x) - das Schwarz des Videos wirkt also
   gegenueber dem dunklen Seitenhintergrund (--bg: #0a0d12) wie
   transparent, waehrend die hellen/leuchtenden Linien normal sichtbar
   bleiben. Braucht dadurch keine serverseitige Neukodierung mit
   Alphakanal.
   Der Umbruchpunkt (1700px) liegt bewusst hoeher als bei den Noten
   (1500px) - dieses Element braucht mit seiner eigenen Breite plus dem
   132px-Abstand deutlich mehr Platz links neben der Buehne, als die nur
   100px breite Noten-"Buehne" rechts braucht; empirisch mit Playwright an
   mehreren Fensterbreiten (1400/1700/1920/3440px) geprueft, dass ab
   1700px kein ungewollter horizontaler Scrollbalken entsteht.
   BUGFIX #2 (erneute Rueckmeldung mit Screenshot: "der hintergrund von der
   tanzenden frau ist immer noch schwarz und nicht transparent" - trotz
   BUGFIX oben): "position:fixed" IMMER, unabhaengig von z-index, einen
   NEUEN Stacking-Context (siehe MDN "stacking context" - anders als
   position:absolute/relative, wo dafuer erst z-index != auto noetig
   waere). #dance-video-wrap selbst hat "position:fixed" - dadurch
   erzeugt der WRAPPER SELBST bereits einen neuen, isolierten
   Stacking-Context fuer seine eigenen Kind-Elemente. "mix-blend-mode:
   screen" stand aber auf dem <video> DARIN (siehe vorherige Version
   dieser Regel) - dieses Blending fand also wieder nur INNERHALB des
   Wrappers statt, gegen praktisch nichts (nur das <video> selbst ist
   darin), genau dasselbe grundsaetzliche Problem wie BUGFIX oben, nur
   eine Ebene tiefer: diesmal nicht durch einen transformierten Vorfahren
   (.page) verursacht, sondern durch den Wrapper selbst. Mit einem
   isolierten Playwright-Test unabhaengig von diesem Video zweifelsfrei
   nachgewiesen (zwei identische schwarze Flaechen vor einem
   Farbverlauf-Hintergrund, eine mit "mix-blend-mode" auf dem inneren
   Kind-Element wie zuvor, eine mit "mix-blend-mode" auf dem aeusseren,
   position:fixed-Wrapper selbst): die Variante mit dem Blend-Mode auf
   dem INNEREN Element ergab einen tatsaechlich reinschwarzen Pixel
   (0,0,0) - keinerlei Blending fand statt, exakt das gemeldete Problem.
   Die Variante mit dem Blend-Mode auf dem AEUSSEREN, fixed-positionierten
   Wrapper selbst ergab einen Pixel, der (bis auf Rundungsfehler) exakt
   dem Hintergrund an derselben Stelle entspricht - echte, nachweisbare
   Transparenz. Loesung: "mix-blend-mode: screen" steht jetzt HIER auf
   dem Wrapper selbst (nicht mehr auf dem <video> darin) - dadurch
   blendet der (fertig gerenderte) Wrapper als Ganzes gegen SEINEN
   Elternkontext (Bodyebene, dort liegen .bg-glow/.music-bg-rainbow/
   der Sternenhimmel), statt dass sein <video>-Kind vergeblich versucht,
   gegen die eigene, leere Wrapper-Ebene zu blenden. */
.dance-video-wrap {
  display: none;
  position: fixed;
  top: 0; /* Platzhalter - wird von app.js (positionDanceVideoWrap) auf den echten Wert gesetzt */
  left: 0; /* Platzhalter - wird von app.js (positionDanceVideoWrap) auf den echten Wert gesetzt */
  width: clamp(140px, 10vw, 200px);
  /* BUGFIX (bei der eigenen Nachverifikation der Positionierung aufgefallen,
     per Playwright gemessen: bis zu ~69px vertikaler Versatz): OHNE
     "aspect-ratio" haengt die tatsaechliche Rendering-Hoehe dieses
     Containers rein von "height:auto" auf dem <video> darunter ab - und
     DAS wiederum vom tatsaechlichen Ladezustand des Videos. Solange das
     Video (wegen preload="none") noch keine Metadaten geladen hat, kennt
     der Browser dessen echtes Seitenverhaeltnis (450:674) noch nicht und
     rendert stattdessen mit einer Browser-Standardhoehe (in Tests
     gemessen: 150px statt der eigentlich erwarteten ~288px bei
     192px Breite). positionDanceVideoWrap() in app.js errechnet die
     Vertikal-Zentrierung aber anhand des BEKANNTEN Seitenverhaeltnisses
     (DANCE_VIDEO_ASPECT_RATIO = 674/450), nicht anhand der tatsaechlich
     gerenderten Boxhoehe - ohne "aspect-ratio" hier entsteht dadurch ein
     Missverhaeltnis zwischen angenommener und tatsaechlicher Hoehe, bis
     die Video-Metadaten geladen sind (kurzes, aber reales Verrutschen
     auch bei echten Nutzern, nicht nur ein Sandbox-Artefakt). Mit
     "aspect-ratio" steht die Boxhoehe dagegen sofort beim Seitenaufbau
     fest, unabhaengig vom Ladezustand des Videos - exakt dasselbe
     Seitenverhaeltnis, das auch app.js fuer die Positionierung annimmt. */
  aspect-ratio: 450 / 674;
  /* Siehe BUGFIX #2 oben: hier (auf dem Wrapper), NICHT auf dem <video>
     darin. */
  mix-blend-mode: screen;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.6s ease;
}
@media (min-width: 1700px) {
  .dance-video-wrap { display: block; }
  .dance-video-wrap.dancing { opacity: 1; }
}
/* Doppelpufferung gegen den (kurzen) Rest-Hänger am Schleifenpunkt - siehe
   ausfuehrlicher Kommentar bei getDanceVideoController() in app.js und bei
   #dance-video-wrap in index.php. Zwei <video>-Elemente liegen exakt
   deckungsgleich uebereinander ("position:absolute" innerhalb des
   "position:fixed"-Wrappers, der dafuer bereits den noetigen Containing
   Block bildet); app.js blendet per Klasse "dance-video-active" (reine
   Opacity, keine Dekodier-Kosten) um, statt den Schleifensprung per
   currentTime-Zuweisung (="Seek", siehe app.js-Kommentar) umzusetzen. */
.dance-video-wrap video {
  display: block;
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
}
.dance-video-wrap video.dance-video-active {
  opacity: 1;
}

.image-stage {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-lg);
  background: linear-gradient(160deg, var(--surface-2), var(--bg-soft));
  border: 1px solid var(--border);
  box-shadow: var(--shadow);
  overflow: hidden;
  transition: border-color 0.25s ease, box-shadow 0.25s ease, padding 0.25s ease;
  /* "Passepartout"-Rahmen: der Abstand hier ist die Matte zwischen
     Gehaeuse und Foto. Absolut positionierte Ebenen (Platzhalter,
     Diashow-Layer, Zeitstempel) ignorieren dieses Padding bewusst (siehe
     unten) und fuellen weiterhin die komplette Buehne - der Rahmen
     erscheint also automatisch NUR, wenn das eigene Foto (#display-image,
     normaler Fluss-Inhalt) tatsaechlich sichtbar ist, ohne dass dafuer
     extra JS/PHP-Klassen noetig sind. */
  padding: 18px;
  box-sizing: border-box;
  /* Zentriert #display-image, das jetzt (siehe unten) nur noch so gross
     ist wie das Foto selbst tatsaechlich dargestellt wird - der Rahmen
     liegt dadurch direkt an der Grafik an, nicht an schwarzen
     Letterbox-Balken drumherum, egal welches Seitenverhaeltnis das Foto
     hat. */
  display: flex;
  align-items: center;
  justify-content: center;
}
.image-stage:hover {
  border-color: rgba(91, 140, 255, 0.35);
  box-shadow: 0 12px 44px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(91, 140, 255, 0.15), 0 0 40px rgba(91, 140, 255, 0.08);
}
.image-stage:fullscreen:hover,
.image-stage:-webkit-full-screen:hover { box-shadow: none; }

.image-stage:fullscreen,
.image-stage:-webkit-full-screen {
  width: 100vw;
  height: 100vh;
  max-width: none;
  aspect-ratio: auto;
  border-radius: 0;
  /* Im Vollbild soll das Foto randlos den ganzen Bildschirm fuellen -
     der Rahmen/die Matte macht dort keinen Sinn und wird entfernt. */
  padding: 0;
  background: #000;
}

.image-stage img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}

/* Modernes Passepartout um das eigene hochgeladene Foto: #display-image
   wird NICHT mehr auf 100% der Buehne gestreckt (das haette bei einem
   Foto, dessen Seitenverhaeltnis nicht exakt 16:9 ist, zu schwarzen
   Letterbox-Baelken IM Rahmen gefuehrt) - stattdessen bekommt es genau
   die Groesse, in der das Foto tatsaechlich dargestellt wird (max-width/
   max-height + width/height:auto, zentriert per Flexbox auf .image-stage).
   Der Rahmen (Ring aus heller Linie + Akzent-Blau + Schatten) liegt damit
   direkt an der Grafik selbst an, ganz gleich welches Format das Foto
   hat, ohne dabei auch nur ein Pixel des Fotos zu verdecken. Gilt bewusst
   nur fuer #display-image (das eigene Foto), NICHT fuer die
   Diashow-Ebenen, die weiterhin randlos die komplette Buehne fuellen. */
#display-image {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  border-radius: calc(var(--radius-lg) - 8px);
  box-shadow:
    0 0 0 3px rgba(255, 255, 255, 0.92),
    0 0 0 6px rgba(91, 140, 255, 0.65),
    0 14px 34px rgba(0, 0, 0, 0.55);
  background: #05070b;
  /* Wunsch: Klick auf das Bild oeffnet den Schichthinweis (bzw. das
     "sehr gut"-Hinweisfenster) - siehe wireDisplayImageClick() in
     assets/app.js. */
  cursor: pointer;
}
/* Wunsch: "in der diashow sieht man bei jedem bildwechseln so ein kleinen
   weißen rahmen[...] das soll nicht sein." Ursache: waehrend die Diashow
   laeuft, haelt transitionToSlideshowImage() in assets/app.js #display-image
   bewusst im Hintergrund synchron zum jeweils aktuellen Diashow-Bild (damit
   beim Stoppen der Diashow sanft zum normalen "eigenes Foto"-Rahmen
   uebergeblendet wird, siehe stopSlideshow()). Die beiden Diashow-Ebenen
   darueber (".slideshow-layer") ueberdecken #display-image dabei normalerweise
   vollstaendig - beim Umschalten auf ein neues Bild beginnt das Ausblenden
   der alten Ebene aber ein bis zwei Animationsframes FRUEHER als das
   Einblenden der neuen Ebene, wodurch fuer einen Sekundenbruchteil beide
   Ebenen zusammen nicht ganz deckend sind und der weisse/blaue
   Rahmen-Schatten von #display-image durchscheinen konnte. Waehrend die
   Diashow AKTIV LAEUFT (Klasse "slideshow-running" auf #image-stage, siehe
   startSlideshow()/stopSlideshow()) wird #display-image deshalb komplett
   unsichtbar gehalten - das Foto samt Rahmen bleibt dadurch bei jedem
   Bildwechsel verlaesslich verdeckt. Sobald die Diashow gestoppt wird,
   entfaellt die Klasse sofort wieder, sodass der anschliessende Verblassen
   der obersten Ebene (siehe stopSlideshow()) wie gewohnt sanft zum
   Foto-Rahmen ueberblendet. */
.image-stage.slideshow-running #display-image {
  opacity: 0;
}
.image-stage:fullscreen #display-image,
.image-stage:-webkit-full-screen #display-image {
  width: 100%;
  height: 100%;
  border-radius: 0;
  box-shadow: none;
}
@media (max-width: 640px) {
  .image-stage { padding: 12px; }
}

.fullscreen-exit-btn {
  position: absolute;
  top: 16px;
  right: 16px;
  z-index: 5;
  display: none;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  background: rgba(10, 13, 18, 0.7);
  color: var(--text);
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
  backdrop-filter: blur(6px);
}
.fullscreen-exit-btn:hover { border-color: var(--accent); }

/* ---------- Hintergrundmusik: Regenbogen-Hintergrund (nur Login-Fenster) ----------
   Liegt auf z-index 0, also GENAU AUF DER EBENE von .bg-glow/.bg-grid/
   .particles, und damit HINTER dem eigentlichen Login-Kaestchen
   (.lock-screen/.login-box-wrap/.login-box liegen alle auf z-index 1
   und haben einen deckenden Hintergrund) - der Effekt betrifft also
   sichtbar nur den Hintergrund, nie das Login-Fenster selbst. Wird per
   JS (app.js, Web-Audio-API) im Takt der Login-Musik angesteuert:
   --music-hue/--music-glow-opacity/--music-scale werden dafuer laufend
   aktualisiert, solange die Musik laeuft. Sobald man sich einloggt,
   wird diese komplette Login-Ansicht (inkl. Musik/Skript) durch das
   normale Neuladen der Seite ersetzt - der Effekt endet damit von
   selbst. */
.music-bg-rainbow {
  position: fixed;
  inset: -15%;
  z-index: 0;
  pointer-events: none;
  opacity: 0;
  background: conic-gradient(
    from calc(var(--music-hue, 0) * 1deg) at 50% 50%,
    #ff2d55, #ff8a00, #ffee00, #4dff6a, #00e5ff, #4d6bff, #b04dff, #ff2d55
  );
  filter: blur(80px) saturate(1.6);
  transform: scale(var(--music-scale, 1));
  transition: opacity 0.6s ease;
  will-change: opacity, transform;
}
body.music-reactive .music-bg-rainbow {
  opacity: var(--music-glow-opacity, 0.5);
}
@media (prefers-reduced-motion: reduce) {
  .music-bg-rainbow { transition: none; }
}

/* ---------- Hintergrundmusik: Freigabe-Button (Login-Fenster) ----------
   Browser blockieren autoplay MIT Ton, solange der/die Benutzer noch
   nicht mit der Seite interagiert hat - dieser Button erscheint dann
   automatisch (siehe app.js) und startet die Musik nach einem Klick.
   Sitzt als normales Flex-Kind mittig ueber dem Login-Kaestchen (siehe
   .lock-screen, display:flex + align-items:center), NICHT frei
   positioniert - dadurch bleibt "mittig ueber dem Login-Fenster" auch
   bei unterschiedlichen Bildschirmgroessen automatisch erhalten. */
.music-enable-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 18px;
  border-radius: 999px;
  border: 1px solid var(--accent);
  background: var(--surface-2);
  color: var(--text);
  font-family: var(--font);
  font-weight: 500;
  font-size: 0.85rem;
  cursor: pointer;
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.35), 0 0 0 3px var(--accent-soft);
  animation: musicBtnPulse 2.2s ease-in-out infinite;
  /* 60px weiter nach oben versetzt (Wunsch: mehr Abstand zum
     Login-Kaestchen darunter) - per transform, damit sich dadurch nicht
     zusaetzlich die Flex-Zentrierung/Position des Login-Kaestchens
     selbst verschiebt. */
  transform: translateY(-60px);
}
.music-enable-btn svg { width: 16px; height: 16px; color: var(--accent); flex-shrink: 0; }
.music-enable-btn:hover { background: var(--surface-hover); border-color: var(--accent-hover); }
@keyframes musicBtnPulse {
  0%, 100% { box-shadow: 0 8px 22px rgba(0, 0, 0, 0.35), 0 0 0 3px var(--accent-soft); }
  50% { box-shadow: 0 8px 22px rgba(0, 0, 0, 0.35), 0 0 0 6px var(--accent-soft); }
}
.image-stage:fullscreen .fullscreen-exit-btn,
.image-stage:-webkit-full-screen .fullscreen-exit-btn { display: flex; }

/* Wunsch: "beim vergrößern/zoomen der bilder ruckelt das bild permanent
   und das liegt nicht an der leistung vom pc ... ansonsten das zoomen
   ausschalten" - der frueher hier vorhandene 14 Sekunden lange Ken-Burns-
   Zoom (Transform von scale(1) auf scale(1.08), Klasse "layer-zoom") wurde
   deshalb komplett entfernt: zwei grosse, ueberlagerte Bildebenen
   gleichzeitig zu skalieren UND per Opacity ueberzublenden fuehrte schon
   bei Touch-Geraeten (siehe historisch "no-zoom" weiter unten) zu
   sichtbarem Ruckeln und trat inzwischen auch auf Desktop-Geraeten mit an
   sich ausreichender Leistung auf. Es bleibt bewusst nur noch die reine
   Ueberblendung (Opacity) uebrig - die ist deutlich anspruchsloser fuer den
   Browser und laeuft dadurch zuverlaessig ruckelfrei. */
.slideshow-layer {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 3.2s ease-in-out;
  pointer-events: none;
  z-index: 2;
  will-change: opacity;
  backface-visibility: hidden;
}
.slideshow-layer.layer-visible { opacity: 1; }

/* ---------- Leistungsmodus: aufwendige Hintergrund-Effekte abschalten ----------
   Wunsch: "... effekte wie zum beispiel feuerwerk abschaltet, das die
   rechenleistung ausreicht". Im Leistungsmodus (Klasse "reduced-effects" auf
   <body>, gesetzt von wirePerformanceMode() in assets/app.js) werden alle
   bestaetigten Effekte ausgeblendet: Feuerwerk, Hintergrund-Partikel, der
   Aurora-Glow-Hintergrund und (NACHTRAG, Wunsch: "der Leistungsmodus soll
   die taenzerin auch abschalten, wenn der pc zu schwach von der
   rechenleistung ist") die tanzende Silhouette links neben der Bild-Buehne
   - Video-Dekodierung samt CSS-Mischmodus ist auf schwachen Geraeten
   spuerbar teuer. Das Feuerwerk stoppt zusaetzlich auch seine
   Zeichenschleife komplett (siehe window.b420SetFireworksReducedEffects),
   die Silhouette wird zusaetzlich auch tatsaechlich pausiert (siehe
   applyMode() in app.js, dort danceVideo.pause()) statt nur per CSS
   ausgeblendet - das Ausblenden hier greift zusaetzlich sofort per CSS,
   unabhaengig vom Zeitpunkt der JS-Ausfuehrung. */
body.reduced-effects .bg-glow,
body.reduced-effects .particles,
body.reduced-effects .fireworks-canvas,
body.reduced-effects .dance-video-wrap {
  display: none;
}

.image-placeholder {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  color: var(--text-faint);
}
.image-placeholder svg { width: 34px; height: 34px; opacity: 0.6; }
.image-placeholder span.label { font-size: 0.85rem; letter-spacing: 0.02em; }

/* Zeitstempel "Zuletzt aktualisiert: ..." - dezentes Badge links auf dem
   Bild: gut lesbar, aber klein genug, um nichts zu verdecken. Reagiert
   nicht auf Klicks (pointer-events: none), damit es nie im Weg ist.
   Die vertikale Position ("top") wird per JS animiert (siehe app.js,
   "Zeitstempel-Badge: vertikales Abprallen") - das Badge gleitet
   gleichmaessig von oben nach unten und zurueck, wie ein abprallendes
   DVD-Logo. "top: 14px" hier ist nur der Startwert vor dem ersten
   JS-Frame. */
.image-timestamp {
  position: absolute;
  left: 14px;
  top: 14px;
  z-index: 4;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  max-width: calc(100% - 28px);
  padding: 8px 14px;
  border-radius: 14px;
  background: rgba(10, 13, 18, 0.6);
  border: 1px solid var(--border);
  backdrop-filter: blur(6px);
  color: var(--text-faint);
  font-size: 0.74rem;
  line-height: 1.3;
  text-align: center;
  pointer-events: none;
  opacity: 0.78;
  transition: opacity 0.2s ease;
}
.image-stage:hover .image-timestamp { opacity: 1; }
.image-timestamp svg { width: 13px; height: 13px; flex-shrink: 0; opacity: 0.8; margin-bottom: 1px; }
.image-timestamp-label { white-space: nowrap; }
.image-timestamp-value { color: var(--text); font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
/* Zusatzhinweis "- HEUTE -", nur sichtbar, wenn das angezeigte Bild vom
   heutigen Kalendertag ist (siehe index.php/$imageIsFromToday sowie
   app.js/setImageTimestampNow) - dank der bereits bestehenden
   flex-direction:column + align-items:center-Anordnung von
   .image-timestamp automatisch mittig unter dem Zeitstempel. */
.image-timestamp-today {
  color: var(--success);
  font-weight: 700;
  font-size: 0.68rem;
  letter-spacing: 0.04em;
  white-space: nowrap;
}
@media (max-width: 640px) {
  .image-timestamp { font-size: 0.68rem; padding: 6px 12px; left: 10px; top: 10px; }
}

/* ---------- Werkzeugleiste ---------- */
.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 22px;
  padding: 16px 22px;
  border-radius: var(--radius-lg);
  background: var(--surface);
  border: 1px solid var(--border);
  transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}
.toolbar:hover {
  transform: translateY(-2px);
  border-color: rgba(91, 140, 255, 0.25);
  box-shadow: 0 12px 30px rgba(0, 0, 0, 0.35), 0 0 0 1px var(--accent-soft);
}

.toolbar-group {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.toolbar-divider {
  width: 1px;
  align-self: stretch;
  background: var(--border);
}

/* Zwei farblich getrennte Untergruppen: Hochladen (blau) und Snipping Tool (tuerkis) */
.toolbar-subgroup {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  padding: 8px 14px;
  border-radius: 999px;
  border: 1px solid transparent;
  transition: border-color 0.2s ease, background 0.2s ease;
}

.toolbar-subgroup-upload {
  background: var(--accent-soft);
  border-color: rgba(91, 140, 255, 0.35);
}

.toolbar-subgroup-snip {
  background: rgba(52, 209, 196, 0.14);
  border-color: rgba(52, 209, 196, 0.35);
}

/* Die Buttons innerhalb der tuerkisen Gruppe brauchen einen eigenen,
   dunkleren Hintergrund, sonst verschwimmen sie mit der tuerkisen Faerbung
   der Gruppe (btn-ghost ist normalerweise transparent). */
.toolbar-subgroup-snip .btn-ghost {
  background: var(--surface-2);
  border-color: rgba(52, 209, 196, 0.5);
  color: var(--text);
}
.toolbar-subgroup-snip .btn-ghost:hover {
  background: var(--surface-hover);
  border-color: rgba(52, 209, 196, 0.8);
  color: var(--text);
}

.toolbar .url-input {
  padding: 10px 14px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--bg-soft);
  color: var(--text);
  font-family: var(--font);
  font-size: 0.88rem;
  min-width: 200px;
}
.toolbar .url-input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }

/* Diashow-Suchfeld: auf Wunsch um 50% verkleinert (200px -> 100px). */
#slideshow-query-input.url-input {
  min-width: 100px;
  width: 100px;
}

.hint {
  color: var(--text-faint);
  font-size: 0.78rem;
  margin: 0;
}

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  cursor: pointer;
  border: 1px solid var(--border-strong);
  background: var(--surface-2);
  color: var(--text);
  font-family: var(--font);
  font-weight: 500;
  font-size: 0.85rem;
  padding: 9px 16px;
  border-radius: 999px;
  text-decoration: none;
  position: relative;
  overflow: hidden;
  isolation: isolate;
  transition: transform 0.15s ease, background 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}
.btn svg { width: 16px; height: 16px; flex-shrink: 0; }
/* Glanz-Sweep beim Hover: ein diagonaler Lichtstreif laeuft einmal ueber den Button */
.btn::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(115deg, transparent 35%, rgba(255, 255, 255, 0.22) 50%, transparent 65%);
  transform: translateX(-120%);
  transition: transform 0.55s ease;
  pointer-events: none;
  z-index: 0;
}
.btn:hover::before { transform: translateX(120%); }
.btn:hover { background: var(--surface-hover); border-color: var(--border-strong); transform: translateY(-2px); box-shadow: 0 6px 16px rgba(0, 0, 0, 0.3); }
.btn:active { transform: translateY(0); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; box-shadow: none; }
.btn:disabled::before { display: none; }

.btn-primary {
  background: linear-gradient(135deg, var(--accent), #4f7aff);
  border-color: var(--accent);
  color: #06090f;
  font-weight: 600;
}
.btn-primary:hover {
  background: linear-gradient(135deg, var(--accent-hover), #6a8fff);
  border-color: var(--accent-hover);
  box-shadow: 0 8px 22px rgba(91, 140, 255, 0.35);
}

.btn-ghost { background: transparent; border-color: var(--border); color: var(--text-dim); }
.btn-ghost:hover { color: var(--text); border-color: var(--border-strong); background: var(--surface); }

.btn-danger { border-color: rgba(242, 73, 92, 0.35); color: #ffb2bb; background: transparent; }
.btn-danger:hover { background: var(--danger-soft); border-color: rgba(242, 73, 92, 0.6); }
.btn-danger:disabled { opacity: 0.35; }

.btn-sm { padding: 7px 12px; font-size: 0.8rem; }

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--text-dim);
  cursor: pointer;
}
.icon-btn:hover { color: var(--text); border-color: var(--accent); transform: translateY(-1px); }
.icon-btn svg { width: 17px; height: 17px; }

/* ---------- Leistungsmodus-Schalter in der Kopfzeile ----------
   Wunsch: automatische Erkennung schwacher Hardware PLUS ein manueller
   3-Stufen-Schalter (Automatisch/An/Aus), siehe wirePerformanceMode() in
   assets/app.js. Ein Klick schaltet der Reihe nach durch alle drei Stufen;
   der aktuelle Stand steht als kurzes Label neben dem Blitz-Symbol, Details
   (welche Effekte aktuell abgeschaltet sind, was die Automatik erkannt hat)
   stehen im title-/aria-label-Tooltip. Optisch dieselbe "Pillen"-Bauform
   wie der Musik-Player daneben. Bewusst NICHT auf Handy/Tablet ausgeblendet
   (anders als die zwischenzeitlich wieder entfernte Zoom-Steuerung) - der
   Leistungsmodus soll auf jedem Geraet nutzbar bleiben. */
.performance-mode-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 32px;
  padding: 0 12px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--text-dim);
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
}
.performance-mode-btn:hover { color: var(--text); border-color: var(--accent); }
.performance-mode-btn svg { width: 15px; height: 15px; flex-shrink: 0; }
.performance-mode-btn.performance-mode-active { color: var(--accent); border-color: var(--accent); }

/* ---------- Musik-Player der Startseite (Kopfzeile) ----------
   Bedienknoepfe (Zurueck/Play-Pause/Weiter) UND die Auswahlliste bilden
   zusammen EINEN gemeinsamen, optisch verbundenen Bereich (ein
   gemeinsamer Rahmen/Hintergrund statt zweier getrennter "Pillen") -
   dadurch ist auf einen Blick erkennbar, dass beides zusammengehoert.
   Waehrend tatsaechlich (hoerbar) Musik laeuft, bekommt der gesamte
   Bereich zusaetzlich einen Akzent-Rahmen/Leuchten (Klasse .is-playing,
   wird von app.js/wireFrontendMusicPlayer gesetzt). */
.frontend-player {
  display: flex;
  align-items: center;
  gap: 1px;
  padding: 3px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface-2);
  transition: border-color 0.3s ease, box-shadow 0.3s ease;
}
.frontend-player.is-playing {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.frontend-player-btn {
  width: 32px;
  height: 32px;
  border: none;
  background: transparent;
}
.frontend-player-btn:hover { background: rgba(255, 255, 255, 0.08); border-color: transparent; transform: none; }
.frontend-player-btn svg { width: 14px; height: 14px; }
.frontend-track-select {
  max-width: 140px;
  padding: 6px 10px 6px 9px;
  margin-left: 3px;
  border-radius: 0 999px 999px 0;
  border: none;
  border-left: 1px solid var(--border);
  background: transparent;
  color: var(--text-dim);
  font-family: var(--font);
  font-size: 0.78rem;
  cursor: pointer;
}
.frontend-track-select:focus { outline: none; color: var(--text); }
/* Die aufgeklappte Liste eines <select> wird vom Browser/Betriebssystem
   gerendert und ignoriert dabei "background: transparent" auf dem <select>
   selbst - ohne explizite Farben hier landet man auf hellem/weissem
   System-Hintergrund kombiniert mit dem hellen "--text-dim"-Text von oben,
   was praktisch unlesbar ist (fast gleiche Helligkeit). Deshalb hier fuer
   JEDE <option> einen eigenen, garantiert kontrastreichen dunklen
   Hintergrund samt hellem Text setzen - unabhaengig vom Betriebssystem-Theme. */
.frontend-track-select option {
  background-color: var(--surface-2);
  color: var(--text);
}
/* Aktuell geladenes/abspielendes Musikstueck farblich erkennbar machen -
   sowohl in der (dann aufgeklappten) Liste als auch im geschlossenen Feld
   selbst, da Browser den Stil der gerade ausgewaehlten <option> normaler-
   weise auch im geschlossenen Feld uebernehmen. app.js setzt dafuer die
   Klasse "now-playing" auf genau der aktuell geladenen <option>, und
   zusaetzlich "is-audible" auf dem <select>, solange wirklich (hoerbar)
   abgespielt wird statt nur pausiert/geladen. Bekommt zusaetzlich einen
   gruenen Hintergrund (mit dunklem, garantiert kontrastreichem Text),
   damit es auf einen Blick in der Liste erkennbar ist. */
.frontend-track-select option.now-playing {
  background-color: var(--success);
  color: #08281d;
  font-weight: 600;
}
.frontend-track-select.is-audible {
  color: var(--accent-text, var(--accent));
}
@media (max-width: 780px) {
  .frontend-track-select { display: none; }
}

/* ---------- Kopfzeile: Radio-Player + Stumm-Knopf + MP3-Player als EINE
   zusammenhaengende "Medienkonsole" ----------
   Wunsch: "die aufteilung in der headline, passt nicht und ist
   unübersichtlich [...] professionell aufteilen." Radio-Player und
   MP3-Player waren vorher zwei einzelne, optisch unabhaengige "Pillen"
   (siehe .frontend-player oben) mit dem Stumm-Knopf als dritter, wieder
   anders geformter (runder) Baustein dazwischen - macht auf den ersten
   Blick nicht klar, dass alle drei zusammengehoeren. ".topbar-group-media"
   (siehe HTML in index.php/events.php/event-gallery.php) ist jetzt selbst
   die (einzige) Pille: die inneren ".frontend-player"-Bereiche und der
   Stumm-Knopf verlieren dafuer ihren EIGENEN Rahmen/Hintergrund und
   fuegen sich stattdessen nahtlos in die gemeinsame Huelle ein, jeweils
   durch einen sehr duennen Trennstrich voneinander abgesetzt. Der
   Akzent-Rahmen/Leuchten fuer "gerade wird abgespielt" (.is-playing)
   bleibt erhalten, wirkt jetzt aber gezielt nur auf dem jeweils
   spielenden Abschnitt (Radio ODER MP3-Player) statt auf der ganzen
   Konsole. */
.topbar-group-media {
  padding: 3px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface-2);
  gap: 2px;
}
.topbar-group-media .frontend-player {
  padding: 0;
  border-radius: 999px;
  border: 1px solid transparent;
  background: transparent;
}
/* Wunsch: "der radioplayer und der mp3-player sollen farblich besser
   voneinander unterschieden werden koennen" - Radio bekommt durchgehend
   (nicht nur beim Abspielen) die Akzentfarbe Tuerkis (--teal), der
   MP3-Player bleibt bei der bisherigen Akzentfarbe Blau (--accent). So ist
   auf einen Blick erkennbar, welcher der beiden Bereiche welcher ist -
   unabhaengig davon, ob gerade etwas laeuft. Die "is-playing"-Hervorhebung
   nutzt konsequent dieselbe jeweilige Farbe, nur staerker/gesaettigter.
   NACHBESSERUNG: eine reine Icon-/Text-Einfaerbung (siehe vorherige
   Fassung) war bei normaler Bildschirmgroesse/-abstand kaum wahrnehmbar
   (nur duenne SVG-Striche in kleiner Groesse) - jetzt bekommt der GESAMTE
   Bereich (Pille) bereits im Ruhezustand einen deutlich sichtbaren
   farbigen Hintergrund-/Rahmen-Ton, nicht erst beim Abspielen. */
.topbar-group-media #radio-player {
  border-color: rgba(52, 209, 196, 0.4);
  background: rgba(52, 209, 196, 0.14);
}
.topbar-group-media #radio-player .frontend-player-btn,
.topbar-group-media #radio-player .frontend-track-select {
  color: var(--teal);
}
.topbar-group-media #radio-player .frontend-player-btn:hover { background: rgba(52, 209, 196, 0.22); }
.topbar-group-media #radio-player.is-playing {
  border-color: var(--teal);
  background: rgba(52, 209, 196, 0.26);
  box-shadow: 0 0 0 2px var(--teal-soft);
}
.topbar-group-media #frontend-player {
  border-color: rgba(91, 140, 255, 0.4);
  background: rgba(91, 140, 255, 0.14);
}
.topbar-group-media #frontend-player .frontend-player-btn,
.topbar-group-media #frontend-player .frontend-track-select {
  color: var(--accent);
}
.topbar-group-media #frontend-player .frontend-player-btn:hover { background: rgba(91, 140, 255, 0.22); }
.topbar-group-media #frontend-player.is-playing {
  border-color: var(--accent);
  background: rgba(91, 140, 255, 0.26);
  box-shadow: 0 0 0 2px var(--accent-soft);
}
.topbar-group-media #music-mute-btn {
  width: 32px;
  height: 32px;
  border: none;
  border-radius: 999px;
  background: transparent;
}
.topbar-group-media #music-mute-btn:hover { background: rgba(255, 255, 255, 0.08); transform: none; }
.topbar-group-media #music-mute-btn svg { width: 15px; height: 15px; }
/* Wunsch: "bei dem Mute-Button soll der ring um den button (bei mute) in
   einem kraeftigen rot leuchten. wenn musik zu hoeren ist, soll der ring
   [...] in einem hellen kraeftigen gruen leuchten. immer nur der aeussere
   ring soll rot/gruen leuchten." Die Klassen "is-muted"/"is-audible"
   werden von app.js (wireMusicMuteButton) synchron mit dem tatsaechlichen
   Stummschaltungs- UND Wiedergabe-Zustand beider Audioquellen (Radio + MP3)
   gesetzt - nicht nur bei einem Klick auf diesen Button selbst, sondern
   auch bei automatischen Aenderungen (Autoplay-Fallback, Play/Pause ueber
   die Player selbst). Bewusst NUR der Ring (box-shadow, zweischichtig fuer
   einen sichtbaren Leucht-/Glimmer-Effekt) wird eingefaerbt - das Icon
   selbst bleibt in der neutralen Grundfarbe (kein "color:"-Wechsel), damit
   ausschliesslich der aeussere Ring die Farbe traegt. */
.topbar-group-media #music-mute-btn.is-muted {
  box-shadow: 0 0 0 2px var(--danger), 0 0 9px 2px rgba(242, 73, 92, 0.75);
}
.topbar-group-media #music-mute-btn.is-muted:hover { background: var(--danger-soft); }
.topbar-group-media #music-mute-btn.is-audible {
  box-shadow: 0 0 0 2px var(--ring-green), 0 0 9px 2px rgba(45, 230, 133, 0.75);
}
.topbar-group-media #music-mute-btn.is-audible:hover { background: var(--ring-green-soft); }
/* Duenner Trennstrich innerhalb der Medienkonsole (Radio-Player | Stumm-
   Knopf | MP3-Player) - bewusst als eigenes, unauffaelliges Element per
   PHP nur dann eingefuegt, wenn auf BEIDEN Seiten tatsaechlich etwas
   steht (siehe index.php/events.php/event-gallery.php), statt per CSS
   ":has()" zu raten - so bleibt es unabhaengig vom Browser zuverlaessig. */
.topbar-media-divider {
  width: 1px;
  height: 18px;
  background: var(--border);
  flex-shrink: 0;
  margin: 0 2px;
}

/* ---------- Klick-Glow (kurzer Leucht-Ring beim Klicken jedes Buttons) ---------- */
@keyframes clickGlow {
  0%   { box-shadow: 0 0 0 0 rgba(91, 140, 255, 0.45); }
  60%  { box-shadow: 0 0 0 10px rgba(91, 140, 255, 0.12); }
  100% { box-shadow: 0 0 0 14px rgba(91, 140, 255, 0); }
}
.btn.click-glow,
.icon-btn.click-glow,
.user-menu-btn.click-glow,
.event-tile-btn.click-glow,
.event-new-toggle-btn.click-glow,
.fullscreen-exit-btn.click-glow,
.lightbox-close-btn.click-glow,
.lightbox-nav-btn.click-glow,
.event-move-btn.click-glow,
.gallery-thumb-move-btn.click-glow,
.gallery-thumb-download-btn.click-glow,
.gallery-thumb-hide-btn.click-glow,
.frontend-player-btn.click-glow { animation: clickGlow 0.55s ease-out; }
.btn-primary.click-glow { animation: clickGlowPrimary 0.55s ease-out; }
@keyframes clickGlowPrimary {
  0%   { box-shadow: 0 0 0 0 rgba(91, 140, 255, 0.6); }
  60%  { box-shadow: 0 0 0 12px rgba(91, 140, 255, 0.16); }
  100% { box-shadow: 0 0 0 16px rgba(91, 140, 255, 0); }
}
/* Loeschen-aehnliche Buttons: derselbe kurze Leucht-Ring, aber in Warn-/
   Gefahrenfarbe statt Akzentblau (konsistent mit ihrem dauerhaften
   rot-getoenten Glimmen, siehe weiter unten). */
.btn-danger.click-glow,
.gallery-thumb-delete-btn.click-glow { animation: clickGlowDanger 0.55s ease-out; }
@keyframes clickGlowDanger {
  0%   { box-shadow: 0 0 0 0 rgba(242, 73, 92, 0.55); }
  60%  { box-shadow: 0 0 0 12px rgba(242, 73, 92, 0.14); }
  100% { box-shadow: 0 0 0 16px rgba(242, 73, 92, 0); }
}

/* ---------- Cards / Formulare (Einstellungen, Admin) ---------- */
.card {
  padding: 22px 24px;
  border-radius: var(--radius-lg);
  background: var(--surface);
  border: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 14px;
  transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}
.card:hover {
  transform: translateY(-2px);
  border-color: rgba(91, 140, 255, 0.25);
  box-shadow: 0 14px 32px rgba(0, 0, 0, 0.35), 0 0 0 1px var(--accent-soft);
}

.card-head { display: flex; flex-direction: column; gap: 3px; }
.card-head h2 { margin: 0; font-size: 1.02rem; font-weight: 600; }
.card-head p { margin: 0; font-size: 0.82rem; color: var(--text-dim); }

.field { display: flex; flex-direction: column; gap: 6px; }
.field label {
  font-size: 0.74rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--text-faint);
  font-weight: 600;
}
.field input,
.field select,
.field textarea {
  padding: 10px 13px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--bg-soft);
  color: var(--text);
  font-family: var(--font);
  font-size: 0.92rem;
}
.field textarea { resize: vertical; min-height: 90px; line-height: 1.5; }
.field input:focus, .field select:focus, .field textarea:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }

.field-row { display: flex; gap: 12px; flex-wrap: wrap; }
.field-row .field { flex: 1 1 180px; }

.card-actions { display: flex; justify-content: flex-end; gap: 10px; }

/* Liste der hochgeladenen Startseiten-Musikstuecke (Adminbereich, Karte
   "Hintergrundmusik" -> Player-Abschnitt). */
.frontend-track-list { list-style: none; margin: 0 0 4px; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.frontend-track-item {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 10px 12px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md, 10px);
}
.frontend-track-name { font-weight: 600; min-width: 90px; }
.frontend-track-item audio { flex: 1 1 220px; min-width: 180px; height: 34px; }
.frontend-track-item form { margin: 0; }
/* Ausgeblendete Musikstuecke (siehe "enabled"/set_frontend_track_enabled())
   bleiben in dieser Verwaltungsliste sichtbar, aber optisch abgesetzt -
   sie tauchen nur im Player auf der Startseite nicht mehr auf. */
.frontend-track-item-disabled { opacity: 0.55; }

.frontend-track-reorder { display: flex; flex-direction: column; gap: 2px; }
.frontend-track-reorder .btn { padding: 2px 8px; line-height: 1.3; }
.frontend-track-rename-form { display: flex; align-items: center; gap: 6px; }
.frontend-track-rename-form .table-input { min-width: 130px; }

/* Anleitung (anleitung.php): vom Administrator gepflegter Hilfetext,
   siehe render_simple_richtext() in includes/functions.php. */
.instructions-text p { margin: 0 0 14px; line-height: 1.6; }
.instructions-text p:last-child { margin-bottom: 0; }
.instructions-text ul { margin: 0 0 14px; padding-left: 22px; line-height: 1.6; }
.instructions-text ul:last-child { margin-bottom: 0; }
.instructions-text li { margin-bottom: 6px; }

.link-card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 16px 18px;
  border-radius: var(--radius);
  background: var(--bg-soft);
  border: 1px solid var(--border);
  text-decoration: none;
  color: var(--text);
  transition: transform 0.18s ease, border-color 0.18s ease, background 0.18s ease, box-shadow 0.18s ease;
}
.link-card:hover {
  border-color: rgba(91, 140, 255, 0.3);
  background: var(--surface-hover);
  transform: translateY(-2px);
  box-shadow: 0 10px 26px rgba(0, 0, 0, 0.3), 0 0 0 1px var(--accent-soft);
}
.link-card .lc-text strong { display: block; font-size: 0.92rem; }
.link-card .lc-text span { font-size: 0.8rem; color: var(--text-dim); }
.link-card svg { width: 18px; height: 18px; color: var(--text-dim); }

/* ---------- Toggle-Switch (An/Aus-Einstellungen, z. B. Maskottchen) ---------- */
.switch-row { display: flex; align-items: center; gap: 14px; }
.switch-row + .switch-row { margin-top: 4px; }
.switch {
  position: relative;
  display: inline-flex;
  flex-shrink: 0;
  cursor: pointer;
}
.switch input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  margin: 0;
}
.switch-track {
  width: 44px;
  height: 24px;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  display: inline-block;
  position: relative;
  transition: background 0.2s ease, border-color 0.2s ease;
}
.switch-thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--text-dim);
  transition: transform 0.25s cubic-bezier(.34, 1.56, .64, 1), background 0.2s ease;
}
.switch input:checked + .switch-track {
  background: linear-gradient(135deg, var(--accent), #4f7aff);
  border-color: var(--accent);
}
.switch input:checked + .switch-track .switch-thumb {
  transform: translateX(20px);
  background: #fff;
}
.switch input:focus-visible + .switch-track {
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.switch input:disabled + .switch-track { opacity: 0.5; cursor: not-allowed; }
.switch-label { display: flex; flex-direction: column; gap: 2px; }
.switch-label strong { font-size: 0.88rem; font-weight: 600; }
.switch-label span { font-size: 0.78rem; color: var(--text-dim); }

/* ---------- Badges ---------- */
.badge {
  display: inline-flex;
  align-items: center;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}
.badge-admin { background: var(--accent-soft); color: var(--accent-text); box-shadow: 0 0 0 1px rgba(91, 140, 255, 0.25), 0 0 14px rgba(91, 140, 255, 0.3); }
.badge-editor { background: rgba(52, 209, 196, 0.14); color: #a6f2ea; box-shadow: 0 0 0 1px rgba(52, 209, 196, 0.22), 0 0 14px rgba(52, 209, 196, 0.22); }
.badge-viewer { background: rgba(255, 255, 255, 0.08); color: var(--text-dim); box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.06); }

/* ---------- Tabelle (Benutzerverwaltung) ---------- */
/* Die Tabelle "Bestehende Benutzer" ist breiter als der sichtbare Bereich
   (Spalteninhalte mit white-space: nowrap). Damit der Löschen-Button
   trotzdem IMMER sichtbar ist (Meldung: "Ich sehe den Löschen-Button gar
   nicht"), bleibt die letzte Spalte ("Aktion") beim horizontalen Scrollen
   fixiert (position: sticky) statt aus dem sichtbaren Bereich zu
   verschwinden. Zusaetzlich zeigt table-wrap einen dezenten
   Schatten am rechten Rand, sobald noch weiterer Inhalt zum Scrollen
   vorhanden ist, damit ueberhaupt erkennbar ist, dass die Tabelle breiter
   ist als der sichtbare Ausschnitt. */
.table-wrap {
  width: 100%;
  overflow-x: auto;
  position: relative;
  background:
    linear-gradient(to right, var(--surface) 0, var(--surface) 0) 0 0,
    linear-gradient(to left, var(--surface) 0, var(--surface) 0) 100% 0,
    linear-gradient(to right, rgba(0, 0, 0, 0.35), rgba(0, 0, 0, 0)) 0 0,
    linear-gradient(to left, rgba(0, 0, 0, 0.35), rgba(0, 0, 0, 0)) 100% 0;
  background-repeat: no-repeat;
  background-size: 24px 100%, 24px 100%, 14px 100%, 14px 100%;
  background-attachment: local, local, scroll, scroll;
}
.data-table { width: 100%; border-collapse: collapse; font-size: 0.86rem; }
.data-table th, .data-table td {
  text-align: left;
  padding: 12px 14px;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
.data-table th {
  color: var(--text-faint);
  text-transform: uppercase;
  font-size: 0.7rem;
  letter-spacing: 0.07em;
  font-weight: 600;
}
.data-table tr:last-child td { border-bottom: none; }
.data-table .user-cell { display: flex; align-items: center; gap: 10px; }
/* Tabelle "Bestehende Benutzer": die Spaltenueberschriften duerfen
   umbrechen (weniger erzwungene Mindestbreite pro Spalte als bei einer
   langen einzeiligen Ueberschrift), und die Datum/Zeit-Spalten duerfen
   zwischen Datum und Uhrzeit umbrechen. Das macht die Tabelle insgesamt
   schmaler, damit sie auf gaengigen Bildschirmbreiten (ab ca. 1280px)
   komplett ohne horizontales Scrollen passt und der Löschen-Button immer
   direkt sichtbar ist. */
.users-table th,
.users-table td {
  padding: 10px 8px;
}
.users-table th {
  white-space: normal;
  vertical-align: bottom;
  min-width: 68px;
}
.users-table td.col-date {
  white-space: normal;
  max-width: 108px;
}
.users-table th.col-actions,
.users-table td.col-actions {
  white-space: nowrap;
}
.data-table select {
  padding: 6px 10px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--bg-soft);
  color: var(--text);
  font-family: var(--font);
  font-size: 0.82rem;
}
.inline-form { display: flex; gap: 8px; align-items: center; }
.table-input {
  padding: 7px 10px;
  width: 130px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--bg-soft);
  color: var(--text);
  font-family: var(--font);
  font-size: 0.82rem;
}

/* ---------- Toast ---------- */
.toast {
  position: fixed;
  left: 50%;
  bottom: 28px;
  transform: translate(-50%, 16px) scale(0.9);
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  color: var(--text);
  padding: 11px 20px;
  border-radius: 999px;
  font-size: 0.85rem;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease, transform 0.4s cubic-bezier(.34, 1.56, .64, 1);
  box-shadow: var(--shadow), 0 0 0 1px var(--accent-soft);
  z-index: 50;
}
.toast.show { opacity: 1; transform: translate(-50%, 0) scale(1); }

/* ---------- Globale Ladeanzeige ----------
   Siehe assets/app.js (ganz oben im Haupt-IIFE): zwei Bausteine, die in
   JEDEM Bereich der App automatisch zeigen, dass gerade etwas passiert -
   ein duenner Fortschrittsbalken ganz oben fuer alle Hintergrund-Aktionen
   (Speichern, Umsortieren, Anpinnen, ...) und ein deutlicheres Overlay MIT
   echtem Prozentwert speziell fuer Datei-Uploads (Profilbild, Events-
   Fotos/Videos, Musikstuecke), die auf einer mobilen Verbindung durchaus
   laenger dauern koennen. */
.top-loading-bar {
  position: fixed;
  top: 0;
  left: 0;
  width: 0%;
  height: 3px;
  background: linear-gradient(90deg, var(--accent), var(--teal));
  box-shadow: 0 0 10px var(--accent), 0 0 3px var(--accent);
  z-index: 200;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
}
.top-loading-bar.active {
  opacity: 1;
  width: 78%;
  transition: width 6s cubic-bezier(0.15, 0.8, 0.3, 1), opacity 0.2s ease;
}
.top-loading-bar.done {
  width: 100%;
  opacity: 1;
  transition: width 0.25s ease, opacity 0.4s ease 0.15s;
}
@media (prefers-reduced-motion: reduce) {
  .top-loading-bar.active { width: 40%; transition: opacity 0.2s ease; }
}

.upload-overlay {
  position: fixed;
  inset: 0;
  background: rgba(6, 8, 14, 0.72);
  backdrop-filter: blur(3px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 300;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
  padding: 20px;
}
.upload-overlay.show { opacity: 1; pointer-events: auto; }
.upload-overlay-card {
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow), 0 0 0 1px var(--accent-soft);
  padding: 28px 32px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  min-width: 260px;
  max-width: 340px;
  text-align: center;
  transform: translateY(8px) scale(0.97);
  transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.upload-overlay.show .upload-overlay-card { transform: translateY(0) scale(1); }
.upload-spinner {
  width: 52px;
  height: 52px;
  border-radius: 50%;
  background: conic-gradient(var(--accent) calc(var(--upload-progress, 0) * 1%), var(--surface) 0);
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 5px), #000 calc(100% - 5px));
  mask: radial-gradient(farthest-side, transparent calc(100% - 5px), #000 calc(100% - 5px));
  transition: background 0.15s linear;
}
.upload-spinner.spin { animation: uploadSpin 1s linear infinite; }
@keyframes uploadSpin { to { transform: rotate(360deg); } }
.upload-overlay-percent {
  color: var(--accent-text);
  font-size: 1.4rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  min-height: 1.4rem;
}
.upload-overlay-text { color: var(--text); font-weight: 600; font-size: 0.95rem; }
.upload-overlay-hint { color: var(--text-faint); font-size: 0.78rem; }
@media (prefers-reduced-motion: reduce) {
  .upload-spinner.spin { animation: none; opacity: 0.85; }
}
@media (max-width: 640px) {
  .upload-overlay-card { padding: 22px 20px; min-width: 0; width: 100%; max-width: 300px; }
  .upload-spinner { width: 44px; height: 44px; }
}

/* ---------- Login ---------- */
.lock-screen {
  position: relative;
  z-index: 1;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 26px;
  padding: 24px;
}

/* ---------- Maskottchen unterhalb der Login-Karte ---------- */
.mascot-link {
  position: relative;
  z-index: 1;
  display: inline-flex;
  text-decoration: none;
  outline-offset: 6px;
  animation: mascotEnter 0.7s cubic-bezier(.16, 1, .3, 1) both;
  animation-delay: 0.25s;
}
.mascot-bob {
  display: inline-flex;
  animation: mascotBob 3.6s ease-in-out infinite;
  animation-delay: 0.9s;
}
.mascot-figure {
  width: 42px;
  height: auto;
  display: block;
  image-rendering: pixelated;
  image-rendering: -moz-crisp-edges;
  filter: drop-shadow(0 10px 16px rgba(0, 0, 0, 0.5));
  transition: transform 0.3s cubic-bezier(.34, 1.56, .64, 1), filter 0.3s ease;
  cursor: pointer;
}
.mascot-link:hover .mascot-figure {
  transform: translateY(-7px) scale(1.08) rotate(-2deg);
  filter: drop-shadow(0 16px 26px rgba(91, 140, 255, 0.5));
}
.mascot-link:active .mascot-figure {
  transform: translateY(-2px) scale(0.97);
}
@keyframes mascotEnter {
  from { opacity: 0; transform: translateY(16px); }
  to { opacity: 1; transform: translateY(63px); }
}
@keyframes mascotBob {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-6px); }
}

.login-box-wrap {
  position: relative;
  width: min(92%, 380px);
  margin: 0 auto;
  z-index: 1;
  animation: loginEnter 0.7s cubic-bezier(.16, 1, .3, 1) both;
}
@keyframes loginEnter {
  from { opacity: 0; transform: translateY(18px) scale(0.96); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

/* rotierender Farbverlauf-Ring hinter der Karte - erzeugt eine
   animierte "Glow-Border" */
.login-glow-ring {
  position: absolute;
  inset: -2px;
  border-radius: calc(var(--radius-lg) + 5px);
  background: conic-gradient(from 0deg, var(--accent), #8b6bff, var(--teal), var(--accent));
  filter: blur(1px);
  opacity: 0.9;
  animation: ringRotate 5s linear infinite;
  z-index: 0;
}
.login-glow-ring::after {
  content: "";
  position: absolute;
  inset: -14px;
  border-radius: inherit;
  background: inherit;
  filter: blur(22px);
  opacity: 0.55;
  z-index: -1;
}
@keyframes ringRotate { to { transform: rotate(360deg); } }

.login-box {
  position: relative;
  z-index: 1;
  margin: 0;
  width: 100%;
  padding: 36px 30px;
  border-radius: var(--radius-lg);
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: var(--shadow);
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}
.login-box-wrap:hover .login-box {
  transform: translateY(-3px);
  box-shadow: 0 16px 44px rgba(0, 0, 0, 0.5), 0 0 60px rgba(91, 140, 255, 0.1);
}

.login-box .brand-badge-lg {
  width: 54px; height: 54px; border-radius: 16px; font-size: 1.2rem; margin-bottom: 2px;
  box-shadow: 0 0 0 6px var(--accent-soft), 0 8px 20px rgba(91, 140, 255, 0.35);
  animation: badgeFloat 4s ease-in-out infinite;
}
@keyframes badgeFloat {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-4px); }
}
.login-box .login-title { margin: 0; font-size: 1.3rem; font-weight: 700; }
.login-sub { color: var(--text-dim); margin: -8px 0 4px; font-size: 0.88rem; }

.login-box form { width: 100%; display: flex; flex-direction: column; gap: 10px; }
.login-box input {
  padding: 11px 15px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--bg-soft);
  color: var(--text);
  font-size: 0.95rem;
  font-family: var(--font);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.login-box input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.login-box .btn { justify-content: center; margin-top: 4px; }
.login-box .btn-primary { animation: primaryIdlePulse 3.5s ease-in-out infinite; }
@keyframes primaryIdlePulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(91, 140, 255, 0); }
  50% { box-shadow: 0 0 0 6px rgba(91, 140, 255, 0.1); }
}

.flash {
  list-style: none;
  padding: 10px 14px;
  margin: 0;
  width: 100%;
  background: var(--danger-soft);
  border: 1px solid rgba(242, 73, 92, 0.3);
  color: #ffb4c2;
  border-radius: var(--radius-sm);
  font-size: 0.82rem;
  text-align: left;
}
.flash-ok { background: rgba(52, 211, 153, 0.1); border-color: rgba(52, 211, 153, 0.3); color: #a7f3d4; }

/* ---------- responsive ----------
   Die Maskottchen-Links (Ramona/Tekken) bleiben bei jeder Fensterbreite
   sichtbar und rutschen auf schmalen Fenstern per flex-wrap in eine
   eigene Zeile statt ausgeblendet zu werden. */
@media (max-width: 720px) {
  .topbar { padding: 12px 16px; }
  .mascot-topbar-link,
  .tekken-topbar-link,
  .slotracer-topbar-link { margin-left: 0; }
  .page { padding: 24px 16px 56px; gap: 20px; }
  .shift-panel { justify-content: center; text-align: center; }
  .toolbar { justify-content: center; }
  .toolbar-divider { display: none; }
  .field-row { flex-direction: column; }
  .flip-digit { width: 40px; height: 54px; perspective: 300px; }
  .flip-digit .digit-num { font-size: 1.8rem; }
  .flip-colon { font-size: 1.6rem; }
}

/* ---------- Handy-Modus ----------
   Auf schmalen Bildschirmen (Smartphones) wird die Anzeige-Seite auf das
   Wesentliche reduziert: nach dem Login sieht man ausschliesslich das
   eigene, hochgeladene Bild im Vollbild - kein Schichtcounter, keine
   Diashow, kein Snipping-Tool/Einfuegen/Vollbild-Button und keine sonstige
   Werkzeugleiste. Die Login-Seite selbst bleibt unveraendert (dort muss
   man sich ja erst noch anmelden). Ein kleiner, halbtransparenter
   Konto-Button bleibt oben rechts erhalten, damit man sich weiterhin
   abmelden bzw. die Einstellungen/den Adminbereich erreichen kann. */
@media (max-width: 767px) {
  body.has-app {
    overflow: hidden;
  }
  body.has-app .bg-glow,
  body.has-app .bg-grid,
  body.has-app .particles,
  body.has-app .fireworks-canvas {
    display: none;
  }

  body.has-app .page {
    padding: 0;
    margin: 0;
    max-width: none;
    gap: 0;
    min-height: 100dvh;
    animation: none;
  }

  body.has-app .shift-panel,
  body.has-app .toolbar,
  body.has-app .hint {
    display: none !important;
  }

  /* "Bild anzeigen von" bleibt im Handy-Modus normalerweise ebenfalls
     versteckt (siehe oben) - AUSSER fuer Administratoren/Bearbeiter
     (Body-Klasse "has-viewer-picker", siehe index.php): die sollen auch
     auf dem Handy gezielt ein anderes Bild aufrufen (bzw. als Bearbeiter
     zusaetzlich anpinnen) koennen. Da .image-stage hier fixed/inset:0 ist
     und damit sonst alles verdeckt, wird die Auswahl als kompaktes,
     halbtransparentes Steuerelement oben rechts unterhalb des
     Konto-Buttons ueber dem Bild eingeblendet. */
  body.has-app:not(.has-viewer-picker) .view-user-picker {
    display: none !important;
  }
  body.has-app.has-viewer-picker .view-user-picker {
    position: fixed;
    top: 56px;
    right: 10px;
    z-index: 21;
    max-width: 170px;
    margin: 0;
    gap: 4px;
  }
  body.has-app.has-viewer-picker .view-user-picker label {
    display: none;
  }
  body.has-app.has-viewer-picker .view-user-picker select {
    background: rgba(10, 12, 20, 0.6);
    backdrop-filter: blur(6px);
    border-color: rgba(255, 255, 255, 0.16);
    padding: 6px 8px;
    font-size: 0.78rem;
  }
  /* Der Anpinnen-Hinweistext (siehe .view-user-pin-row weiter unten) waere
     in diesem kompakten, schwebenden Kasten zu lang - dort bleibt nur der
     Button sichtbar, der Text ist auf dem PC (breiterer Platz) gedacht. */
  body.has-app.has-viewer-picker .view-user-pin-row .page-subtitle {
    display: none;
  }
  body.has-app.has-viewer-picker .view-user-pin-row {
    margin-top: 4px;
  }

  body.has-app .topbar {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 20;
    background: transparent;
    border: none;
    box-shadow: none;
    padding: 10px 12px;
    pointer-events: none;
  }
  body.has-app .topbar-left,
  body.has-app .topbar-clock,
  body.has-app #radio-player,
  body.has-app #online-radio-audio {
    display: none;
  }
  body.has-app .topbar-right {
    margin-left: auto;
    pointer-events: auto;
  }
  body.has-app .user-menu-btn {
    background: rgba(10, 12, 20, 0.55);
    backdrop-filter: blur(6px);
    padding: 6px;
  }
  body.has-app .user-menu-btn span:not(.avatar),
  body.has-app .user-menu-btn .chev {
    display: none;
  }

  body.has-app .image-stage {
    position: fixed;
    inset: 0;
    width: 100vw;
    height: 100dvh;
    border-radius: 0;
    border: none;
    box-shadow: none;
    z-index: 1;
  }
  body.has-app .image-stage img { background: #000; }
  body.has-app .image-timestamp,
  body.has-app .fullscreen-exit-btn { display: none !important; }

  /* Wunsch: "im handy-modus soll bei event und in der galerie kein radio
     player oder mp3-player sein" - anders als auf der Startseite (dort per
     body.has-app oben) gilt das hier unabhaengig vom App-Modus, einfach ab
     dieser Handy-Breite (siehe events.php/event-gallery.php, body-Klasse
     "hide-player-mobile"). Nur die Anzeige wird ausgeblendet, nicht die
     Wiedergabe selbst - laeuft bereits Musik/Radio, spielt sie im
     Hintergrund einfach weiter, nur ohne sichtbare/bedienbare Kontrollen. */
  body.hide-player-mobile #radio-player,
  body.hide-player-mobile #online-radio-audio,
  body.hide-player-mobile #frontend-player,
  body.hide-player-mobile #music-mute-btn {
    display: none;
  }

  /* Bugfix (Galerie, "Fotos/Videos hochladen"): im Handy-Modus wird
     .field-row weiter oben (siehe @media max-width:720px) auf
     "flex-direction: column" umgestellt - das per Inline-Style gesetzte
     "align-items:flex-end" auf diesem Formular (fuer die Desktop-Ansicht
     gedacht, wo Datei-Feld und Button nebeneinander UNTEN buendig stehen
     sollen) wirkt in der gestapelten Handy-Ansicht dann als HORIZONTALE
     Ausrichtung nach RECHTS - dadurch stand der "Hochladen"-Button am
     rechten Rand und war teilweise abgeschnitten, statt wie der Rest der
     Karte die volle Breite zu nutzen. Wunsch: "der Button 'Hochladen' [...]
     mittig unter 'Dateien auswählen' setzen [...] der Block [...] ist
     vertikal viel zu gross". Ab dieser Breite deshalb: Formular auf volle
     Breite stellen und den Button-Wrapper mittig zentrieren; der
     ohnehin recht grosszuegige Abstand zwischen Datei-Feld und Button wird
     zusaetzlich etwas verringert, damit die Karte insgesamt kompakter wirkt. */
  .event-upload-form {
    align-items: stretch !important;
    gap: 10px;
  }
  /* WICHTIG: die inline gesetzte "flex:1 1 260px" auf dem Datei-Feld (siehe
     event-gallery.php) meint "260px" als BREITEN-Basiswert fuer die
     Desktop-Ansicht (Feld+Button nebeneinander) - im Handy-Modus wird
     .field-row aber per @media (max-width:720px) auf
     "flex-direction: column" umgestellt, wodurch derselbe Basiswert
     stattdessen als HOEHEN-Basiswert interpretiert wird: das Feld bekam
     dadurch eine erzwungene Mindesthoehe von 260px (weit mehr als Label +
     Datei-Auswahl brauchen) - genau das war die Ursache fuer den viel zu
     hohen Block. "!important" ist noetig, um die Inline-Angabe (hoehere
     Spezifitaet) hier gezielt zu ueberschreiben. */
  .event-upload-form .field {
    flex: 0 1 auto !important;
  }
  .event-upload-actions {
    justify-content: center;
  }

  /* Wunsch: "handyversion: dort braucht man die leistungsmessung nicht, da
     der leistungsfresser der effekte in der handyversion nicht vorhanden
     ist" - der Leistungsmodus-Schalter (#performance-mode-btn) schaltet
     ausschliesslich Feuerwerk/Hintergrund-Partikel-Aurora-Glow und den
     Diashow-Zoom-Effekt um (siehe wirePerformanceMode() in assets/app.js).
     Auf der Startseite sind genau diese drei Effekte im Handy-Modus bereits
     per body.has-app oben abgeschaltet; body.hide-effects-mobile uebertraegt
     dieselbe Abschaltung jetzt auch auf alle anderen Seiten mit diesem
     Schalter (Event/Galerie/Schichtplan/Einstellungen/Anleitung/Admin-
     Bereich, siehe deren body-Klasse). Der Schalter selbst wird deshalb ab
     dieser Breite auf JEDER Seite ausgeblendet - er erscheint ohnehin nur,
     wenn man bereits angemeldet ist (nie auf dem unveraendert bleibenden
     Login-Bildschirm), es gibt hier also nichts mehr umzuschalten. */
  .performance-mode-btn {
    display: none;
  }
  body.hide-effects-mobile .bg-glow,
  body.hide-effects-mobile .bg-grid,
  body.hide-effects-mobile .particles,
  body.hide-effects-mobile .fireworks-canvas {
    display: none;
  }
}

/* ---------- Schichthinweis (Infofenster) ---------- */
.modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(6, 8, 14, 0.6);
  backdrop-filter: blur(3px);
}
.modal-overlay[hidden] { display: none; }
.modal-box {
  width: 100%;
  max-width: 380px;
  padding: 24px;
  border-radius: var(--radius-lg);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
  text-align: center;
}
.modal-box h2 {
  margin: 0 0 12px;
  font-size: 1.15rem;
  font-weight: 700;
}
.modal-box p {
  margin: 0 0 18px;
  color: var(--text-dim);
  line-height: 1.5;
}
.modal-box p strong { color: var(--text); }
.modal-box .card-actions { justify-content: center; }
.modal-box .shift-info-extra {
  margin: -8px 0 18px;
  font-size: 0.82rem;
  font-weight: 600;
  line-height: 1.4;
}
.modal-box .shift-info-extra-ok { color: var(--success); }

/* Rollen-Verwaltung (Adminbereich, Karte "Rollen verwalten") - je Rolle ein
   umrandeter Block mit Checkbox-Raster fuer die einzelnen Kacheln, die
   dieser Rolle zugewiesen werden koennen (siehe ASSIGNABLE_PERMISSIONS in
   includes/auth.php). */
.checkbox-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 6px 16px;
}
.checkbox-row {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.88rem;
  color: var(--text-dim);
  cursor: pointer;
}
.checkbox-row input[type="checkbox"],
.checkbox-row input[type="radio"] {
  width: 16px;
  height: 16px;
  accent-color: var(--accent);
  cursor: pointer;
  flex-shrink: 0;
}
.role-editor + .role-editor { margin-top: 0; }
/* Freigabe-Auswahl (event-gallery.php, Karte "Freigabe") - drei Radio-Optionen
   (privat / nur ansehen / ansehen+hochladen) untereinander, gleiche Optik
   wie .checkbox-row. */
.share-mode-options {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 16px;
}
.modal-box .shift-info-extra-warn { color: var(--danger); }

/* ---------- Chef-Taste (siehe assets/app.js, wireBossKey()) ----------
   Deckt beim Aktivieren wirklich ALLES ab (hoeherer z-index als
   .modal-overlay/#toast), damit auch ein gerade offenes Schichthinweis-
   Fenster o.ae. zuverlaessig verdeckt wird. Das Bild selbst wird per
   object-fit:contain unverzerrt und vollstaendig sichtbar dargestellt -
   unabhaengig vom Seitenverhaeltnis des hochgeladenen bzw. Standardbilds. */
.boss-key-overlay {
  position: fixed;
  inset: 0;
  z-index: 500;
  background: #000;
  display: flex;
  align-items: center;
  justify-content: center;
}
.boss-key-overlay[hidden] { display: none; }
.boss-key-overlay img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* ---------- Events (events.php) - Raster der eigenen Event-Buttons ---------- */
.event-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 14px;
  margin-bottom: 6px;
}
.event-tile {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 8px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 12px;
  background: var(--surface-2);
}
.event-tile-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 18px 10px;
  border-radius: var(--radius-sm);
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--text);
  text-decoration: none;
  text-align: center;
  transition: transform 0.2s ease, border-color 0.2s ease, background 0.2s ease;
}
.event-tile-btn:hover { transform: translateY(-2px); border-color: var(--border-strong); background: var(--surface-hover); }
.event-tile-btn svg { width: 26px; height: 26px; color: var(--accent); }
/* Kleines Vorschaubild des ZUERST hochgeladenen Fotos/Videos eines Events
   (siehe event_tile_thumb_url() in includes/functions.php) - ersetzt das
   generische Platzhalter-Icon oben, sobald das Event mindestens eine Datei
   enthaelt. */
.event-tile-thumb {
  width: 64px;
  height: 64px;
  border-radius: var(--radius-sm);
  object-fit: cover;
  background: #000;
}
/* Videos bekommen einen farbigen Rahmen, damit sie sich auf den ersten
   Blick von Foto-Vorschaubildern unterscheiden lassen. */
.event-tile-thumb-video {
  border: 3px solid var(--video-accent);
}
/* Umhuellt das Vorschaubild/-video einer Kachel nur, damit
   .event-tile-play-icon (s.u.) relativ dazu zentriert werden kann statt
   relativ zur ganzen Kachel (.event-tile ist ebenfalls position:relative,
   aber deutlich groesser als das 64x64px-Vorschaubild). */
.event-tile-thumb-wrap {
  position: relative;
  display: inline-flex;
  width: 64px;
  height: 64px;
}
/* Bleibt auch dann sichtbar, wenn fuer ein Video bereits ein
   eingefangenes Vorschaubild vorliegt (siehe
   event_media_needs_video_fallback() in includes/functions.php) und die
   Kachel daher ein normales <img> statt eines <video>-Elements zeigt -
   ohne dieses Icon waere ein Video optisch nicht mehr von einem Foto zu
   unterscheiden. */
.event-tile-play-icon {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: rgba(10, 12, 20, 0.55);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.65rem;
  padding-left: 2px;
  pointer-events: none;
}
.event-tile-label { font-weight: 600; font-size: 0.95rem; word-break: break-word; }
.event-tile-count { font-size: 0.76rem; color: var(--text-faint); }
.event-tile-badge {
  display: inline-block;
  margin-top: 2px;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 600;
}
.event-tile-badge-open { background: var(--accent-soft); color: var(--accent-text); }
.event-tile-badge-view { background: var(--surface-2); color: var(--text-dim); border: 1px solid var(--border); }
/* Reihenfolge-Pfeile der eigenen Events ("Meine Events") - laufen per AJAX
   gegen move-event.php (siehe assets/app.js, wireEventReorder()), damit
   die Seite dabei nicht neu laedt und nicht an den Anfang springt. */
.event-tile-reorder { display: flex; gap: 6px; }
.event-move-btn {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  font-size: 0.85rem;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}
.event-move-btn:hover:not(:disabled) { background: var(--accent); border-color: var(--accent); color: #fff; }
.event-move-btn:disabled { opacity: 0.35; cursor: default; }
.event-tile-actions { display: flex; flex-direction: column; gap: 8px; }
.event-rename-form { display: flex; gap: 6px; }
.event-rename-form .table-input { flex: 1; }

/* ---------- "NEU"-Markierung fuer Event-Galerien ----------
   Admin/Bearbeiter koennen eine Galerie in "Meine Events" manuell als NEU
   markieren (Button .event-new-toggle-btn) - sichtbar dann als kleines
   oranges Schild sowohl an der eigenen Kachel als auch fuer Betrachter an
   der jeweils freigegebenen Kachel (siehe events.php) sowie als
   Zusatzmarkierung am grossen "EVENT"-Button auf der Startseite (siehe
   .event-launch-new-badge unten). */
.new-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 2px 9px;
  border-radius: 999px;
  background: var(--new-badge);
  color: var(--new-badge-text);
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
  box-shadow: 0 2px 8px rgba(245, 158, 11, 0.45);
}
.event-tile-new-badge {
  position: absolute;
  top: -8px;
  right: 10px;
  z-index: 2;
}
/* Umschalt-Button fuer Admin/Bearbeiter, direkt neben den
   Reihenfolge-Pfeilen in "Meine Events" - grau/neutral im inaktiven
   Zustand, wechselt beim Aktivieren auf dieselbe Orange-Farbe wie das
   Schild selbst. */
.event-new-toggle-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 10px;
  height: 26px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-dim);
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.03em;
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease, box-shadow 0.2s ease;
}
.event-new-toggle-btn:hover { border-color: var(--border-strong); color: var(--text); }
.event-new-toggle-btn.active {
  background: var(--new-badge);
  border-color: var(--new-badge);
  color: var(--new-badge-text);
  box-shadow: 0 2px 8px rgba(245, 158, 11, 0.4);
}
.event-new-toggle-btn.active:hover { filter: brightness(1.08); }
/* Zusatzmarkierung am grossen "EVENT"-Button auf der Startseite (siehe
   index.php) - .event-launch-wrap haelt Button + Schild zusammen, damit
   sich am umgebenden Flex-Layout (.shift-panel) nichts aendert. */
.event-launch-wrap { position: relative; display: inline-flex; }
.event-launch-new-badge {
  position: absolute;
  top: -10px;
  right: -10px;
  z-index: 2;
  animation: event-launch-new-pulse 2.2s ease-in-out infinite;
}
@keyframes event-launch-new-pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.12); }
}
@media (prefers-reduced-motion: reduce) {
  .event-launch-new-badge { animation: none; }
}

/* ---------- Event-Galerie (event-gallery.php) - Vorschauraster ---------- */
.gallery-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 12px;
}
.gallery-thumb {
  position: relative;
  aspect-ratio: 1 / 1;
  border-radius: var(--radius-sm);
  overflow: hidden;
  border: 1px solid var(--border);
  background: var(--surface-2);
}
.gallery-thumb-btn {
  display: block;
  width: 100%;
  height: 100%;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
}
.gallery-thumb-btn img,
.gallery-thumb-btn video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 0.2s ease;
  pointer-events: none;
}
.gallery-thumb-btn:hover img,
.gallery-thumb-btn:hover video { transform: scale(1.05); }
.gallery-thumb-play-icon {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: rgba(10, 12, 20, 0.55);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.95rem;
  padding-left: 3px;
  pointer-events: none;
}
/* Videos im Galerie-Raster bekommen denselben farbigen Rahmen wie die
   Event-Kachel-Vorschaubilder (siehe .event-tile-thumb-video oben). */
.gallery-thumb-video {
  border: 3px solid var(--video-accent);
}
/* Ausgeblendete Dateien (siehe hidden/set_event_image_hidden()) bleiben nur
   in DIESER Verwaltungsansicht (canManageThis) sichtbar, deutlich
   schwaecher dargestellt plus kleinem Hinweis-Badge - ein geteilter
   Betrachter bekommt sie ueberhaupt nicht erst geliefert (siehe
   get_event_images($eventId, false) in event-gallery.php). */
.gallery-thumb-hidden { opacity: 0.45; }
.gallery-thumb-hidden-badge {
  position: absolute;
  left: 6px;
  bottom: 6px;
  z-index: 1;
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(10, 12, 20, 0.7);
  color: #fff;
  font-size: 0.68rem;
  font-weight: 600;
  backdrop-filter: blur(2px);
}
.gallery-thumb-reorder {
  position: absolute;
  /* 36px statt 6px: laesst oben Platz fuer die Auswahl-Checkbox
     (.gallery-thumb-select, ebenfalls oben links), damit sich beide
     Kontrollelemente nicht gegenseitig ueberlappen/verdecken. */
  top: 36px;
  left: 6px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.gallery-thumb-move-btn {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: none;
  background: rgba(10, 12, 20, 0.65);
  color: #fff;
  font-size: 0.85rem;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  backdrop-filter: blur(2px);
  transition: background 0.2s ease;
}
.gallery-thumb-move-btn:hover:not(:disabled) { background: var(--accent); }
.gallery-thumb-move-btn:disabled { opacity: 0.35; cursor: default; }
.gallery-thumb-top-actions {
  position: absolute;
  top: 6px;
  right: 6px;
  display: flex;
  align-items: center;
  gap: 6px;
}
.gallery-thumb-top-actions form { margin: 0; }
.gallery-thumb-hide-btn {
  height: 26px;
  padding: 0 10px;
  border-radius: 999px;
  border: none;
  background: rgba(10, 12, 20, 0.65);
  color: #fff;
  font-size: 0.7rem;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
  white-space: nowrap;
  backdrop-filter: blur(2px);
  transition: background 0.2s ease;
}
.gallery-thumb-hide-btn:hover:not(:disabled) { background: var(--accent); }
.gallery-thumb-hide-btn:disabled { opacity: 0.5; cursor: default; }
.gallery-thumb-delete-btn {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: none;
  background: rgba(10, 12, 20, 0.65);
  color: #fff;
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  backdrop-filter: blur(2px);
  transition: background 0.2s ease;
}
.gallery-thumb-delete-btn:hover { background: var(--danger); }
.gallery-thumb-download-btn {
  /* Eigene, freistehende Ecke unten rechts statt eines dritten Buttons in
     der ohnehin schon vollen oberen Aktionsleiste: dort (neben
     "Ausblenden"/"Loeschen") ragte er bei schmalen Kachelbreiten ueber den
     Rand hinaus bzw. wurde bei sehr schmalen Kacheln so weit nach links
     gedrueckt, dass er die Auswahl-Checkbox oben links ueberlagerte -
     kaum bis gar nicht zu erkennen bzw. mit ihr verwechselbar. Unten
     rechts ist die Ecke frei (unten links steht ggf. nur der
     "Ausgeblendet"-Hinweis), daher hier eine eigene, unabhaengige
     Position ohne jede Ueberschneidung mit Checkbox/Pfeilen/Ausblenden/
     Loeschen. */
  position: absolute;
  bottom: 6px;
  right: 6px;
  z-index: 1;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: none;
  background: rgba(10, 12, 20, 0.65);
  color: #fff;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  backdrop-filter: blur(2px);
  transition: background 0.2s ease;
  text-decoration: none;
  flex-shrink: 0;
}
.gallery-thumb-download-btn svg { width: 15px; height: 15px; }
.gallery-thumb-download-btn:hover { background: var(--accent); }

/* ---------- Event-Galerie - Mehrfachauswahl (Verschieben in andere Galerie) ---------- */
.gallery-thumb-select {
  position: absolute;
  top: 6px;
  left: 6px;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: rgba(10, 12, 20, 0.65);
  backdrop-filter: blur(2px);
  cursor: pointer;
}
.gallery-thumb-select input[type="checkbox"] {
  margin: 0;
  width: 16px;
  height: 16px;
  accent-color: var(--accent);
  cursor: pointer;
}
/* ---------- Event-Galerie - Diashow im Vollbild ---------- */
.gallery-slideshow-bar {
  display: flex;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: 12px;
  padding: 10px 14px;
  margin-bottom: 14px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--bg-soft);
}
.gallery-slideshow-field { flex: 0 0 auto; }
.gallery-slideshow-field input {
  width: 90px;
}

.gallery-bulk-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  padding: 10px 14px;
  margin-bottom: 14px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--bg-soft);
}
.gallery-bulk-select-all { margin: 0; }
.gallery-bulk-count { font-size: 0.85rem; color: var(--text-dim); }
.gallery-bulk-hint { font-size: 0.85rem; color: var(--text-dim); }
.gallery-bulk-bar .table-input { width: auto; min-width: 170px; }

/* ---------- Event-Galerie - Vollbild-Lightbox ---------- */
body.lightbox-open { overflow: hidden; }
.lightbox-overlay {
  position: fixed;
  inset: 0;
  background: rgba(6, 8, 14, 0.94);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  padding: 24px;
}
.lightbox-overlay[hidden] { display: none; }
/* Braucht eine FESTE (nicht "auto") Breite/Hoehe, damit die
   "max-width/max-height: 100%" der Bild-/Video-Elemente unten ueberhaupt
   etwas zum Prozent-Rechnen haben: ohne das (frueher stand hier nur
   "max-width/max-height: 100%") bleibt die Groesse dieses Containers vom
   Inhalt bestimmt ("auto"), wodurch Prozent-Angaben auf Kindelementen
   laut CSS-Spezifikation wirkungslos bleiben ("none") - grosse Fotos
   wurden dadurch in voller Originalgroesse angezeigt statt an den
   verfuegbaren Platz angepasst zu werden. */
.lightbox-media {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
}
.lightbox-image,
.lightbox-video {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  border-radius: var(--radius-sm);
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
  user-select: none;
}
/* ---------- Vollbild-Lightbox: Lade-Indikator (event-gallery.php) ----------
   Zeigt beim Oeffnen/Wechseln (Pfeile, Wischen, Tastatur) eine drehende
   Ladeanzeige in der Mitte, solange das volle Foto/Video noch geladen wird
   (siehe die "show()"-Funktion in assets/app.js - vorher wurde die
   Vorschau beim Wechsel sofort komplett entfernt und blieb bis zum Ende
   des Ladevorgangs einfach leer/schwarz, was besonders bei grossen
   Videos/langsamer Verbindung wie ein haengengebliebenes Programm wirkte).
   Als eigenes, immer vorhandenes Element (statt Teil von .lightbox-media)
   umgesetzt, damit es unabhaengig vom kompletten Leeren/Neubefuellen von
   .lightbox-media beim Medienwechsel bestehen bleibt. */
.lightbox-spinner {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 52px;
  height: 52px;
  border-radius: 50%;
  border: 4px solid rgba(255, 255, 255, 0.18);
  border-top-color: var(--accent);
  pointer-events: none;
  z-index: 2;
  animation: lightboxSpinnerRotate 0.85s linear infinite;
}
.lightbox-spinner[hidden] { display: none; }
@keyframes lightboxSpinnerRotate {
  to { transform: translate(-50%, -50%) rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
  .lightbox-spinner { animation: none; opacity: 0.75; }
}
.lightbox-close-btn {
  position: absolute;
  top: 18px;
  right: 18px;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  background: rgba(255, 255, 255, 0.06);
  color: var(--text);
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.lightbox-close-btn:hover { background: rgba(255, 255, 255, 0.14); }
.lightbox-nav-btn {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 48px;
  height: 48px;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  background: rgba(255, 255, 255, 0.06);
  color: var(--text);
  font-size: 1.1rem;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.lightbox-nav-btn:hover { background: rgba(255, 255, 255, 0.14); }
.lightbox-prev-btn { left: 18px; }
.lightbox-next-btn { right: 18px; }
.lightbox-counter {
  position: absolute;
  bottom: 18px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 0.85rem;
  color: var(--text-dim);
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  padding: 5px 14px;
}
@media (max-width: 640px) {
  .lightbox-nav-btn { width: 40px; height: 40px; }
  .lightbox-prev-btn { left: 8px; }
  .lightbox-next-btn { right: 8px; }
  .lightbox-close-btn { top: 10px; right: 10px; }
}

/* Status-Badge "Diashow laeuft" - erscheint oben mittig, solange die
   Diashow im Vollbild aktiv ist (Wunsch: "diese als diashow im vollbild
   anzeigen zu lassen"). */
.lightbox-slideshow-indicator {
  position: absolute;
  top: 18px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--success);
  background: rgba(52, 211, 153, 0.12);
  border: 1px solid rgba(52, 211, 153, 0.4);
  border-radius: 999px;
  padding: 5px 16px;
  white-space: nowrap;
  z-index: 5;
}
@media (max-width: 640px) {
  .lightbox-slideshow-indicator {
    top: 10px;
    font-size: 0.72rem;
    padding: 4px 12px;
    max-width: calc(100% - 100px);
    overflow: hidden;
    text-overflow: ellipsis;
  }
}

/* =====================================================================
   Einheitliche 3D-Optik + Glow-Effekt fuer ALLE Buttons (Wunsch: "alle
   buttons sollen einen 3D optik mit glow effekt haben"). Vorbild ist der
   grosse "EVENT"-Button (.event-launch-btn, bereits mit 3D/Glow), der
   aber staendig pulsiert - das auf JEDEN kleinen Button (Loeschen,
   Ausblenden, Pfeile, Menues, Player-Steuerung usw.) zu uebertragen,
   waere bei vielen gleichzeitig sichtbaren Buttons (z. B. eine Galerie
   mit vielen Kacheln) unruhig/ueberladen. Deshalb bewusst die dezente
   Variante: jeder Button bekommt dauerhaft eine leichte 3D-Kante (helle
   obere Bevel-Kante + dunkle "Stufe" darunter, wie ein leicht erhabener
   Knopf) plus ein ruhiges, konstantes Glimmen in Akzentfarbe - beides
   wird beim Ueberfahren/Klicken kurz staerker, damit weiterhin klar
   spuerbares Feedback entsteht, ohne dass die ganze Oberflaeche staendig
   pulsiert. .event-launch-btn und .music-enable-btn behalten ihr
   bestehendes, bewusst auffaelligeres Sonderdesign (grosse
   Haupt-Handlungsflaechen) unveraendert.
   Technisch nur zusaetzliche box-shadow/transform-Deklarationen (keine
   Aenderung an Groesse/Padding/Position) - daher unkritisch fuer die
   bestehenden Layout-Tests (Breite/Hoehe/Position der Buttons bleiben
   exakt gleich, box-shadow beeinflusst nicht den Kasten selbst). */
:root {
  --btn3d-bevel-light: rgba(255, 255, 255, 0.16);
  --btn3d-bevel-dark: rgba(0, 0, 0, 0.32);
  --btn3d-edge: rgba(0, 0, 0, 0.4);
  --btn3d-glow: rgba(91, 140, 255, 0.25);
  --btn3d-glow-strong: rgba(91, 140, 255, 0.6);
  --btn3d-glow-danger: rgba(242, 73, 92, 0.28);
  --btn3d-glow-danger-strong: rgba(242, 73, 92, 0.62);
}

/* ---- Familie A: groessere Pillen-/Kachel-Buttons ---- */
.btn,
.user-menu-btn,
.event-tile-btn,
.event-new-toggle-btn {
  box-shadow:
    inset 0 1px 0 var(--btn3d-bevel-light),
    inset 0 -2px 0 var(--btn3d-bevel-dark),
    0 2px 0 var(--btn3d-edge),
    0 0 10px var(--btn3d-glow);
}
.btn:hover,
.user-menu-btn:hover,
.event-tile-btn:hover,
.event-new-toggle-btn:hover {
  box-shadow:
    inset 0 1px 0 var(--btn3d-bevel-light),
    inset 0 -2px 0 var(--btn3d-bevel-dark),
    0 3px 0 var(--btn3d-edge),
    0 0 16px 2px var(--btn3d-glow-strong);
}
.btn:active {
  box-shadow:
    inset 0 1px 0 var(--btn3d-bevel-light),
    inset 0 -1px 0 var(--btn3d-bevel-dark),
    0 1px 0 var(--btn3d-edge),
    0 0 8px var(--btn3d-glow);
}
.btn:disabled { box-shadow: none; }
.btn-primary {
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.3),
    inset 0 -2px 0 rgba(0, 0, 0, 0.22),
    0 2px 0 #3f63cc,
    0 0 12px var(--btn3d-glow);
}
.btn-primary:hover {
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.35),
    inset 0 -2px 0 rgba(0, 0, 0, 0.22),
    0 3px 0 #3f63cc,
    0 0 18px 2px var(--btn3d-glow-strong);
}
.btn-danger {
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.08),
    inset 0 -2px 0 rgba(0, 0, 0, 0.3),
    0 2px 0 rgba(242, 73, 92, 0.35),
    0 0 10px var(--btn3d-glow-danger);
}
.btn-danger:hover {
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.1),
    inset 0 -2px 0 rgba(0, 0, 0, 0.3),
    0 3px 0 rgba(242, 73, 92, 0.5),
    0 0 16px 2px var(--btn3d-glow-danger-strong);
}
.event-new-toggle-btn.active {
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.3),
    inset 0 -2px 0 rgba(0, 0, 0, 0.25),
    0 2px 0 #b8790b,
    0 0 12px rgba(245, 158, 11, 0.45);
}

/* ---- Familie B: mittelgrosse runde Icon-Buttons (mehr Abstand zu
        Nachbarelementen, daher zusaetzlich leichtes Anheben/Skalieren
        beim Hover moeglich, ohne Ueberlappungsrisiko) ---- */
.icon-btn,
.fullscreen-exit-btn,
.lightbox-close-btn,
.lightbox-nav-btn {
  box-shadow:
    inset 0 1px 0 var(--btn3d-bevel-light),
    inset 0 -2px 0 var(--btn3d-bevel-dark),
    0 2px 0 var(--btn3d-edge),
    0 0 8px var(--btn3d-glow);
}
.icon-btn:hover,
.fullscreen-exit-btn:hover,
.lightbox-close-btn:hover,
.lightbox-nav-btn:hover {
  box-shadow:
    inset 0 1px 0 var(--btn3d-bevel-light),
    inset 0 -2px 0 var(--btn3d-bevel-dark),
    0 3px 0 var(--btn3d-edge),
    0 0 14px 2px var(--btn3d-glow-strong);
}
.icon-btn:active { transform: translateY(1px); }
/* .lightbox-nav-btn zentriert sich permanent per
   "transform: translateY(-50%)" - beim Hover/Klick wird hier bewusst nur
   zusaetzlich skaliert statt das transform zu ersetzen, damit die
   vertikale Zentrierung erhalten bleibt. */
.lightbox-nav-btn:hover { transform: translateY(-50%) scale(1.06); }
.lightbox-nav-btn:active { transform: translateY(-50%) scale(0.96); }
.lightbox-close-btn:hover { transform: scale(1.06); }
.lightbox-close-btn:active { transform: scale(0.96); }
.fullscreen-exit-btn:hover { transform: scale(1.06); }

/* ---- Familie C: kleine, dicht gepackte Kreis-Buttons (Galerie-Kacheln,
        Event-Reihenfolge-Pfeile, Player) - bewusst OHNE hover-Transform
        (translate/scale), damit bei den eng benachbarten Kontrollelementen
        (siehe test53: Checkbox/Pfeile/Download-Button in derselben Ecke
        einer schmalen Kachel) kein zusaetzliches Ueberlappungsrisiko durch
        Bewegung entsteht - hier wirkt nur das verstaerkte Glimmen. ---- */
.event-move-btn,
.gallery-thumb-move-btn,
.gallery-thumb-download-btn,
.frontend-player-btn {
  box-shadow:
    inset 0 1px 0 var(--btn3d-bevel-light),
    inset 0 -1px 0 var(--btn3d-bevel-dark),
    0 1px 0 var(--btn3d-edge),
    0 0 6px var(--btn3d-glow);
}
.event-move-btn:hover:not(:disabled),
.gallery-thumb-move-btn:hover:not(:disabled),
.gallery-thumb-download-btn:hover,
.frontend-player-btn:hover {
  box-shadow:
    inset 0 1px 0 var(--btn3d-bevel-light),
    inset 0 -1px 0 var(--btn3d-bevel-dark),
    0 2px 0 var(--btn3d-edge),
    0 0 10px 1px var(--btn3d-glow-strong);
}
.event-move-btn:disabled,
.gallery-thumb-move-btn:disabled { box-shadow: none; }
/* Loeschen-Buttons: Glimmen in Warn-/Gefahrenfarbe statt Akzentblau, damit
   die Signalwirkung ("das entfernt etwas") erhalten bleibt. */
.gallery-thumb-delete-btn {
  box-shadow:
    inset 0 1px 0 var(--btn3d-bevel-light),
    inset 0 -1px 0 var(--btn3d-bevel-dark),
    0 1px 0 var(--btn3d-edge),
    0 0 6px var(--btn3d-glow-danger);
}
.gallery-thumb-delete-btn:hover {
  box-shadow:
    inset 0 1px 0 var(--btn3d-bevel-light),
    inset 0 -1px 0 var(--btn3d-bevel-dark),
    0 2px 0 var(--btn3d-edge),
    0 0 10px 1px var(--btn3d-glow-danger-strong);
}

/* ---- Familie D: kleine Pillen-Buttons (Ausblenden-Button in der
        Galerie) ---- */
.gallery-thumb-hide-btn {
  box-shadow:
    inset 0 1px 0 var(--btn3d-bevel-light),
    inset 0 -1px 0 var(--btn3d-bevel-dark),
    0 1px 0 var(--btn3d-edge),
    0 0 6px var(--btn3d-glow);
}
.gallery-thumb-hide-btn:hover:not(:disabled) {
  box-shadow:
    inset 0 1px 0 var(--btn3d-bevel-light),
    inset 0 -1px 0 var(--btn3d-bevel-dark),
    0 2px 0 var(--btn3d-edge),
    0 0 10px 1px var(--btn3d-glow-strong);
}
.gallery-thumb-hide-btn:disabled { box-shadow: none; }

@media (prefers-reduced-motion: reduce) {
  .icon-btn:active,
  .lightbox-nav-btn:hover,
  .lightbox-nav-btn:active,
  .lightbox-close-btn:hover,
  .lightbox-close-btn:active,
  .fullscreen-exit-btn:hover { transform: none; }
}

/* ---- Mehrfachauswahl "Zustaendiger Bearbeiter" (admin/users.php): 2-3
        Bearbeiter koennen gleichzeitig einem Betrachter zugeordnet werden,
        daher Checkbox-Liste statt Einzelauswahl-Select. ---- */
.editor-checkbox-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-bottom: 6px;
}
.editor-checkbox-item {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 0.9em;
  cursor: pointer;
}

/* ============ Hilfe-Chatfenster (Startseite, unten rechts) ============
   Wunsch: "Ich brauche unten rechts unter notizen2 ein kleines
   chatfenster." Bewusst position:fixed statt an die (nur ab 1500px und
   nur bei aktivierter Notiz 2 sichtbare) Notizzettel-Position gekoppelt -
   siehe ausfuehrliche Begruendung im HTML-Kommentar ueber
   #chatbot-widget in index.php. z-index 55: ueber dem Toast (50), aber
   unter Modal-Overlay/Chef-Taste (60/500), damit ein offenes
   Infofenster/die Chef-Taste den Chat bei Bedarf trotzdem vollstaendig
   verdeckt. */
.chatbot-widget {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 55;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 12px;
}
.chatbot-toggle-btn {
  width: 54px;
  height: 54px;
  border-radius: 50%;
  border: none;
  cursor: pointer;
  background: var(--accent);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--shadow), 0 0 0 1px var(--accent-soft);
  transition: transform 0.15s ease, background 0.15s ease;
}
.chatbot-toggle-btn:hover { background: var(--accent-hover); transform: scale(1.05); }
.chatbot-toggle-btn svg { width: 25px; height: 25px; }
.chatbot-panel {
  width: min(340px, calc(100vw - 40px));
  max-height: min(70vh, 480px);
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  overflow: hidden;
}
.chatbot-panel[hidden] { display: none; }
.chatbot-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 14px;
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
  font-weight: 600;
}
.chatbot-close-btn {
  border: none;
  background: transparent;
  color: var(--text-dim);
  cursor: pointer;
  padding: 4px;
  display: flex;
  border-radius: var(--radius-sm);
}
.chatbot-close-btn:hover { color: var(--text); background: var(--surface-hover); }
.chatbot-close-btn svg { width: 16px; height: 16px; }
.chatbot-messages {
  flex: 1;
  overflow-y: auto;
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-height: 180px;
}
.chatbot-msg {
  max-width: 85%;
  padding: 8px 12px;
  border-radius: var(--radius);
  font-size: 0.87rem;
  line-height: 1.4;
  white-space: pre-wrap;
}
.chatbot-msg-bot {
  align-self: flex-start;
  background: var(--surface-2);
  color: var(--text);
  border-bottom-left-radius: 4px;
}
.chatbot-msg-user {
  align-self: flex-end;
  background: var(--accent);
  color: #fff;
  border-bottom-right-radius: 4px;
}
/* Wunsch: "Auf dem Handy-Modus muss auch der senden Button anders
   positioniert werden ... der Button ist nur zur Hälfte sichtbar." Ursache:
   .chatbot-panel schneidet (overflow: hidden, fuer die runden Ecken) alles
   ab, was ueber seine Breite hinausragt - auf schmalen Bildschirmen reichte
   der Platz fuer Eingabefeld UND Button nicht immer in einer Zeile, wodurch
   der Button teilweise abgeschnitten wurde. flex-wrap sorgt dafuer, dass der
   Button bei Platzmangel in eine eigene Zeile darunter rutscht, statt
   abgeschnitten zu werden - passiert auf breiten Bildschirmen (genug Platz)
   nie. */
.chatbot-form {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 10px;
  border-top: 1px solid var(--border);
  background: var(--surface);
}
.chatbot-form input[type="text"] {
  flex: 1 1 140px;
  min-width: 0;
  padding: 9px 12px;
  border-radius: var(--radius);
  border: 1px solid var(--border-strong);
  background: var(--bg-soft);
  color: var(--text);
  font-family: var(--font);
  font-size: 0.87rem;
}
.chatbot-form input[type="text"]:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.chatbot-form button[type="submit"] {
  flex-shrink: 0;
  white-space: nowrap;
}
@media (max-width: 480px) {
  .chatbot-widget { right: 12px; bottom: 12px; }
  /* Wunsch: "in der handyversion muss der chatbot schmaler werden und die
     fenster besser (professioneller) positioniert werden, so dass alles
     sichtbar und uebersichtlich ist." Vorher wurde das Fenster hier auf
     "calc(100vw - 24px)" gesetzt - auf einem normalen Handy (~390px
     Breite) also rund 366px breit, praktisch die GESAMTE Bildschirmbreite
     mit nur 12px Rand je Seite. Dadurch wirkte das Fenster wie eine grosse,
     unfoermige Flaeche statt eines kompakten Chat-Fensters, und verdeckte
     nebenbei ueberdurchschnittlich viel vom Rest der Seite dahinter. Jetzt
     deutlich schmaler mit spuerbarem Rand auf beiden Seiten (passend zur
     unten-rechts verankerten Sprechblasen-Position, wie bei uebliche
     Chat-Widgets): auf einem 390px-Handy ergibt das rund 300px Breite -
     schmal und klar als eigenstaendiges, "schwebendes" Fenster erkennbar,
     nicht mehr wandfuellend. */
  .chatbot-panel {
    width: min(300px, calc(100vw - 56px));
    max-height: min(60vh, 420px);
  }
  /* Auf sehr schmalen Bildschirmen reicht die Breite oft nicht fuer
     Eingabefeld + Button nebeneinander - der Button bekommt hier bewusst
     die volle Breite in einer eigenen Zeile, statt gequetscht oder
     abgeschnitten zu werden. */
  .chatbot-form input[type="text"] { flex-basis: 100%; }
  .chatbot-form button[type="submit"] { flex: 1 1 auto; }
}
