/* ======================================================================
   P.M.C. Airsoft Website - zentrales Design-System (§6, §7, §65)
   Farben kommen als CSS-Variablen aus dem Admin-Backend (siehe
   templates/header.php) - hier werden nur diese Variablen verwendet,
   damit eine Farbänderung konsistent überall wirkt.
   ====================================================================== */

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }

/* ---- Scrollbar im eigenen Farbstil statt grauer System-Optik (Martins
   Wunsch, 21.09.26 - zunächst nur beim "Unsere Geschichte"-Zeitstrahl
   aufgefallen ("sieht aus wie Windows 95"), auf Nachfrage bewusst site-weit
   auf ALLE Scrollbars angewendet statt nur dort, damit es überall zum
   Farbstil passt). scrollbar-width/-color deckt Firefox ab, die drei
   ::-webkit-scrollbar-*-Regeln Chrome/Edge/Safari - zusammen alle gängigen
   Browser. Gilt automatisch für die Haupt-Seiten-Scrollbar UND jeden
   anderen Bereich mit eigenem overflow (z.B. .timeline-scroll). */
html {
  scrollbar-width: thin; scrollbar-color: var(--color-accent) var(--color-bg);
}
*::-webkit-scrollbar { width: 10px; height: 8px; }
*::-webkit-scrollbar-track { background: var(--color-bg); }
*::-webkit-scrollbar-thumb { background: var(--color-accent); border-radius: 999px; }
*::-webkit-scrollbar-thumb:hover { background: var(--color-link-hover); }

/* Martins Wunsch (22.09.26): "Standard-Abstand zwischen Kopf und Fußleiste
   ... der leere Shop hat die Fußleiste unterm Banner, die vollen Seiten
   ganz unten ... soll dynamisch bleiben, nur Seiten kürzer als das
   Standard-Bildschirmmaß betroffen, quasi ein Minimum-Abstand" - klassisches
   "Sticky Footer"-Muster per Flexbox statt einer festen Mindesthöhe: body
   wird zur Flex-Spalte über die volle Bildschirmhöhe (min-height:100vh),
   #main (siehe unten) wächst dank flex:1 auf den verbleibenden Platz, wenn
   der Seiteninhalt kürzer als der Bildschirm ist - dadurch landet die
   Fußleiste NIE höher als am unteren Bildschirmrand. Bei längeren Seiten
   hat #main ohnehin schon mehr Inhalt als 100vh, flex:1 ändert daran
   nichts - die Fußleiste bleibt ganz regulär direkt nach dem Inhalt, wie
   bisher. Voll dynamisch, keine feste Pixel-/vh-Mindesthöhe nötig. */
body {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: 'Inter', 'Segoe UI', sans-serif;
  line-height: 1.55;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}
#main { flex: 1 0 auto; }

h1, h2, h3, h4, .brand, button, .btn, .motto-main, .motto-sub {
  font-family: 'Rajdhani', 'Segoe UI', sans-serif;
  font-weight: 700;
  letter-spacing: .02em;
}

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

.wrap { max-width: 1180px; margin: 0 auto; padding: 0 1.2rem; }

.skip-link {
  position: absolute; left: -999px; top: 0; background: var(--color-accent);
  color: #111; padding: .5rem 1rem; z-index: 100;
}
.skip-link:focus { left: 1rem; top: 1rem; }

/* ---- Header / Navigation ------------------------------------------- */
.site-header {
  background: var(--color-primary);
  border-bottom: 1px solid var(--color-border);
  position: sticky; top: 0; z-index: 50;
}
.header-inner { display: flex; align-items: center; justify-content: space-between; padding: .7rem 1.2rem; }
.brand img { height: 42px; display: block; }
.brand-fallback { font-size: 1.3rem; color: var(--color-text); }
/* Wrapper für Logo + mobilen Menü-Button (siehe .nav-toggle weiter unten
   und templates/header.php). Seit Martins Feedback 18.09.26 sitzen beide
   als normale Flex-Kinder nebeneinander (Logo bleibt klickbar, Button
   eigenständig daneben) - kein position:absolute/inset mehr nötig, daher
   reicht hier ein einfaches Flex-Layout mit kleinem Abstand; am Desktop
   bleibt der Button unsichtbar (display:none), optisch identisch zum
   ursprünglichen einzelnen .brand-Link. */
.brand-wrap { display: inline-flex; align-items: center; gap: .5rem; }
/* Nav + Anmelden/Registrieren zusammen rechts, statt über die gesamte
   Kopfzeile verteilt - wie in der Design-Vorlage. */
.header-right { display: flex; align-items: center; gap: 1.4rem; }
/* .main-nav explizit als Flex-Reihe (Toggle/Suche/Liste nebeneinander) -
   die Reihenfolge im Markup bestimmt dadurch zuverlässig die Position der
   Suche (Martins Wunsch: Lupe links von "Start", also vor der <ul>). */
.main-nav { display: flex; align-items: center; gap: 1rem; }
.main-nav ul { list-style: none; display: flex; align-items: center; gap: 1.2rem; margin: 0; padding: 0; }
/* Military-Look: fett, leicht gespreizt, in Versalien - passend zum
   restlichen kantigen Rajdhani-Schriftbild der Seite. */
.main-nav a {
  color: var(--color-text);
  font-family: 'Rajdhani', 'Segoe UI', sans-serif;
  font-weight: 700;
  font-size: .92rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  position: relative;
  padding: .25rem 0;
  display: inline-block;
}
.main-nav a::after {
  content: ""; position: absolute; left: 0; right: 100%; bottom: -3px; height: 2px;
  background: var(--color-accent); transition: right .25s ease;
}
.main-nav a:hover { color: var(--color-accent); text-decoration: none; }
.main-nav a:hover::after { right: 0; }
/* Aktive Seite: eigene Textfarbe + dauerhafter Unterstrich (templates/header.php
   vergleicht den aktuellen Pfad gegen jeden Nav-Punkt und setzt die Klasse). */
.main-nav a.active { color: var(--color-accent); }
.main-nav a.active::after { right: 0; }
/* Basis: unsichtbar/inaktiv am Desktop. Die eigentliche Optik (volle
   Klickfläche über dem Logo + sichtbare Glaskachel) entsteht erst im
   @media max-width:820px weiter unten. */
.nav-toggle { display: none; background: none; border: none; padding: 0; cursor: pointer; }
/* Die eigentliche sichtbare "Glaskachel" (Martins Wunsch 18.09.26,
   echter Glasmorphismus-Look): halbtransparenter, weichgezeichneter
   Hintergrund mit dezentem Rand, Standard-Fallback solange kein eigenes
   Menü-Button-Logo im Branding hinterlegt ist (siehe .nav-toggle-logo). */
.nav-toggle-glass {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; border-radius: 9px; flex-shrink: 0;
  background: rgba(255, 255, 255, .14);
  border: 1px solid rgba(255, 255, 255, .3);
  backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px);
  box-shadow: 0 2px 10px rgba(0, 0, 0, .3);
  color: #fff; font-size: 1.2rem; line-height: 1;
}
/* Eigenes Menü-Button-Logo (Martins Wunsch 18.09.26, Branding-Slot
   "nav_button_mobile"): ersetzt die Glaskachel 1:1 durch ein Bild in
   gleicher Logohöhe wie das normale Logo (.brand img, 42px) - dadurch
   bleibt die Kopfzeilenhöhe identisch, egal ob mit oder ohne eigenes
   Menü-Logo. Nur relevant, wenn header.php dieses <img> überhaupt
   rendert (kein zusätzliches display:none am Desktop nötig, das erledigt
   bereits die .nav-toggle-Basisregel oben). */
.nav-toggle-logo { display: block; height: 42px; }
.header-auth { display: flex; align-items: center; gap: .6rem; flex-shrink: 0; }
/* Anmelden/Registrieren als zusätzliche Menüpunkte im einklappbaren Menü
   (siehe templates/header.php, `<ul id="navList">`) - genau wie Admin/
   Abmelden für eingeloggte Nutzer schon immer TEIL dieser Liste sind.
   Am Desktop unsichtbar (hier), erst im @media max-width:820px sichtbar,
   wo im Gegenzug der eigenständige .header-auth-Block ausgeblendet wird -
   siehe dortiger Kommentar für den Hintergrund (Martins Feedback
   18.09.26, mobile Ansicht: Buttons ragten sonst aus dem Rahmen). */
.nav-auth-mobile-only { display: none; }
/* .btn-sm selbst steht (bewusst als reiner Platzhalter/Doku-Hinweis) hier
   an der ursprünglichen Stelle - die tatsächliche Regel MUSS nach der
   Basis-.btn-Regel stehen, siehe dortiger Kommentar (derselbe Bug wie
   einst bei .btn-gold/.btn-admin/.btn-logout: gleiche Spezifität, spätere
   Quellreihenfolge gewinnt). */

/* ---- Kopfzeilen-Suche (Lupe vor Anmelden/Registrieren) --------------- */
.nav-search { display: flex; align-items: center; position: relative; margin-left: .2rem; }
.nav-search-toggle {
  background: none; border: none; color: var(--color-text-secondary); cursor: pointer;
  display: flex; align-items: center; padding: .3rem; border-radius: 4px; transition: color .2s;
}
.nav-search-toggle:hover, .nav-search.open .nav-search-toggle { color: var(--color-accent); }
.nav-search-input {
  width: 0; opacity: 0; padding: 0; margin-left: 0; border: 1px solid var(--color-border);
  border-radius: 5px; background: var(--color-card-bg); color: var(--color-text);
  font-size: .85rem; height: 32px; transition: width .22s ease, opacity .18s ease, padding .22s ease, margin-left .22s ease;
}
.nav-search.open .nav-search-input { width: 210px; opacity: 1; padding: 0 .6rem; margin-left: .4rem; }
.search-form-full { display: flex; gap: .6rem; margin-bottom: 1.2rem; }
.search-form-full input { flex: 1; padding: .55rem .8rem; border: 1px solid var(--color-border); border-radius: 6px; background: var(--color-card-bg); color: var(--color-text); }
.search-tag {
  display: inline-block; margin: 0 .5rem .5rem 0; padding: .4rem .9rem; border: 1px solid var(--color-border);
  border-radius: 999px; font-size: .85rem; color: var(--color-text);
}
.search-tag:hover { border-color: var(--color-accent); color: var(--color-accent); text-decoration: none; }
@media (max-width: 820px) {
  .nav-search { display: none; }
}

/* ---- Profil-Dropdown oben rechts (Martins Wunsch 20.09.26: "ein Profil-
   Icon oben rechts, das ein Dropdown mit Mein Profil/Admin Panel/Meine
   Bestellungen/Abmelden öffnet") - ersetzt die bisher einzeln in der Nav
   verstreuten Links. Bewusst dauerhaft sichtbar (auch mobil, siehe unten -
   keine @media-Ausblendung wie bei .nav-search), da Martin ausdrücklich
   "oben rechts in der Ecke" wollte, nicht erst nach Öffnen des
   Hamburger-Menüs. */
.nav-user-menu { position: relative; flex-shrink: 0; }
.nav-user-toggle {
  position: relative; background: none; border: none; padding: 0; cursor: pointer;
  display: flex; align-items: center; border-radius: 50%;
}
.nav-user-avatar {
  width: 38px; height: 38px; border-radius: 50%; object-fit: cover;
  display: block; border: 2px solid var(--color-border); transition: border-color .2s;
}
.nav-user-toggle:hover .nav-user-avatar, .nav-user-menu.open .nav-user-avatar { border-color: var(--color-accent); }
/* Zahl offener Admin-Aktionen am Profil-Icon (Martins Wunsch, 20.09.26) -
   bewusst dieselbe Form/Größe wie .nav-cart-badge weiter unten, aber in
   der Akzentfarbe statt fest grün, da es (anders als "Warenkorb hat
   Inhalt") ein rollenabhängiger Hinweis ist, kein neutraler Status. */
.nav-admin-badge {
  position: absolute; top: -3px; right: -4px; min-width: 17px; height: 17px; padding: 0 4px;
  border-radius: 999px; background: var(--color-accent); color: var(--color-primary); font-size: .68rem; font-weight: 700;
  line-height: 17px; text-align: center; box-shadow: 0 0 0 2px var(--color-primary);
}
/* Rotes "!" bei Systemwarnungen (Martins Wunsch, 22.09.26: "genau wie vor
   dem Admin Panel, die Farbe soll mit dem Abmeldebutton verknüpft werden"),
   siehe admin_has_system_warning() in includes/functions.php. Bewusst UNTEN
   LINKS am Profilbild (gegenüberliegende Ecke zu .nav-admin-badge oben
   rechts), damit Zahl UND Ausrufezeichen gleichzeitig sichtbar sein können
   - zwei getrennte Aussagen (Zahl = "das musst du bearbeiten",
   Ausrufezeichen = "am System stimmt etwas nicht"). Farbe bewusst
   var(--color-logout) statt der Akzentfarbe der Zahl-Badges. */
.nav-admin-warning {
  position: absolute; bottom: -3px; left: -4px; min-width: 17px; height: 17px; padding: 0 4px;
  border-radius: 999px; background: var(--color-logout, #b23a3a); color: #fff; font-size: .68rem; font-weight: 700;
  line-height: 17px; text-align: center; box-shadow: 0 0 0 2px var(--color-primary);
}
/* Dieselbe Zahl nochmal neben "Admin Panel" im aufgeklappten Dropdown -
   kleine Pille statt freistehender Zahl, damit sie sich klar vom
   Linktext abhebt. */
.nav-user-admin-count {
  display: inline-block; min-width: 17px; height: 17px; padding: 0 4px; margin-left: .3rem;
  border-radius: 999px; background: var(--color-accent); color: var(--color-primary); font-size: .68rem; font-weight: 700;
  line-height: 17px; text-align: center; vertical-align: middle;
}
/* Analog zu .nav-admin-warning oben, hier als Pille neben "Admin Panel" im
   aufgeklappten Dropdown (analog zu .nav-user-admin-count). */
.nav-user-admin-warning {
  display: inline-block; min-width: 17px; height: 17px; padding: 0 4px; margin-left: .3rem;
  border-radius: 999px; background: var(--color-logout, #b23a3a); color: #fff; font-size: .68rem; font-weight: 700;
  line-height: 17px; text-align: center; vertical-align: middle;
}
.nav-user-dropdown {
  display: none; flex-direction: column; list-style: none; margin: 0; padding: .5rem;
  position: absolute; right: 0; top: calc(100% + 12px); min-width: 190px;
  background: var(--color-primary); border: 1px solid var(--color-border); border-radius: 8px;
  box-shadow: 0 14px 28px rgba(0, 0, 0, .4); z-index: 70;
}
.nav-user-menu.open .nav-user-dropdown { display: flex; }
.nav-user-dropdown a {
  display: block; padding: .55rem .7rem; border-radius: 5px;
  color: var(--color-text); font-family: 'Rajdhani', 'Segoe UI', sans-serif;
  font-weight: 700; font-size: .88rem; letter-spacing: .06em; text-transform: uppercase;
}
.nav-user-dropdown a:hover, .nav-user-dropdown a.active { background: rgba(255, 255, 255, .07); color: var(--color-accent); text-decoration: none; }
.nav-user-dropdown a.nav-user-logout { color: var(--color-logout, #b23a3a); }
.nav-user-dropdown a.nav-user-logout:hover { background: rgba(178, 58, 58, .15); color: var(--color-logout, #b23a3a); }
/* Martins Wunsch (20.09.26): "Admin Panel sollte mit der Akzentfarbe
   gekoppelt sein, dadurch fällt er im Dropdown auf" - DAUERHAFT (nicht nur
   bei :hover, das gilt für alle Dropdown-Punkte gleich) in der Akzentfarbe,
   damit er sich sichtbar von Mein Profil/Meine Bestellungen/Abmelden
   abhebt. Eigene, spezifischere Regel statt sich auf die geteilte
   `.nav-user-dropdown a:hover`-Regel zu verlassen. */
.nav-user-dropdown a.nav-user-admin { color: var(--color-accent); }
.nav-user-dropdown a.nav-user-admin:hover { background: rgba(255, 255, 255, .07); color: var(--color-accent); }

/* ---- Warenkorb-Icon oben rechts (Martins Wunsch 20.09.26: "selbe Größe
   wie der Profil-Icon-Button") - exakt dieselben Maße/Rahmen-Logik wie
   .nav-user-toggle/.nav-user-avatar oben, damit beide Icons nebeneinander
   optisch als Paar wirken. */
.nav-cart-toggle {
  position: relative; flex-shrink: 0; display: flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; border-radius: 50%;
  background: rgba(255, 255, 255, .06); border: 2px solid var(--color-border);
  color: var(--color-text); transition: border-color .2s, color .2s;
}
.nav-cart-toggle:hover, .nav-cart-toggle.active { border-color: var(--color-accent); color: var(--color-accent); text-decoration: none; }
/* War bewusst fest grün codiert (gleiches Muster wie die fest codierten
   .flash-ok/.flash-err-Farben) - auf Martins ausdrücklichen Wunsch
   (22.09.26: "Die Zahl am Warenkorb kann man über Design und Farbe nicht
   anpassen") jetzt doch als eigene Designfarbe var(--color-cart-badge),
   siehe admin/design.php. Startwert #3fae4e = bisheriger fest codierter
   Wert, damit sich am Aussehen nichts ändert, bis Martin es bewusst anpasst. */
.nav-cart-badge {
  position: absolute; top: -3px; right: -4px; min-width: 17px; height: 17px; padding: 0 4px;
  border-radius: 999px; background: var(--color-cart-badge, #3fae4e); color: #fff; font-size: .68rem; font-weight: 700;
  line-height: 17px; text-align: center; box-shadow: 0 0 0 2px var(--color-primary);
}

/* Admin-Link/Abmelden-Farbregeln stehen jetzt bewusst NACH der Basis-
   Buttonregel .btn (siehe unten, kurz nach .btn-gold) - siehe Kommentar
   dort für den Hintergrund (derselbe Bug wie einst bei .btn-gold in v1.1.0). */

.social-icon { display: inline-flex; opacity: .92; transition: opacity .2s, transform .2s; }
.social-icon:hover { opacity: 1; transform: translateY(-1px); }
.social-icon svg { width: 22px; height: 22px; display: block; }
/* Fußzeile: Martins Wunsch - Icons in der Größe beider Mottos übereinander */
.footer-social { display: flex; align-items: center; gap: .6rem; }
.footer-social .social-icon svg { width: 42px; height: 42px; }

@media (max-width: 820px) {
  /* Eigenständiger Button neben (nicht mehr über) dem Logo (Martins
     Feedback 18.09.26: Logo soll wieder ein normaler, unangetasteter
     Start-Link sein, Menü-Button klar davon getrennt) - einfaches
     Flex-Kind in .brand-wrap, keine Sonderpositionierung mehr nötig, die
     sichtbare Glaskachel (.nav-toggle-glass) bestimmt die Größe. */
  .nav-toggle {
    display: flex; align-items: center; justify-content: center;
  }
  /* Eigenes Menü-Logo hinterlegt (nav_button_mobile) -> normales Logo
     verschwindet mobil komplett, der Button mit dem Menü-Logo übernimmt
     optisch dessen Platz (Martins Wunsch 18.09.26: 2. Logo ERSETZT den
     Burger-Button, statt nur daneben zu stehen). Ohne hinterlegtes
     Menü-Logo bleibt .brand wie gehabt sichtbar (siehe Regel oben). */
  .brand-wrap.has-mobile-toggle-logo .brand { display: none; }
  .header-right { gap: .6rem; }
  /* Öffnet unter dem Button (Martins Feedback 18.09.26: "Button ist
     links, Menü macht sich rechts auf, das verwirrt") - Button sitzt
     links neben dem Logo, daher öffnet das Menü ebenfalls von links. */
  .main-nav ul { display: none; flex-direction: column; position: absolute; left: 1rem; top: 64px; background: var(--color-primary); border: 1px solid var(--color-border); padding: 1rem; border-radius: 8px; z-index: 60; }
  .main-nav ul.open { display: flex; }
  /* KORRIGIERTER Bugfix (Martins Feedback 18.09.26, mobile Ansicht:
     "Anmelden/Registrieren ragen aus dem Rahmen"): eine erste Fassung
     dieses Fixes hatte nur die Button-Breite reduziert (min-width
     entfernt) - das reichte auf wirklich schmalen Handys (320-360px,
     nachträglich mit einem zuverlässig ladenden Test-Logo statt eines
     evtl. nicht geladenen Platzhalterbilds nachgeprüft) NICHT aus, weil
     Logo + Menü-Symbol + beide Buttons selbst in kleinster Form nicht
     gleichzeitig in eine Zeile passen. Sauberere Lösung, konsistent mit
     Admin/Abmelden (die für eingeloggte Nutzer schon immer NUR im
     aufgeklappten Menü stehen, nicht permanent in der Kopfzeile): der
     eigenständige .header-auth-Block verschwindet hier komplett, Anmelden/
     Registrieren erscheinen stattdessen als zusätzliche Punkte im
     einklappbaren Menü (.nav-auth-mobile-only, siehe templates/header.php)
     - dadurch beansprucht die Kopfzeile im geschlossenen Zustand nur noch
     Platz für Logo+Menü-Symbol, unabhängig von Button- oder Logobreite. */
  .header-auth { display: none; }
  .nav-auth-mobile-only { display: list-item; }
}

/* ---- Flash-Meldungen -------------------------------------------------- */
.flash { margin-top: 1rem; padding: .8rem 1rem; border-radius: 6px; }
.flash-ok { background: #1e4022; border: 1px solid #2f6a34; }
.flash-err { background: #4a1e1e; border: 1px solid #7a2b2b; }

/* ---- Layout-Bausteine -------------------------------------------------- */
section { padding: 2.5rem 0; }
.section-title { font-size: 1.7rem; margin-bottom: 1.2rem; color: var(--color-accent); }
.grid { display: grid; gap: 1.2rem; }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 900px) { .grid-3, .grid-4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 600px) { .grid-3, .grid-4 { grid-template-columns: 1fr; } }

/* ---- Shop-Produktseite: Bild/Details-Layout (Martins Feedback 25.09.26,
   Handy-Screenshot: "warum ist nicht oben das Bild unten der Text") -
   pages/shop_product.php nutzte bisher einen reinen Inline-Style
   (grid-template-columns:1fr 1fr) OHNE jeden responsiven Umbruch (einziger
   Fundort dieses Musters im ganzen Projekt) - dadurch blieb die Seite auch
   auf dem Handy zweispaltig und gequetscht. Eigene Klasse statt Wieder-
   verwendung von .grid-3/.grid-4 (die brechen erst bei 900px auf 2 Spalten
   um, hier sollen es aber von Anfang an nur 2 Spalten sein, die dann direkt
   auf 1 Spalte umbrechen). */
.shop-product-layout { display: grid; grid-template-columns: 1fr 1fr; gap: 2rem; align-items: start; }
@media (max-width: 640px) { .shop-product-layout { grid-template-columns: 1fr; } }

/* ---- Shop-Produktbilder: fester Rahmen fürs Pillarbox-Weichzeichnen
   (Martins Feedback 25.09.26: "bei den Bildern im Shop fehlt der Pi-
   sowieso-Effekt") - analog zu .event-detail-cover (siehe dortiger
   ausführlicher Kommentar weiter unten), da die Weichzeichenebene (::before)
   einen positionierten Wrapper-Container braucht und die Produktseite
   mehrere Bilder untereinander zeigt. Ohne .pillarbox (Platzhalterbild ohne
   eigenes Foto) bleibt es beim gewohnten object-fit:cover. */
.shop-product-image { display: block; width: 100%; aspect-ratio: 4/3; border-radius: 8px; overflow: hidden; margin-bottom: .8rem; background: var(--color-bg); }
.shop-product-image img { width: 100%; height: 100%; display: block; object-fit: cover; }

.card {
  background: var(--color-card-bg);
  border: 1px solid var(--color-border);
  border-radius: 10px;
  overflow: hidden;
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
}
.card:hover { transform: translateY(-4px); box-shadow: 0 14px 28px rgba(0,0,0,.4); border-color: var(--color-accent); }
.card img { width: 100%; display: block; aspect-ratio: 4/3; object-fit: cover; }
.card-body { padding: .9rem 1rem; }
.card-title { margin: 0 0 .3rem; font-size: 1.05rem; }
.card-meta { color: var(--color-text-secondary); font-size: .82rem; }

/* ---- Admin-Galerieverwaltung: die Karte des aktuell gesetzten Thumbnail-
   Bilds leuchtet in der Akzentfarbe (Martins Wunsch 22.09.26: "Können wir
   das als Thumbnail markierte Bild ... von der Kachel her in der
   Akzentfarbe leuchten lassen, also nur im Admin-Bereich, nicht in der für
   alle sichtbaren Galerie") - bewusst eine eigene Klasse statt einer
   Änderung an .card selbst, damit News-/Team-/Event-Kacheln usw. unberührt
   bleiben und die öffentlichen Galerie-Ansichten (die .gallery-tile bzw.
   .gallery-flip-tile statt .card nutzen) gar nicht erst betroffen sind. */
.card.is-thumbnail {
  border-color: var(--color-accent);
  box-shadow: 0 0 14px var(--color-accent);
}

/* ---- Wiederverwendbarer Hover-Glow (News-/Events-/Galerie-/Team-Übersicht,
   §Feedback: "der selbe Glow wie auf der Startseite die News haben") - löst
   auf Karten mit dieser Klasse den Standard-Hover (Lift+Schatten) ab. ---- */
/* Generalisiert auf ALLE Tags (nicht mehr nur <a>), damit z.B. auch die
   Video-Kachel (ein <div>, da kein einzelner Link den ganzen Block umfasst)
   denselben Effekt bekommt - Martins Feedback: "das rot umrandete hat beim
   drüber hovern nicht den standard grünen glow effekt". */
.glow-card { transition: box-shadow .25s ease, border-color .25s ease; }
.glow-card:not(.card) { border: 1px solid var(--color-border); border-radius: 10px; }
.panel-feature.glow-card, .video-feature.glow-card { padding: .7rem; }
.glow-card:hover, .glow-card:focus-within {
  transform: none; text-decoration: none;
  border-color: var(--color-glow);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-glow) 65%, transparent),
              0 0 26px 3px color-mix(in srgb, var(--color-glow) 55%, transparent);
}
.panel-list li a.glow-card { border-radius: 6px; }

/* ---- Wiederverwendbare rotierende Hover-Diashow (Events/Galerie/Team) --
   Löst das alte "Flip zu einem 4er-Mosaik" ab (Martins Feedback: das war
   nirgends gefordert und wirkte wie "immer dieselben 4 Bilder"). Eine
   einzelne Bildfläche (.rotor-img) rotiert per JS (main.js) bei Hover/Fokus
   einzeln durch alle Bilder - abwechselnd um eine andere Achse gedreht, wie
   eine "echte" Diashow statt eines Grids - und kehrt beim Verlassen zum
   ursprünglichen Thumbnail zurück. Ohne JS/reduced-motion bleibt einfach
   das Thumbnail stehen (progressive enhancement, kein CSS-only-Fallback
   nötig, da das Thumbnail ohnehin schon das sichtbare Bild ist). */
.rotor { position: relative; display: block; width: 100%; height: 100%; overflow: hidden; perspective: 1400px; }
.rotor-img { width: 100%; height: 100%; object-fit: cover; display: block; backface-visibility: hidden; }
/* In .card-Grids (Events/Team-Übersicht) ersetzt .rotor das direkte <img> */
/* height:auto ist hier Pflicht (nicht nur Kosmetik): .rotor setzt oben
   height:100%, was sich in einem .card mit nachfolgendem .card-body (Team-
   Kacheln) NICHT auf die reine Bildfläche bezieht, sondern auf die GESAMTE
   Kachelhöhe inkl. Textbereich - das drückt .card-body dann komplett aus dem
   sichtbaren Bereich (von .card's overflow:hidden verschluckt). Derselbe
   Bug wie bei .panel-feature-img.rotor weiter unten, hier fürs Karten-Raster
   nachgezogen. */
.card .rotor { aspect-ratio: 4/3; height: auto; }

/* ---- Team-Übersicht: quadratische Kacheln (wie zuvor), unabhängig von
   Team-Member/Anwärter/Veteran - Bugfix: Veteranen-Fotos wurden vorher über
   die feste 100x100px-Kreisklasse .profile-avatar gestaucht; jetzt bekommen
   sie dieselbe Kachelgröße wie alle anderen, nur mit Graustufen-Look. -----
   Martins Feedback (18.09.26): Rufname/Beitrittsdatum unter dem Bild waren
   unsichtbar UND die Kacheln unterschiedlich groß, weil .rotor (s.o.) hier
   die komplette Kachelhöhe statt der quadratischen Bildfläche einnahm -
   height:auto behebt beides (die Bildhöhe ergibt sich jetzt wirklich aus
   aspect-ratio 1/1, .card-body bekommt seinen Platz zurück, und da jede
   Kachel dieselbe Breite hat, wird jede Kachel dadurch auch gleich hoch). */
.team-grid .rotor { aspect-ratio: 1/1; height: auto; }
.team-card-img.veteran { filter: grayscale(85%) brightness(.85); }

/* ---- Hero-Banner (Startseite) ------------------------------------------- */
/* Wie die restlichen Seiteninhalte auf die Inhaltsbreite (.wrap, 1180px,
   zentriert) begrenzt statt komplett randlos über die volle Fensterbreite -
   auf Martins Wunsch, damit der Banner in eine Flucht mit dem Rest der
   Seite kommt (nicht randlos/"edge-to-edge"). */
/* ---- Seiten-Header (News/Events/Galerie/Team/Über uns/Links) ----------- */
/* Breite exakt wie der Inhaltsbereich von .wrap (1180px minus dessen
   1.2rem-Innenabstand je Seite) berechnet, statt einfach max-width:1180px -
   sonst reicht die Box randlos bis 1180px, während die Kacheln darunter
   (in .wrap) durch dessen Padding 1.2rem eingerückt beginnen und der
   Header dadurch nicht bündig mit den Kacheln abschließt. */
.page-header {
  position: relative; width: calc(100% - 2.4rem); max-width: calc(1180px - 2.4rem);
  margin: 1.6rem auto 1.4rem; min-height: 170px;
  display: flex; align-items: center; border: 1px solid var(--color-border);
  border-radius: 10px; overflow: hidden; background: var(--color-primary);
}
.page-header-bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0; }
.page-header-overlay {
  position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(90deg, rgba(15,20,16,.88) 15%, rgba(15,20,16,.35) 100%);
}
.page-header-inner { position: relative; z-index: 2; padding: 2.2rem 1.6rem; }
.page-header-inner h1 {
  margin: 0; font-size: clamp(1.8rem, 4vw, 2.6rem); text-transform: uppercase; color: var(--color-text);
}
.page-header-inner p { margin: .5rem 0 0; color: var(--color-text-secondary); max-width: 560px; }
@media (max-width: 700px) {
  .page-header { min-height: 130px; }
  .page-header-inner { padding: 1.6rem 1.2rem; }
}

.hero-banner {
  position: relative;
  max-width: 1180px;
  /* Abstand zur Kopfzeile darüber (Martins Feedback: "zu wenig Abstand") -
     gleicher Wert wie der obere Abstand von .page-header auf den anderen
     Seiten, damit Start- und Inhaltsseiten einheitlich wirken. */
  margin: 1.6rem auto 0;
  background-color: var(--color-primary);
  background-size: cover;
  background-position: center 30%;
  border: 1px solid var(--color-border);
  border-radius: 10px;
  overflow: hidden;
}
.hero-banner-inner { position: relative; z-index: 2; padding: 3.6rem 1.2rem 3.2rem; max-width: 640px; }
.hero-tags {
  margin: 0 0 .9rem; font-family: 'Rajdhani', sans-serif; font-weight: 600;
  letter-spacing: .12em; text-transform: uppercase; font-size: .85rem; color: var(--color-text-secondary);
}
.hero-tags .sep { color: var(--color-border); margin: 0 .3rem; }
.hero-banner h1 {
  margin: 0 0 .7rem; font-size: clamp(2rem, 5vw, 3.1rem); line-height: 1.05;
  color: var(--color-text); text-transform: uppercase;
}
.hero-banner h1 .accent { color: var(--color-accent); }
.hero-banner .motto-sub { margin: 0 0 1.4rem; font-size: 1.05rem; color: var(--color-text-secondary); max-width: 480px; }
.hero-actions { display: flex; flex-wrap: wrap; gap: .8rem; }
/* Signatur-Schriftzug oben rechts (motto_main/motto_sub) - ersetzt die
   frühere reine Textdarstellung, Inhalt bleibt admin-editierbar. */
.hero-signature { position: absolute; z-index: 2; top: 7%; right: 5%; text-align: right; max-width: 260px; }
.hero-signature-main {
  margin: 0; font-family: 'Caveat', 'Segoe Script', cursive; font-size: 1.9rem;
  color: var(--color-text); line-height: 1.1; transform: rotate(-2deg);
}
.hero-signature-sub {
  margin: .8rem 0 0; display: flex; align-items: center; justify-content: flex-end; gap: .5rem;
  font-family: 'Rajdhani', sans-serif; font-weight: 600; letter-spacing: .1em; font-size: .8rem;
  color: var(--color-text-secondary);
}
.chevrons { display: inline-flex; gap: 2px; }
.chevrons span { display: inline-block; width: 6px; height: 16px; background: var(--color-accent); transform: skewX(-20deg); }
@media (max-width: 700px) {
  .hero-banner-inner { padding: 2.8rem 1.2rem 2rem; }
  .hero-signature { display: none; }
}

/* ---- Startseite: 3-Spalten-Panel-Layout (Aktuelle News / Kommende Events /
   Aktuelles Video) - je Panel ein hervorgehobener erster Eintrag + kompakte
   Liste der beiden nächsten Einträge ---------------------------------------- */
/* grid-template-rows + subgrid (Martins Feedback v1.1.3-Review, violett
   markiert): die Trennlinie zwischen "hervorgehobener Eintrag" und
   "kompakter Liste" soll bei News UND Events zwingend auf derselben Höhe
   liegen - Events ist dabei der "Master", da Spielort+Adresse dort nicht
   gekürzt werden können. Ohne subgrid würde jede Spalte einfach ihre
   eigene (unterschiedlich hohe) Feature-Kachel bekommen; mit subgrid teilen
   sich alle drei Panels dieselben 3 Zeilen-Spuren (Kopf/Feature/Liste), die
   von der jeweils höchsten Spalte je Zeile bestimmt werden. Browser-
   Unterstützung (Chrome 117+, Firefox 71+, Safari 16+) ist für dieses
   Projekt unproblematisch. Das Video-Panel bekommt bewusst KEIN eigenes
   subgrid (siehe .video-feature unten) - dessen Inhalt ist strukturell
   anders (kein Feature/Liste-Split) und spannt row 2+3 als ein Block. */
.home-panels { display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: auto auto auto; gap: 1.6rem; }
.home-panel {
  display: grid; grid-template-rows: subgrid; grid-row: span 3;
  background: var(--color-card-bg); border: 1px solid var(--color-border);
  border-radius: 10px; padding: 1.2rem 1.3rem 1.4rem;
}
/* ECHTER BUG (Martins Feedback 19.09.26, Screenshot mit orangener/roter
   Markierung): Abstand zwischen der Trennlinie hier und der Feature-Kachel
   darunter (Bild) wirkte gemessen VIERMAL so groß wie der Abstand zwischen
   Feature-Kachel und Terminliste darunter (.panel-list, roter Vergleichs-
   wert) - 41.6px vs. 12.8px. Ursache: derselbe Mechanismus, der schon bei
   .panel-list dokumentiert ist (siehe dortiger Kommentar) - .home-panels
   hat einen festen Grid-Zeilenabstand (gap:1.6rem=25.6px), der
   automatisch ZUSÄTZLICH zum eigenen margin-bottom hier addiert wird
   (16px + 25.6px = 41.6px). .panel-list löst das bereits über ein
   negatives margin-top von -.8rem (zieht die Hälfte des Grid-Gaps wieder
   ein, Rest bleibt .8rem=12.8px sichtbar) - derselbe Wert hier auf
   margin-bottom übertragen bringt beide Abstände exakt auf denselben,
   von Martin gewünschten kleineren Wert (25.6px - 12.8px = 12.8px netto).
   Per Playwright-Nachmessung bestätigt: beide Abstände jetzt exakt 12.8px. */
.home-panel-head {
  grid-row: 1;
  display: flex; align-items: center; justify-content: space-between; gap: .6rem;
  margin: 0 0 -.8rem; padding-bottom: .8rem; border-bottom: 1px solid var(--color-border);
}
.panel-empty { margin: 0; }
.home-panel .panel-empty { grid-row: 2 / span 2; }
.home-panel-head h2 { margin: 0; font-size: 1.05rem; display: flex; align-items: center; gap: .5rem; color: var(--color-text); }
.panel-icon { width: 20px; height: 20px; flex-shrink: 0; fill: none; stroke: var(--color-accent); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.panel-more { font-size: .8rem; white-space: nowrap; color: var(--color-text-secondary); }
.panel-more:hover { color: var(--color-accent); }

/* grid-row NUR innerhalb des Startseiten-Panel-Subgrids relevant (siehe
   .home-panels oben) - hier bewusst per Vorfahren-Selektor eingeschränkt,
   damit .panel-feature/.panel-list auch außerhalb (z.B. Events-Seite)
   gefahrlos wiederverwendet werden können, ohne von einer impliziten
   Grid-Zeilen-Platzierung überrascht zu werden. */
.home-panel .panel-feature { grid-row: 2; }
.panel-feature { display: block; color: var(--color-text); }
.panel-feature:hover { text-decoration: none; }
.panel-feature-img {
  position: relative; display: block; aspect-ratio: 16/10; border-radius: 8px;
  overflow: hidden; background: var(--color-bg); margin-bottom: .8rem;
}
.panel-feature-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Bugfix (Martins Screenshot-Feedback, Startseite "Kommende Events"):
   Beim nächsten Event ist .panel-feature-img zusätzlich mit .rotor
   kombiniert (Diashow-Bild). .rotor setzt eigentlich "height: 100%" fürs
   Hover-Overlay in der kompakten Terminliste - kombiniert mit dem festen
   aspect-ratio hier gewinnt aber die explizite Höhe, sobald die Kachel
   (Grid-Element im Subgrid-Layout) eine definierte Höhe bekommt. Das Bild
   wuchs dadurch auf die komplette Kachelhöhe, wodurch Titel/Adresse/Button
   nach unten aus der Kachel heraus- und in die Liste darunter hineinragten
   ("Kachel sieht kaputt/überlappend aus"). Hier gewinnt wieder das
   aspect-ratio. */
.panel-feature-img.rotor { height: auto; }
.panel-feature-date {
  position: absolute; left: .6rem; bottom: .6rem; background: var(--color-button); color: #fff;
  font-family: 'Rajdhani', sans-serif; font-weight: 600; letter-spacing: .02em;
  font-size: .78rem; padding: .18rem .55rem; border-radius: 4px;
}
.panel-feature h3 { margin: 0 0 .4rem; font-size: 1.08rem; line-height: 1.3; }
.panel-feature:hover h3 { color: var(--color-accent); }
.panel-feature-excerpt {
  margin: 0 0 .6rem; color: var(--color-text-secondary); font-size: .87rem; line-height: 1.4;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.panel-cta { display: inline-block; font-family: 'Rajdhani', sans-serif; font-weight: 600; font-size: .85rem; color: var(--color-accent); }

/* Event-Feature-Details (Martins Feedback v1.1.3-Review): statt einer
   einzelnen, ggf. abgeschnittenen Zeile jetzt Titel/Spielfeld/Adresse/
   Uhrzeit als eigene Zeilen - genau Martins Vorschlag "Überschrift Event /
   Spielfeld / Adresse / Uhrzeit von bis oder ganztägig / Details ansehen". */
.event-feature-details { margin: 0 0 .6rem; color: var(--color-text-secondary); font-size: .87rem; line-height: 1.45; }
.event-feature-details > span { display: block; }

.home-panel .panel-list { grid-row: 3; }
/* Bugfix (Martins Feedback 18.09.26, rote Linien im Screenshot): Der
   Abstand ÜBER der Trennlinie (Freiraum zwischen Feature-Kachel und
   Trennstrich) war deutlich größer als der Abstand DARUNTER (Trennstrich
   bis erster Listeneintrag) - 2,5rem oben gegen nur 0,8rem unten. Ursache:
   .home-panels nutzt CSS-Grid-subgrid mit einem festen Zeilenabstand
   (gap: 1,6rem) ZWISCHEN Feature-Zeile und Listen-Zeile - dieser
   Grid-Gap addierte sich zum eigenen margin-top von .panel-list
   (0,9rem), macht zusammen 1,6rem + 0,9rem = 2,5rem oben, während unten
   nur das padding-top (0,8rem) wirkt. Der Grid-Gap lässt sich hier nicht
   einfach entfernen (dieselbe gap-Eigenschaft bestimmt auch den Abstand
   Kopf->Feature), daher zieht ein NEGATIVES margin-top einen Teil davon
   wieder ein: 1,6rem - 0,8rem = 0,8rem oben, exakt symmetrisch zu den
   0,8rem unten (padding-top bewusst unverändert) - und in Summe spürbar
   weniger "Freiraum" als vorher (1,6rem statt 3,3rem insgesamt). */
.panel-list { list-style: none; margin: -.8rem 0 0; padding: .8rem 0 0; border-top: 1px solid var(--color-border); display: flex; flex-direction: column; gap: .3rem; }
.panel-list li a { display: flex; align-items: center; gap: .7rem; padding: .4rem .3rem; border-radius: 6px; color: var(--color-text); }
.panel-list li a:hover { background: var(--color-bg); text-decoration: none; }
/* Direkter Kind-Selektor (>) statt Nachfahren-Selektor: sollte NUR das
   kleine News-Vorschaubild treffen (direktes <img> im <a>). Traf vorher
   versehentlich auch das .rotor-img der Events-Kompaktzeilen (liegt dort
   in einem verschachtelten <span class="rotor">), zwang es fälschlich auf
   44x44px und ließ es beim Hover als kleinen, deplatziert wirkenden
   Bildfleck über dem Zeilentext erscheinen, statt die ganze Zeile als
   Diashow-Overlay abzudecken - Teil desselben Bugfixes wie oben. */
.panel-list li a > img { width: 44px; height: 44px; border-radius: 6px; object-fit: cover; flex-shrink: 0; }
.panel-list-text {
  flex: 1; min-width: 0; display: flex; flex-direction: column; gap: .1rem; font-size: .88rem;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.panel-list-text small { color: var(--color-text-secondary); font-size: .75rem; }
.panel-arrow { color: var(--color-text-secondary); flex-shrink: 0; }
.panel-list li a:hover .panel-arrow { color: var(--color-accent); }

/* Events-Panel: alle 3 Termine einheitlich als große Kachel (nächster oben,
   die beiden folgenden darunter). Der hervorgehobene erste Termin nutzt
   jetzt (Martins Feedback aus dem v1.1.2-Testdurchgang) dieselbe große
   .panel-feature-Darstellung wie die News-Kachel daneben (Bild, Datums-
   Badge, Titel, Spielort+Adresse) - "harmonischer Einklang". Die beiden
   folgenden Termine erscheinen kompakt (Datum links, Titel+Spielort
   rechts, Eventbanner-Thumbnail rechts daneben, Pfeil ganz rechts).
   Früher (bis inkl. v1.2.4) zeigte sich hier erst beim Hovern/Fokussieren
   ein Bild, das sich per position:absolute;inset:0 über die KOMPLETTE
   Zeile legte und dabei durch die Galeriebilder des Spielfelds rotierte
   ("Flip-Effekt") - von Martin (19.09.26, Screenshot mit blauer
   Markierung) explizit nicht mehr gewünscht: stattdessen jetzt ein
   dauerhaft sichtbares, kleines statisches Eventbanner-Thumbnail
   (.event-compact-thumb, siehe unten) in dergleichen Höhe wie die
   Datumsbox daneben - kein Hover nötig, keine rotierende Diashow mehr für
   diese beiden kompakten Zeilen (die Diashow bleibt an allen anderen
   Stellen wie gehabt, z.B. bei der großen Feature-Kachel des nächsten
   Termins direkt darüber). */
.event-compact {
  display: flex; align-items: center; gap: .8rem;
}
.event-compact-date {
  flex-shrink: 0; display: flex; flex-direction: column; align-items: center; justify-content: center;
  min-width: 46px; padding: .35rem .2rem; border-radius: 6px; background: var(--color-secondary); line-height: 1.2;
}
.event-compact-date b { font-family: 'Rajdhani', sans-serif; font-size: 1.15rem; line-height: 1; color: var(--color-text); }
.event-compact-date small { font-size: .64rem; letter-spacing: .04em; text-transform: uppercase; color: var(--color-accent); }
.event-compact-info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: .1rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.event-compact-info strong { font-size: .9rem; font-family: 'Rajdhani', sans-serif; overflow: hidden; text-overflow: ellipsis; }
.event-compact-info span { font-size: .78rem; color: var(--color-text-secondary); overflow: hidden; text-overflow: ellipsis; }
/* Höhe (42px) per Playwright an der tatsächlich gerenderten Höhe von
   .event-compact-date gemessen ("in derselben Höhe wie das Datum", Martins
   Wunsch) - Breite 56px (4:3) daraus abgeleitet, damit das Thumbnail nicht
   quadratisch gequetscht wirkt. */
.event-compact-thumb { flex-shrink: 0; width: 56px; height: 42px; border-radius: 6px; overflow: hidden; display: block; }
.event-compact-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.event-compact:hover .panel-arrow { color: var(--color-accent); }

/* ---- Events-Seite (/events): Zeilen-Darstellung für die kommenden Termine
   (Martins Feedback v1.1.3-Review, Mockup-Bild) - Datum links, Titel/Ort/
   Teaser/Tags mittig, großes Bild rechts mit Glow+Rotor-Diashow und einem
   "Details ansehen"-Button. Die ganze Zeile ist ein einziger <a>-Link zur
   neuen Event-Detailseite (/events/<id>) - kein verschachteltes <a> im
   Button, konsistent mit .panel-feature/.event-compact anderswo. ---------- */
.event-rows { display: flex; flex-direction: column; gap: 1.1rem; }
.event-row {
  display: grid; grid-template-columns: auto 1fr 240px; gap: 1.4rem; align-items: stretch;
  padding: 1.1rem; color: var(--color-text);
}
/* Martins Feedback (Screenshot, 18.09.26): das Datumsfeld wirkte im
   Vergleich zur restlichen Zeile winzig - soll stattdessen die volle
   Zeilenhöhe einnehmen (wie orange markiert). .event-row bekommt dafür
   align-items:stretch (statt center) und .event-row-date selbst ein
   größeres Mindestmaß, damit Tag/Monat darin weiterhin schön mittig
   sitzen, auch wenn die Zeile durch Bild/Text höher ist als die Zahl. */
.event-row-date {
  flex-shrink: 0; display: flex; flex-direction: column; align-items: center; justify-content: center;
  min-width: 84px; padding: .6rem .4rem; border-radius: 8px; background: var(--color-secondary); line-height: 1.15;
}
.event-row-date b { font-family: 'Rajdhani', sans-serif; font-size: 2.3rem; line-height: 1; color: var(--color-text); }
.event-row-date small { font-size: .8rem; letter-spacing: .05em; text-transform: uppercase; color: var(--color-accent); }
.event-row-body h3 { margin: 0 0 .35rem; font-size: 1.15rem; line-height: 1.3; }
.event-row:hover h3 { color: var(--color-accent); }
.event-row-location {
  display: flex; align-items: center; gap: .35rem; margin: 0 0 .55rem;
  color: var(--color-text-secondary); font-size: .85rem;
}
.event-row-location .pin-icon { width: 14px; height: 14px; flex-shrink: 0; fill: none; stroke: var(--color-accent); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.event-row-excerpt {
  margin: 0 0 .7rem; color: var(--color-text-secondary); font-size: .87rem; line-height: 1.4;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.event-row-tags { display: flex; flex-wrap: wrap; gap: .4rem; }
.event-tag {
  display: inline-block; background: var(--color-secondary); color: var(--color-text-secondary);
  font-family: 'Rajdhani', sans-serif; font-weight: 600; letter-spacing: .03em; text-transform: uppercase;
  font-size: .68rem; padding: .25rem .6rem; border-radius: 20px;
}
.event-row-media {
  position: relative; width: 240px; aspect-ratio: 4/3; border-radius: 8px; overflow: hidden;
  background: var(--color-bg); flex-shrink: 0; align-self: center;
}
.event-row-media .rotor-img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Martins Feedback (Screenshot, 18.09.26): "Details" lag als Overlay auf
   dem Bild - stand dort unabhängig vom Text und wirkte, als gehöre er
   zur Diashow statt zum Termin. Jetzt Teil von .event-row-body, direkt
   unter Titel/Ort/Tags, wie ein normaler Button (kein Overlay mehr). */
.event-row-cta {
  display: inline-block; margin-top: .5rem; background: var(--color-button); color: #fff;
  font-family: 'Rajdhani', sans-serif; font-weight: 600; letter-spacing: .03em; text-transform: uppercase;
  font-size: .78rem; padding: .35rem .8rem; border-radius: 20px;
}
@media (max-width: 800px) {
  .event-row { grid-template-columns: auto 1fr; }
  .event-row-media { grid-column: 1 / -1; width: 100%; aspect-ratio: 16/9; }
}
@media (max-width: 520px) {
  .event-row { grid-template-columns: 1fr; text-align: center; }
  .event-row-date { flex-direction: row; gap: .4rem; align-self: center; }
  .event-row-location, .event-row-tags { justify-content: center; }
}

/* Slideshow-Punkte (Martins Feedback: bei mehreren Rotationsbildern muss
   "sauber erkennbar" sein, dass da mehrere Bilder rotieren, nicht nur
   irgendein einzelnes Zufallsbild) - main.js erzeugt/aktualisiert die Punkte
   nur, wenn ein .has-rotor-Element tatsächlich mehr als 1 Bild hat. */
.rotor-dots {
  position: absolute; left: 0; right: 0; bottom: .5rem; z-index: 3;
  display: flex; justify-content: center; gap: .3rem; pointer-events: none;
}
.rotor-dot { width: 6px; height: 6px; border-radius: 50%; background: rgba(255,255,255,.45); transition: background .25s ease, transform .25s ease; }
.rotor-dot.active { background: #fff; transform: scale(1.25); }

/* ---- Events-Seite: "Vergangene Events" (letzte 3) in derselben großen
   .panel-feature-Kachel wie das nächste Event auf der Startseite (Martins
   Wunsch) - hier in einem normalen 3-Spalten-.grid statt im Subgrid der
   Startseite, daher KEIN .home-panel-Vorfahre und die grid-row-Regeln von
   dort greifen hier bewusst nicht (siehe Kommentar oben bei .home-panels). */
.events-archive-link { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }

/* Video-Panel: eigener Block (statt einzelner <a>) spannt beide unteren
   Zeilen des subgrids (Feature+Liste als eine Fläche, da das Video keinen
   Feature/Liste-Split hat) und bekommt per .glow-card denselben Hover-Glow
   wie News/Events (Martins Feedback: fehlte bisher komplett). */
.video-feature { grid-row: 2 / span 2; }
.video-thumb { position: relative; display: block; aspect-ratio: 16/9; border-radius: 8px; overflow: hidden; margin-bottom: .8rem; background: var(--color-bg); }
.video-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.video-play {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-size: 2.4rem; color: #fff; background: rgba(0,0,0,.25); transition: background .2s;
}
.video-thumb:hover .video-play { background: rgba(0,0,0,.4); }
.video-embed { width: 100%; aspect-ratio: 16/9; border-radius: 8px; overflow: hidden; margin-bottom: .8rem; display: block; }

@media (max-width: 900px) { .home-panels { grid-template-columns: 1fr; } }

/* Martins Wunsch (19.09.26, "im Admin-Panel sind alle Buttons unterschiedlich
   hoch und breit"): ECHTE Ursachen per Playwright-Nachmessung gefunden -
   (1) HÖHE: die Regel hier setzte nie eine eigene line-height, wodurch ein
   <button> (Browser-Standard "normal", ~1.15) und ein <a class="btn"> (erbt
   body's line-height:1.55) UNTERSCHIEDLICH hoch wurden (38px vs. 44-46px
   gemessen) - selbes Markup ("Bearbeiten"/"Verwalten" sind <a>, "Löschen"/
   "Speichern" etc. sind <button>), aber optisch andere Höhe je nach
   Element-Typ. Fix: eigene line-height:1.2 fixiert die Höhe unabhängig vom
   Element. (2) BREITE: Ein-Wort-Buttons ("Löschen", "Speichern",
   "Entfernen", "Suchen", "Archivieren", ...) sind bei display:inline-block
   ohne Mindestbreite exakt so breit wie ihr Text - je nach Wortlänge lag das
   gemessen zwischen 90px ("Filtern") und 129px ("Archivieren"), sichtbar
   uneinheitlich im Admin-Bereich. Fix: min-width:8.6rem (=137.6px, per
   Messung mit allen im Adminbereich vorkommenden Ein-Wort-Button-Texten
   gewählt: > breitester Ein-Wort-Text, aber < kürzester vorkommender
   Mehrwort-Text "Datum ändern" bei 147px) - macht ALLE Ein-Wort-Buttons
   exakt gleich breit (getestet: 138px), OHNE mehrwortige Buttons zu
   beeinflussen (die sind durch ihren längeren Text ohnehin schon breiter
   als die Mindestbreite und bleiben unverändert, wie von Martin gewünscht -
   "Buttons mit mehreren Worten kannst du in der Breite so lassen"). Bewusst
   NICHT auf .btn-sm angewendet (siehe dortiger Kommentar) und NICHT auf die
   Kopfzeilen-Buttons .btn-admin/.btn-logout/.btn-auth (haben eigene,
   kleinere min-width:6.9rem, die wegen gleicher Spezifität + späterer
   Position im Stylesheet weiterhin gewinnt). */
.btn, button[type=submit] {
  display: inline-block; background: var(--color-button); color: #fff;
  border: none; padding: .65rem 1.3rem; border-radius: 6px; cursor: pointer;
  font-size: .95rem; line-height: 1.2; min-width: 8.6rem; text-align: center;
}
.btn:hover, button[type=submit]:hover { background: var(--color-button-hover); text-decoration: none; }
/* ECHTER BUG (gefunden 18.09.26 beim Nachmessen der Kopfzeilen-Buttons):
   .btn-sm (weiter oben, kurz nach .header-auth definiert) hat dieselbe
   Spezifität wie die .btn-Basisregel hier (je ein Klassenselektor) - weil
   .btn-sm im Stylesheet VOR .btn steht, gewann bei jedem Element mit BEIDEN
   Klassen (was ausnahmslos überall der Fall ist, z. B. "btn btn-sm btn-
   admin") immer .btn's GRÖSSERES Padding/Schriftgröße, nicht .btn-sm's
   kleinere Werte - .btn-sm hat dadurch faktisch nirgends auf der ganzen
   Seite gewirkt (Kopfzeilen-Buttons, YouTube-Link, Suchen-Button, Cookie-
   Banner "Verstanden", Branding-"Entfernen", Einstellungen-"Neu berechnen"
   waren alle unbeabsichtigt in der GROSSEN .btn-Größe statt der kleinen
   .btn-sm-Größe). Identischer Mechanismus wie einst bei .btn-gold/.btn-
   admin/.btn-logout (siehe dortiger Kommentar) - Regel hierher ans Ende
   verschoben statt !important zu erzwingen. */
/* min-width:0 setzt die neue Mindestbreite der Basis-.btn-Regel (siehe
   dortiger Kommentar, 19.09.26) für .btn-sm-Buttons zurück - .btn-sm ist die
   bewusst KLEINERE Variante (z.B. "Jetzt neu berechnen" in Einstellungen,
   Kopfzeilen-Suche), die sollen sich weiterhin am eigenen Text orientieren
   statt auf die große Ein-Wort-Buttonbreite gezwungen zu werden. */
.btn-sm { padding: .4rem .9rem; font-size: .85rem; min-width: 0; }
/* ECHTER BUG (gefunden 21.09.26 bei Martins Rückmeldung zu den Galerie-
   Buttons): .btn-secondary wird fast überall auf einen <button type="submit">
   angewendet (z.B. "Veröffentlichen", "Passwort setzen", "Ablehnen",
   "Verwerfen", "Datum ändern") - dort gewinnt IMMER die Basisregel oben
   (.btn, button[type=submit]), weil der Attribut-Selektor button[type=submit]
   eine höhere Spezifität hat als der reine Klassenselektor .btn-secondary,
   unabhängig von der Reihenfolge im Stylesheet. .btn-secondary hat dadurch
   praktisch nirgends wie gedacht (heller Rahmen, durchsichtiger Hintergrund)
   gewirkt, sondern sah aus wie ein ganz normaler (grüner) Haupt-Button -
   identischer Mechanismus wie einst bei .btn-delete (siehe dortiger
   Kommentar), dort aber schon mit genau diesem Muster (Selektor kombinieren
   statt !important) behoben - hier jetzt nachgezogen. */
.btn-secondary,
button[type=submit].btn-secondary { background: transparent; border: 1px solid var(--color-border); color: var(--color-text); }
.btn-secondary:hover,
button[type=submit].btn-secondary:hover { background: rgba(255, 255, 255, .08); }
/* Header-Auth-Buttons: gleiches Design wie .btn, unterschiedliche Farbe -
   Anmelden bleibt Grün (Standard-Buttonfarbe), Registrieren in Gold/Gelb. */
.btn-gold { background: var(--color-accent); color: #1a1a1a; }
.btn-gold:hover { background: var(--color-link-hover); color: #1a1a1a; }

/* Admin-Link + Abmelden in der Hauptnav (Martins Wunsch: "gleiche Größe/
   Form/Aussehen wie Anmelden/Registrieren", also selbst .btn.btn-sm +
   Farb-Modifikator, exakt wie .btn-gold oben) - Regel MUSS nach der Basis-
   .btn-Regel stehen: bei gleicher Spezifität (je ein Klassenselektor)
   gewinnt sonst die spätere Quellreihenfolge und .btn's Grün überschreibt
   die Admin-/Logout-Farbe wieder (identischer Bug wie einst bei .btn-gold,
   siehe v1.1.1-Fix - hier mit den seit v1.1.2 neuen .btn-admin/.btn-logout-
   Klassen erneut aufgetreten und jetzt genauso behoben: Regel ans Ende
   verschoben statt !important zu erzwingen).
   Nicht-aktiver Zustand: Gold/Akzentfarbe (wie der Rest der Admin-Panel-
   Schrift). Aktiver Zustand (auf /admin selbst): Button-Grün mit gelb
   unterstrichener, gelber Schrift - deutlich vom Registrieren-Gold
   unterscheidbar, aber erkennbar "aktiv". */
/* Martins Feedback (Screenshot 18.09.26, Nachtrag 18.09.26): "Admin" und
   "Abmelden" sollen exakt gleich groß wirken, obwohl "Abmelden" deutlich
   mehr Zeichen hat - bisher rein auto-width (Padding + Textlänge), dadurch
   sichtbar unterschiedlich breit. Gemeinsame Mindestbreite + zentrierter
   Text löst das unabhängig vom tatsächlichen Wortlaut.
   NACHTRAG: der ursprüngliche Wert (7rem/112px) war knapp zu klein - er lag
   UNTER der tatsächlichen Textbreite von "Abmelden" (~117px bei .btn-sm-
   Padding), wodurch "Abmelden" trotz min-width weiterhin breiter blieb als
   "Admin" (min-width ist eine Untergrenze, kein Deckel). Per Messung
   (Playwright) ermittelt: "Registrieren" ist mit ~134px das längste der 4
   Kopfzeilen-Auth-Wörter - neuer gemeinsamer Wert 8.5rem/136px deckt alle
   ab. Gleichzeitig auf Anmelden/Registrieren ausgeweitet (neue Klasse
   .btn-auth, siehe templates/header.php) - Martins Screenshot zeigte beide
   Nav-Zustände (eingeloggt/ausgeloggt) nebeneinander, "gleich groß" bezieht
   sich auf die Kopfzeilen-Buttons insgesamt, nicht nur auf Admin/Abmelden. */
/* ECHTER BUG (Martins Screenshot 18.09.26, weiterer Nachtrag): trotz der
   obigen Absicht ("Admin/Abmelden exakt wie .btn.btn-sm") wurde das nie
   ganz erreicht - .main-nav a (Zeile 55) ist Element+Klasse (Spezifität
   höher als eine einzelne Klasse) und überschrieb padding/font-size von
   .btn-sm für genau diese zwei Buttons, weil sie zugleich <li>-Kinder von
   .main-nav sind (Anmelden/Registrieren stehen dagegen außerhalb der Nav
   in .header-auth und blieben unberührt). Dadurch waren Admin/Abmelden
   sichtbar NIEDRIGER (.25rem statt .4rem Padding) und in anderer
   Schriftgröße (.92rem statt .85rem, dazu andere letter-spacing/
   Groß-/Kleinschreibung) als Anmelden/Registrieren. .main-nav a.btn hat als
   Element+2-Klassen-Selektor genug Spezifität, um das wieder auf den
   .btn.btn-sm-Wert zurückzusetzen. Gleichzeitig auf Martins Wunsch die
   Breite etwas verkleinert: Padding von .9rem auf .7rem, min-width dafür
   passend neu vermessen (siehe unten). */
.main-nav a.btn {
  padding: .4rem .7rem; font-size: .85rem; letter-spacing: .02em; text-transform: none;
}
/* .btn-admin/.btn-logout (Admin-/Abmelden-Links direkt in der Hauptnav)
   sind seit v1.3.4 obsolet - Admin Panel/Abmelden stehen seither im
   Profil-Dropdown (.nav-user-admin/.nav-user-logout, siehe weiter oben)
   statt als eigene Kopfzeilen-Buttons. .btn-auth bleibt (Anmelden/
   Registrieren nutzen es weiterhin, siehe templates/header.php). */
.btn-auth { padding-left: .7rem; padding-right: .7rem; min-width: 6.9rem; text-align: center; }

/* Martins Wunsch: alle echten "Löschen"-Buttons im Admin-Bereich rot statt
   grün, Größe unverändert. Diese sind fast durchgehend <button type="submit">
   (nicht <a>), daher reicht ein einfacher Klassenselektor (Spezifität 0,1,0)
   NICHT aus, um button[type=submit] (Spezifität 0,1,1) zu schlagen - selber
   Bug-Mechanismus wie einst bei .btn-gold/.btn-admin/.btn-logout, hier aber
   mit einer Basis-Regel, die selbst schon eine Attribut-Regel ist. Lösung:
   zusätzlicher button[type=submit].btn-delete-Selektor mit höherer
   Spezifität als die Basisregel (statt auf Quellreihenfolge allein zu
   vertrauen); .btn-delete allein bleibt für den seltenen Fall eines
   <a class="btn-delete"> nutzbar. Farbe: dieselbe wie .btn-logout
   (var(--color-logout)), keine neue Design-Einstellung nötig. */
.btn-delete,
button[type=submit].btn-delete { background: var(--color-logout, #b23a3a); color: #fff; }
.btn-delete:hover,
button[type=submit].btn-delete:hover { filter: brightness(1.12); color: #fff; }

/* Wiederverwendbarer Container für mehrere Aktions-Buttons/-Formulare
   nebeneinander (z.B. "Verwalten"/"Veröffentlichen"/"Löschen" in einer
   Tabellenspalte, Martins Rückmeldung 21.09.26: "auch ein wenig Abstand
   zwischen den Buttons wäre gut"). Ersetzt die bisherige Lösung über
   einzelne style="display:inline;margin-left:..."-Attribute je Formular:
   die haben bei mehr als zwei Buttons in einer schmalen Spalte auf
   Zeilenumbruch (min-width:8.6rem je Button) keinen sichtbaren Abstand
   mehr erzeugt, weil der horizontale margin-left beim Umbruch auf eine
   neue Zeile keine Lücke *zwischen den Zeilen* ergibt. flex-wrap mit gap
   sorgt für Abstand in beide Richtungen, egal ob die Buttons nebeneinander
   passen oder umbrechen. */
.btn-row { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem; }

/* ECHTER BUG gefixt (Martins Feedback 27.09.26, Screenshot der Shop-
   Produkt-Stammdaten: Eingabefelder/Checkboxen völlig unformatiert, Text
   lief einfach in einer Zeile durch): die Stammdaten-/Farben-/Varianten-/
   Personalisierungs-Abschnitte auf admin/shop_product_detail.php stehen
   NICHT in einem echten <form>-Tag (siehe Datei-Kopfkommentar - alle
   Felder hängen sich per form="shopProductForm" von überall auf der Seite
   an, damit die Bilder-Sektion mit ihrem eigenen Upload-<form> dazwischen
   passt, ohne verschachtelte <form>-Tags). Der Selektor "form.stack ..."
   verlangt aber zwingend ein <form>-Element mit dieser Klasse - ein
   <div class="stack"> erfüllt das NICHT, weshalb die komplette
   Label/Eingabefeld-Formatierung dort ins Leere lief. Neue Klasse
   ".stack-fieldset" für genau diesen Fall (Label/Eingabefeld-Liste OHNE
   umschließendes <form>), dieselbe Formatierung wie form.stack, aber
   klassenbasiert statt elementbasiert - bewusst eine eigene neue Klasse
   statt "form.stack" einfach zu ".stack" abzuschwächen, damit die
   bestehenden ca. 50 echten "<form class="stack">"-Vorkommen im Projekt
   unangetastet bleiben (kein Risiko für Nebenwirkungen anderswo). */
form.stack label, .stack-fieldset label { display: block; margin-top: 1rem; font-size: .9rem; color: var(--color-text-secondary); }
form.stack input, form.stack select, form.stack textarea,
.stack-fieldset input, .stack-fieldset select, .stack-fieldset textarea {
  width: 100%; margin-top: .3rem; padding: .6rem; border-radius: 5px;
  border: 1px solid var(--color-border); background: var(--color-bg); color: var(--color-text);
}
form.stack .actions, .stack-fieldset .actions { margin-top: 1.4rem; }
/* Empfohlene-Auflösung-Hinweis unter Bild-Upload-Feldern (Martins Wunsch
   18.09.26, News/Event-Formulare: "damit Bilder perfekt hochgeladen werden
   können und nicht zwingend der Pillarbox-Effekt benutzt werden muss") -
   bewusst als eigene, etwas kleinere/dezentere Klasse statt Wiederverwendung
   von .card-meta, damit sie unabhängig von Kartenkontexten überall in
   Admin-Formularen einsetzbar ist. */
form.stack .field-hint, .stack-fieldset .field-hint { display: block; margin-top: .35rem; font-size: .78rem; color: var(--color-text-secondary); opacity: .85; }
/* Cloudflare-Turnstile-CAPTCHA (siehe lib/Captcha.php). Das Widget selbst
   (bzw. bei einem Fehler Cloudflares eigene "Problembehebung"-Box) ist
   fremder, von uns nicht gestaltbarer Inhalt (eigenes Script/iframe) -
   dieser Wrapper sorgt nur dafür, dass er sich sauber ins Formular einfügt
   (Abstand nach oben/unten, nicht auf voller Breite gequetscht) statt direkt
   an Formularfeldern oder der Fußzeile zu kleben (Martins Feedback
   22.09.26, Screenshot der Fehlerbox: "passt grafisch auch nicht direkt"). */
.captcha-wrap { margin: 1.2rem 0; max-width: 300px; }

table.admin-table { width: 100%; border-collapse: collapse; margin-top: 1rem; }
table.admin-table th, table.admin-table td { padding: .55rem .7rem; border-bottom: 1px solid var(--color-border); text-align: left; font-size: .9rem; }
table.admin-table th { color: var(--color-accent); }
/* Martins Fund (27.09.26): der Rufname (Benutzer-Spalte) brach in Bestell-Log/
   Aktivitätsprotokoll mitten im Wort um, weil eine lange "Details"-Zelle in
   derselben Zeile den restlichen Spalten (bei table-layout:auto) den Platz
   wegnimmt - besonders auffällig beim Kopieren/Betrachten außerhalb des
   eigentlichen Browsers (Martin: "zumindest bei Thunderbird"). Rufname/
   Benutzername-Zellen bekommen deshalb explizit kein Umbruch mehr. */
table.admin-table td.nowrap { white-space: nowrap; }

/* ---- Mobile-Stapelung für ALLE .admin-table-Tabellen (Martin, 21.09.26,
   Screenshot bei "Meine Bestellungen": Tabelle lief auf dem Handy seitlich
   aus dem Rahmen, Status-Badge/Ansehen-Button wurden abgeschnitten;
   21.09.26 auf Nachfrage geprüft und auf ALLE Admin-Tabellen ausgeweitet,
   da dieselbe Ursache überall zutrifft, wo mehr als 2-3 Spalten mit fixer
   Breite nebeneinanderstehen). Ab 640px wird die Kopfzeile ausgeblendet und
   jede Zeile zu einem Kartenblock; die Beschriftung jeder Zelle kommt per
   data-label-Attribut vor den Wert (siehe ::before unten) - das
   data-label wird NICHT mehr von Hand in jeder .php-Datei gepflegt,
   sondern automatisch per JS aus der Kopfzeile übernommen (siehe
   public/js/main.js, Abschnitt "Tabellen auf dem Handy") - eine Tabelle
   bekommt diese mobile Ansicht also allein durch die Klasse .admin-table,
   ganz ohne Zusatzaufwand pro Seite. Die Kopfzeile ist hier - wie überall
   im Projekt - eine normale <tr><th> Zeile ohne <thead>, daher :first-child
   statt eines thead-Selektors. Zeilen mit einer über mehrere Spalten
   zusammengefassten Zelle (z.B. "Keine Bestellungen vorhanden." oder eine
   Versand/Gesamtsumme-Zeile) bekommen laut JS bewusst KEIN data-label -
   dafür braucht es hier keine Extra-Regel, eine leere ::before-Angabe
   erzeugt ohnehin keinen sichtbaren Inhalt. */
@media (max-width: 640px) {
  table.admin-table, table.admin-table tbody, table.admin-table tr, table.admin-table td { display: block; width: 100%; }
  table.admin-table tr:first-child { display: none; }
  table.admin-table tr { margin-bottom: 1rem; border: 1px solid var(--color-border); border-radius: 8px; padding: .2rem .7rem; }
  table.admin-table td { display: flex; justify-content: space-between; align-items: center; gap: 1rem; text-align: right; }
  table.admin-table td:last-child { border-bottom: none; }
  table.admin-table td::before { content: attr(data-label); font-weight: 700; color: var(--color-accent); text-align: left; }
}

.badge { display: inline-block; padding: .15rem .55rem; border-radius: 12px; font-size: .75rem; background: var(--color-secondary); }

/* ---- Bestell-Timeline beim Kunden (Martins Wunsch 20.09.26: "eine Art
   Timeline beim Kunden anzeigen: Bestellung aufgegeben/bezahlt/versendet/
   abgeschlossen") - Schritte kommen aus Shop::timelineSteps(), die je nach
   Versandart (Versand vs. Abholung/Übergabe) den "Versendet"-Schritt
   weglässt oder einschließt, siehe dortiger Kommentar. Reine CSS-Lösung
   ohne JS: die Verbindungslinie zwischen zwei Punkten läuft von der Mitte
   des eigenen Schritts (left:50%) über die volle Breite des Schritts
   (width:100%) bis zur Mitte des nächsten - funktioniert zuverlässig, weil
   alle Schritte per flex:1 gleich breit sind. */
.order-timeline { display: flex; list-style: none; margin: 1.2rem 0 1.6rem; padding: 0; }
.order-timeline-step { flex: 1; position: relative; text-align: center; font-size: .78rem; color: var(--color-text-secondary); }
.order-timeline-step:not(:last-child)::after {
  content: ""; position: absolute; top: 8px; left: 50%; width: 100%; height: 2px;
  background: var(--color-border); z-index: 0;
}
.order-timeline-step.done:not(:last-child)::after { background: var(--color-accent); }
.order-timeline-dot {
  display: block; width: 18px; height: 18px; margin: 0 auto .4rem; border-radius: 50%;
  background: var(--color-card-bg); border: 2px solid var(--color-border); position: relative; z-index: 1;
}
.order-timeline-step.done .order-timeline-dot { background: var(--color-accent); border-color: var(--color-accent); }
.order-timeline-label { display: block; }
.order-timeline-step.current .order-timeline-label { color: var(--color-accent); font-weight: 700; }
@media (max-width: 480px) {
  .order-timeline-label { font-size: .68rem; }
}

/* Allgemeiner, wiederverwendbarer Trennstrich zwischen zwei Abschnitten
   innerhalb einer Seite (z.B. Events: "Kommende" / "Vergangene") - exakt
   dieselbe Optik wie die Sponsoren-Zeilen-Trenner unten, nur unter einem
   seitenneutralen Namen statt "sponsor-*". */
.section-break { margin-top: 1.4rem; padding-top: 1.4rem; border-top: 1px solid var(--color-border); }

/* ---- Sponsoren (§5) ---------------------------------------------------- */
/* Optische Trennung: zarte Linien zwischen Header/Sponsoren und
   Sponsoren/News-Events-Video (Martins ASCII-Skizze). */
.sponsor-section { margin-top: 1.4rem; padding-top: 1.4rem; border-top: 1px solid var(--color-border); }
.sponsor-section + section { margin-top: 1.4rem; padding-top: 1.4rem; border-top: 1px solid var(--color-border); }
/* Sponsoren-Zeile im selben 3-Spalten-Raster wie die Panel-Reihe darunter
   ausgerichtet (Martins Wunsch: linke Spalte "Unsere Sponsoren" größer/
   fetter, mittlere/rechte Spalte je ein Sponsor-Logo, exakt deckungsgleich
   mit "Aktuelle News"/"Kommende Events"/"Aktuelles Video" darunter).
   WICHTIG: nutzt bewusst NICHT die generische .grid.grid-3 (die hat
   gap:1.2rem), sondern dieselben Werte wie .home-panels (gap:1.6rem,
   gleicher 900px-Umbruch) - sonst liegen Trennstrich/Sponsor-Logo nicht
   exakt über den Spalten der Panels darunter. */
.sponsor-bar { position: relative; display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.6rem; align-items: center; padding: .6rem 0; }
/* Feste Trennlinien (Martins Feedback v1.1.3-Review): früher hing die
   Trennlinie am border-left der jeweiligen .sponsor-logo-link - dadurch
   a) saß sie am Rand statt in der Mitte der Lücke, und b) verschwand sie
   ganz, sobald ein Sponsor-Slot leer ist (die zweite Spalte wird dann
   überhaupt nicht gerendert). Jetzt zwei vom Inhalt unabhängige
   Pseudo-Elemente, exakt in der Mitte der jeweiligen 1.6rem-Lücke
   positioniert - damit liegen sie IMMER exakt zwischen den beiden
   Kacheln aus der Panel-Reihe darunter (identisches 3-Spalten/Gap-Raster). */
.sponsor-bar::before, .sponsor-bar::after {
  content: ''; position: absolute; top: 0; bottom: 0; width: 1px; background: var(--color-border);
}
.sponsor-bar::before { left: calc((100% - 3.2rem) / 3 + .8rem); }
.sponsor-bar::after { left: calc((100% - 3.2rem) / 3 * 2 + 2.4rem); }
@media (max-width: 900px) { .sponsor-bar { grid-template-columns: 1fr; } .sponsor-bar::before, .sponsor-bar::after { display: none; } }
.sponsor-label-cell { display: flex; align-items: center; height: 100%; }
.sponsor-label {
  font-family: 'Rajdhani', 'Segoe UI', sans-serif; font-weight: 700; letter-spacing: .03em;
  text-transform: uppercase; font-size: clamp(1.5rem, 2.6vw, 2.1rem); line-height: 1.15; color: var(--color-text);
}
.sponsor-logo-link {
  display: flex; align-items: center; justify-content: center; height: 100%;
}
/* Sponsor-Logo ca. 1,5x größer als zuvor (86px -> 130px), zusätzlich per
   Admin-Regler (admin/sponsoren.php, 50-500%) skalierbar. */
.sponsor-bar img { height: calc(130px * var(--sponsor-scale, 1)); max-width: 100%; object-fit: contain; filter: grayscale(20%); transition: filter .2s; }
.sponsor-bar a:hover img { filter: none; }
@media (max-width: 900px) {
  .sponsor-label-cell { justify-content: center; text-align: center; }
  .sponsor-logo-link { border-top: 1px solid var(--color-border); padding-top: 1rem; }
  /* Martins Feedback 18.09.26: Sponsor-Logo wirkte auf Mobil winzig, weil
     die Größe (wie am Desktop) über eine FESTE HÖHE lief (130px, ab 600px
     sogar nur noch 70px) statt über die Breite - bei den meist breiten/
     querformatigen Sponsor-Logos blieb dadurch viel ungenutzte Breite in
     der (hier bereits einspaltigen, siehe oben) vollen .wrap-Breite
     übrig, obwohl die Kachel selbst schon genauso breit ist wie die News-
     Kachel darunter. Jetzt bestimmt stattdessen die Breite die Größe -
     100% der Kachelbreite, exakt deckungsgleich mit der News-Kachel
     darunter (Martins Wunsch), der Admin-Regler (50-500%) kann das Logo
     weiterhin verkleinern, aber hier bewusst nicht über die Kachelbreite
     hinaus vergrößern (würde sonst überlaufen). max-height als Schutz
     gegen extrem hochformatige Logo-Uploads, die sonst bei 100% Breite
     unangemessen hoch würden. */
  .sponsor-bar img { width: min(100%, calc(100% * var(--sponsor-scale, 1))); height: auto; max-height: 220px; }
}

/* ---- Galerie-Kacheln mit Hover-/Tap-Bildwechsel (§10-§12) -------------- */
.gallery-tile {
  position: relative; aspect-ratio: 4/3; border-radius: 10px; overflow: hidden;
  border: 1px solid var(--color-border); cursor: pointer; background: var(--color-card-bg);
}
.gallery-tile img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  opacity: 0; transition: opacity 1.6s ease-in-out;
}
.gallery-tile img.active { opacity: 1; }
.gallery-tile .tile-label {
  position: absolute; left: 0; right: 0; bottom: 0; padding: .6rem .8rem;
  background: linear-gradient(transparent, rgba(0,0,0,.75)); color: #fff; font-family: 'Rajdhani', sans-serif;
  display: flex; flex-direction: column; gap: .1rem;
}
@media (prefers-reduced-motion: reduce) {
  .gallery-tile img { transition: none; }
}

/* ---- Galerie-Übersichtsseite (/galerie): eigene Kachel mit Glow +
   verzögertem Flip + Mosaik-Diashow der eigenen Galeriebilder, statt des
   sofortigen Bildwechsels der Startseiten-Kacheln oben. ------------------ */
.gallery-flip-tile { position: relative; aspect-ratio: 4/3; border-radius: 10px; overflow: hidden; border: 1px solid var(--color-border); background: var(--color-card-bg); display: block; }
.gallery-flip-tile .rotor { position: absolute; inset: 0; }
.gallery-flip-tile .tile-label {
  position: absolute; left: 0; right: 0; bottom: 0; padding: .6rem .8rem; z-index: 2;
  background: linear-gradient(transparent, rgba(0,0,0,.75)); color: #fff; font-family: 'Rajdhani', sans-serif; pointer-events: none;
  display: flex; flex-direction: column; gap: .1rem;
}
/* Spielname/Datum/Location auf der Galerie-Kachel (Martins Feedback
   v1.1.3-Review) - der Spielname soll wie die Jahreszahl im Trennstrich
   hervorstechen (Rajdhani, fett, Akzentfarbe), nur etwas kleiner passend
   zur Kachelgröße. */
.tile-game-name {
  font-family: 'Rajdhani', sans-serif; font-weight: 700; font-size: 1.1rem;
  letter-spacing: .03em; color: var(--color-accent); line-height: 1.25;
}
.tile-meta { font-size: .78rem; color: rgba(255,255,255,.85); line-height: 1.3; }

/* ---- Team-Kacheln, an die Galerie-Kacheln angeglichen (Martins Wunsch,
   18.09.26): Rufname genauso hervorgehoben wie der Spielname auf den
   Galerie-Kacheln bzw. die Jahreszahl im Trennstrich (Rajdhani/fett/
   Akzentfarbe) - hier etwas größer, da der Platz im .card-body (statt auf
   einem Bild-Overlay) mehr hergibt. */
.team-callsign {
  font-family: 'Rajdhani', sans-serif; font-weight: 700; font-size: 1.25rem;
  letter-spacing: .03em; color: var(--color-accent);
}

/* ---- Galerien-Karussell (Startseite): horizontal, scroll-snap, mit
   Pfeil-Navigation und Paginierungs-Punkten ------------------------------
   Bugfix: scroll-snap-type war "mandatory" - das ist in Kombination mit
   programmatischem scrollBy()/scrollIntoView() (Pfeil-Klicks) v.a. in
   Safari/iOS dafür bekannt, den Scroll zu blockieren bzw. sofort wieder
   zurückzuschnappen ("kann nicht durchklicken"). "proximity" snapt weiterhin
   sanft beim manuellen Scrollen/Wischen, blockiert aber nicht den von den
   Pfeil-Buttons ausgelösten Scroll. AUSSERDEM hatte .carousel-slide
   touch-action:pan-y pinch-zoom gesetzt - das untersagt dem Browser
   AUSGERECHNET auf der Kachel selbst (die praktisch den ganzen Track
   ausfüllt) horizontales Wischen, wodurch Touch-Swipe in JEDEM Browser
   nicht funktionieren konnte. Jetzt pan-x pan-y (beide Richtungen erlaubt,
   der Browser entscheidet selbst anhand der Wischrichtung). */
.gallery-carousel { position: relative; margin-top: .2rem; }
.carousel-track {
  display: flex; gap: 1.2rem; overflow-x: auto; scroll-snap-type: x proximity;
  padding: .2rem .1rem .6rem; scrollbar-width: none;
  -webkit-overflow-scrolling: touch; touch-action: pan-x;
}
.carousel-track::-webkit-scrollbar { display: none; }
.carousel-slide { flex: 0 0 auto; width: 270px; scroll-snap-align: start; touch-action: pan-x pan-y; }
@media (max-width: 700px) { .carousel-slide { width: 78vw; } }
.carousel-arrow {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 3;
  width: 40px; height: 40px; border-radius: 50%; border: 1px solid var(--color-border);
  background: var(--color-card-bg); color: var(--color-text); font-size: 1.1rem;
  display: flex; align-items: center; justify-content: center; cursor: pointer;
  transition: background .2s, border-color .2s;
}
.carousel-arrow:hover { background: var(--color-button); border-color: var(--color-button); color: #fff; }
.carousel-prev { left: -18px; }
.carousel-next { right: -18px; }
.carousel-arrow[disabled] { opacity: .35; cursor: default; }
.carousel-arrow[disabled]:hover { background: var(--color-card-bg); border-color: var(--color-border); color: var(--color-text); }
@media (max-width: 700px) {
  .carousel-arrow { width: 32px; height: 32px; font-size: .95rem; }
  .carousel-prev { left: 2px; }
  .carousel-next { right: 2px; }
}
.carousel-dots { display: flex; justify-content: center; gap: .5rem; margin-top: .4rem; }
.carousel-dots button {
  width: 8px; height: 8px; border-radius: 50%; border: none; padding: 0;
  background: var(--color-border); cursor: pointer;
}
.carousel-dots button.active { background: var(--color-accent); width: 22px; border-radius: 4px; transition: width .2s, background .2s; }

/* ---- Lightbox (Orden, Galerie) ----------------------------------------- */
.lightbox-overlay {
  position: fixed; inset: 0; background: var(--color-overlay); display: none;
  align-items: center; justify-content: center; z-index: 200; padding: 2rem;
}
.lightbox-overlay.open { display: flex; }
.lightbox-overlay img { max-width: 90vw; max-height: 80vh; border-radius: 8px; cursor: pointer; }
.lightbox-close { position: absolute; top: 1.2rem; right: 1.5rem; color: #fff; font-size: 2rem; cursor: pointer; background: none; border: none; }
/* Vor-/Zurück-Pfeile zum Durchblättern mehrerer Bilder (Martins Wunsch,
   23.09.26) - per Default versteckt, main.js blendet sie nur ein, wenn die
   geöffnete Bildgruppe tatsächlich mehr als ein Bild enthält (z.B. NICHT bei
   der einzelnen Orden-Bild-Ansicht im Profil). */
.lightbox-nav {
  display: none; align-items: center; justify-content: center;
  position: absolute; top: 50%; transform: translateY(-50%);
  color: #fff; font-size: 2.2rem; line-height: 1; cursor: pointer; background: rgba(0,0,0,.35);
  border: none; border-radius: 50%; width: 2.6rem; height: 2.6rem; padding: 0;
}
.lightbox-nav:hover { background: rgba(0,0,0,.55); }
.lightbox-prev { left: .8rem; }
.lightbox-next { right: .8rem; }
@media (max-width: 640px) {
  .lightbox-nav { width: 2.2rem; height: 2.2rem; font-size: 2.2rem; }
}

/* ---- Teamprofile / Orden ------------------------------------------------ */
/* Avatar sitzt jetzt mit klarem Abstand UNTER dem Banner (page-header-
   Klassen, siehe pages/profile.php), statt mit negativem Margin hineinzu-
   ragen und dadurch verdeckt zu wirken. */
.profile-avatar-row { margin-top: 1.3rem; padding-left: 1rem; }

/* ---- Augen-Overlay: liegt über JEDEM bannerartigen Bereich -------------- */
/* Das Augen-PNG ist selbst schon im Format des Banners angelegt und bis auf
   die Augen komplett transparent (Martins Vorgabe) - es soll daher die
   GESAMTE Banner-Fläche abdecken (wie das Hintergrundbild selbst), nicht
   nur eine kleine Ecke. object-fit:cover verhält sich damit genauso wie
   das background-size:cover des Bannerfotos darunter. */
.banner-eyes-overlay {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  width: 100%; height: 100%; object-fit: cover;
}
/* Zwei getrennt einstellbare Transparenzwerte (Martins Feedback v1.1.3-
   Review): Hero Header (Startseite) und Seiten-Header (alle übrigen
   Seiten inkl. Profil/Detailseiten) sollen unabhängig voneinander
   eingestellt werden können, statt einer gemeinsamen Opacity für beide -
   je nach umgebendem Container (.hero-banner bzw. .page-header) greift
   die passende CSS-Variable (admin/branding.php, zwei separate Regler). */
.hero-banner .banner-eyes-overlay { opacity: var(--eyes-overlay-opacity-hero, 1); }
.page-header .banner-eyes-overlay { opacity: var(--eyes-overlay-opacity-page, 1); }
/* Martins Wunsch (18.09.26): deutlich größer, "man soll die Leute schon
   sehen" - statt fest 100px jetzt zwischen ~140px (schmale Handys) und
   240px (Desktop) skalierend, klar über eine reine Verdopplung hinaus. */
.profile-avatar { width: clamp(140px, 22vw, 240px); height: clamp(140px, 22vw, 240px); border-radius: 50%; border: 5px solid var(--color-bg); object-fit: cover; }
.profile-avatar.veteran { filter: grayscale(85%) brightness(.85); }
.orden-grid { display: flex; flex-wrap: wrap; gap: .6rem; }
.orden-grid img { width: 56px; height: 56px; object-fit: contain; background: var(--color-card-bg); border-radius: 8px; padding: 6px; border: 1px solid var(--color-border); cursor: pointer; }

/* ---- "Auf Fotos markiert" - durchlaufendes Karussell im Profil-Footer --
   Martins Wunsch (v1.1.2): über die volle Headerbreite, kein "Blinken"
   (Opacity-Crossfade der Vorversion) mehr, sondern ein tatsächlich
   durchfahrendes Karussell. Reine CSS-Marquee-Animation (kein JS nötig,
   funktioniert dadurch zuverlässig in jedem Browser): die Fotoliste wird
   serverseitig verdoppelt (pages/profile.php) und per translateX(-50%) im
   Kreis geschoben, wodurch der Loop nahtlos wirkt. Läuft in einer .wrap
   (siehe pages/profile.php), dadurch exakt auf Höhe der Kacheln/des
   Headers ausgerichtet. */
.profile-fotos-footer { margin-top: 1.5rem; padding-top: 1.2rem; border-top: 1px solid var(--color-border); }
.profile-fotos-marquee { overflow: hidden; border-radius: 8px; }
/* Martins Wunsch (23.09.26, iPad-Meldung: "in den Teammember-Profilen
   verschwinden im Karussell unten die Bilder"): bekanntes iOS-Safari-
   Problem bei dauerhaft laufenden CSS-transform-Animationen mit vielen
   Bildern - WebKit gibt die Kompositions-Ebene betroffener Bilder unter
   Speicherdruck zwischenzeitlich frei und zeichnet sie nicht immer
   rechtzeitig neu, was wie "verschwundene" Bilder aussieht. translateZ(0)
   erzwingt eine eigene, stabile Kompositions-Ebene für die gesamte Leiste
   statt pro Bild einzeln - der in Fachkreisen gängige Fix für genau dieses
   WebKit-Verhalten. Zusätzlich wird die Bilderzahl weiter unten in
   pages/profile.php jetzt gedeckelt (siehe dortiger Kommentar) - weniger
   gleichzeitig dekodierte Bilder in einer endlos laufenden Animation
   senken den Speicherdruck zusätzlich. */
.profile-fotos-track {
  display: flex; gap: .6rem; width: max-content; animation-name: pmc-photo-marquee;
  animation-timing-function: linear; animation-iteration-count: infinite;
  -webkit-transform: translateZ(0); transform: translateZ(0);
  -webkit-backface-visibility: hidden; backface-visibility: hidden;
}
.profile-fotos-track img { height: 160px; width: auto; border-radius: 8px; object-fit: cover; flex-shrink: 0; display: block; }
@keyframes pmc-photo-marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@media (max-width: 600px) { .profile-fotos-track img { height: 100px; } }
@media (prefers-reduced-motion: reduce) { .profile-fotos-track { animation: none; } }

/* ---- Spielortkarte (§22-§28, nicht interaktiv) -------------------------- */
/* Martins Wunsch: Karte mittig auf der Seite statt linksbündig. */
.map-section { text-align: center; }
.map-image-wrap { position: relative; width: 100%; max-width: 900px; margin: 0 auto; }
.static-map { width: 100%; border-radius: 10px; border: 1px solid var(--color-border); pointer-events: none; display: block; }
.map-panel {
  background: var(--color-card-bg); border: 1px solid var(--color-border); border-radius: 12px;
  padding: 1.2rem; margin: 1rem auto 0; max-width: 940px;
}

/* ---- Pin-Tooltip auf der Spielortkarte (Martins Wunsch, 18.09.26):
   Spielfeldname/Adresse/Veranstalter beim Hovern/Fokussieren eines Pins.
   Positionen kommen aus Map::points() (Prozentwerte, dieselben Koordinaten
   wie im PNG selbst) - der Hotspot ist bewusst etwas größer als der
   sichtbare Pin und leicht nach oben versetzt (die Pin-Spitze zeigt exakt
   auf den Punkt, die Grafik selbst liegt darüber), damit er auch auf
   kleinen Bildschirmen gut trifft. */
.map-marker {
  position: absolute; width: 30px; height: 30px; margin: -22px 0 0 -15px;
  background: none; border: 0; padding: 0; cursor: pointer;
}
.map-tooltip {
  position: absolute; left: 50%; bottom: calc(100% + 6px); transform: translateX(-50%) translateY(4px);
  display: flex; flex-direction: column; gap: .1rem; min-width: 160px; max-width: 240px;
  background: var(--color-card-bg); border: 1px solid var(--pin-color, var(--color-border)); border-radius: 8px;
  padding: .5rem .7rem; box-shadow: 0 10px 30px rgba(0,0,0,.5);
  font-size: .82rem; line-height: 1.4; color: var(--color-text); text-align: left;
  opacity: 0; visibility: hidden; pointer-events: none; transition: opacity .15s ease, transform .15s ease;
  z-index: 5; white-space: normal;
}
/* Martins Wunsch (19.09.26): Popup öffnet in derselben Farbe wie die
   zugehörige Pin-Nadel (--pin-color kommt inline pro Marker aus
   Map::points(), siehe pages/about.php) - passt sich dadurch automatisch
   an, sobald Martin im Admin-Bereich eine Kartenmarker-Farbe ändert. */
.map-tooltip strong { color: var(--pin-color, var(--color-accent)); font-family: 'Rajdhani', sans-serif; font-size: .92rem; }
.map-tooltip span { color: var(--color-text-secondary); }
.map-marker:hover .map-tooltip, .map-marker:focus-visible .map-tooltip {
  opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0);
}
.map-legend { display: flex; flex-wrap: wrap; justify-content: center; gap: .5rem 1.6rem; margin-top: 1rem; font-size: .85rem; color: var(--color-text-secondary); }
/* Martins Wunsch (18.09.26, "Icons passen nicht mit denen zusammen, die auf
   der Karte verwendet wurden"): die Legende zeigt jetzt dieselben, mit
   Map::rebuild() erzeugten und eingefärbten Icon-PNGs wie die Karte selbst
   (siehe Map::legendIconUrl(), lib/Map.php) statt generischer CSS-Formen -
   ein reiner CSS-Nachbau (z.B. per mask-image) kann die eigentliche
   pixelweise Einfärbung (loadTintedIcon()) nicht originalgetreu abbilden. */
.legend-icon { height: 20px; width: auto; margin-right: .35rem; vertical-align: middle; }
/* .legend-dot bleibt als Fallback erhalten für den Fall, dass noch gar
   keine Karte gebaut wurde (frisch installierte Website ohne Galerien) und
   Map::legendIconUrl() daher noch keine Datei liefert. */
.legend-dot { display: inline-block; width: 12px; height: 12px; border-radius: 50%; margin-right: .35rem; vertical-align: middle; background: var(--color-marker, var(--color-accent)); }
.legend-dot.legend-home { border-radius: 3px; }

/* ---- Über uns: neues + altes Logo als Textfluss (v1.1.2, Martins Wunsch):
   neues Logo fließt links neben dem ersten Absatz, altes Logo rechts neben
   dem letzten Absatz - klassisches Bild-im-Textfluss statt separat über/
   unter dem ganzen Block. clearfix auf dem Container, damit der umgebende
   Abschnitt die gefloateten Bilder korrekt mit umschließt. -------------- */
.about-intro-text { width: 100%; }
.about-intro-text::after { content: ""; display: table; clear: both; }
.about-logo { display: block; height: auto; object-fit: contain; }
.about-logo-new { float: left; max-width: 220px; margin: 0 1.4rem 1rem 0; }
.about-logo-old { float: right; clear: both; max-width: 170px; opacity: .85; margin: 1.6rem 0 1rem 1.4rem; }
@media (max-width: 600px) {
  .about-logo-new, .about-logo-old { float: none; display: block; max-width: 220px; margin: 0 auto 1rem; }
}

/* ---- Über uns: scrollbarer Zeitstrahl (Galerien + Team-Beitritte) ----- */
/* Die Scrollbar-Optik selbst kommt jetzt von der globalen Regel ganz oben
   in dieser Datei (site-weit auf Martins Wunsch, ursprünglich hier am
   Zeitstrahl aufgefallen) - hier nur noch das Layout des Zeitstrahls. */
.timeline-scroll { overflow-x: auto; padding: 2.2rem 0 1rem; margin-top: 1rem; }
.timeline-track {
  position: relative; display: flex; gap: 2.2rem; min-width: min-content; padding: 0 1rem;
}
.timeline-track::before {
  content: ""; position: absolute; left: 0; right: 0; top: 68px; height: 2px;
  background: var(--color-border);
}
.timeline-item {
  position: relative; display: flex; flex-direction: column; align-items: center; text-align: center;
  width: 120px; flex-shrink: 0; color: var(--color-text); padding-top: 1.2rem;
}
.timeline-item:hover { text-decoration: none; }
.timeline-img {
  width: 64px; height: 64px; border-radius: 50%; overflow: hidden; border: 2px solid var(--color-border);
  margin-bottom: .9rem; background: var(--color-card-bg);
}
a.timeline-item:hover .timeline-img { border-color: var(--color-accent); }
/* Martins Wunsch (23.09.26 + Nachtrag 24.09.26): im Zeitstrahl glüht
   dauerhaft der Bild-Kreis selbst statt eines Punkts auf der Linie (der
   Punkt entfällt dadurch komplett, siehe pages/about.php - die frühere
   .timeline-dot-Regel ist deshalb entfernt, keine Verwendung mehr).
   Glow nach demselben color-mix()-Muster wie der bestehende Hover-Glow von
   .glow-card. Team-Beitritte: fest grün, an --color-secondary gekoppelt.
   Galerien: an dieselbe Farbe gekoppelt wie ihr Kartenmarker - aktives
   Spielfeld an --color-marker, historisches Spielfeld an --color-marker-
   historical (grau) UND das Thumbnail zusätzlich in Schwarz-Weiß. */
.timeline-img-member {
  border-color: var(--color-secondary);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-secondary) 70%, transparent),
              0 0 10px 3px color-mix(in srgb, var(--color-secondary) 60%, transparent),
              0 0 20px 6px color-mix(in srgb, var(--color-secondary) 35%, transparent);
}
a.timeline-item:hover .timeline-img-member { border-color: var(--color-secondary); }
.timeline-img-active {
  border-color: var(--color-marker);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-marker) 70%, transparent),
              0 0 10px 3px color-mix(in srgb, var(--color-marker) 60%, transparent),
              0 0 20px 6px color-mix(in srgb, var(--color-marker) 35%, transparent);
}
a.timeline-item:hover .timeline-img-active { border-color: var(--color-marker); }
.timeline-img-historical {
  border-color: var(--color-marker-historical);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-marker-historical) 70%, transparent),
              0 0 10px 3px color-mix(in srgb, var(--color-marker-historical) 60%, transparent),
              0 0 20px 6px color-mix(in srgb, var(--color-marker-historical) 35%, transparent);
}
a.timeline-item:hover .timeline-img-historical { border-color: var(--color-marker-historical); }
.timeline-img-historical img { filter: grayscale(1); }
.timeline-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
.timeline-date { font-size: .72rem; color: var(--color-text-secondary); text-transform: uppercase; letter-spacing: .03em; }
.timeline-label { font-family: 'Rajdhani', sans-serif; font-weight: 700; font-size: .92rem; margin-top: .1rem; }
.timeline-sub { font-size: .74rem; color: var(--color-text-secondary); }

/* ---- Links-Seite: Kachelsystem (Partner &amp; Sponsoren / Community &amp;
   Medien), 3 Spalten pro Zeile, Logo links + Text rechts, selber Hover-Glow
   wie das Termine-Panel der Startseite (.glow-card). ---------------------- */
.link-tiles { margin-top: .8rem; align-items: start; }
.link-tile { display: flex; align-items: center; gap: .9rem; padding: .9rem 1.1rem; color: var(--color-text); background: var(--color-card-bg); }
.link-tile:hover { text-decoration: none; }
.link-tile-logo { flex-shrink: 0; width: 48px; height: 48px; display: flex; align-items: center; justify-content: center; border-radius: 8px; overflow: hidden; background: var(--color-bg); }
.link-tile-logo img { width: 100%; height: 100%; object-fit: contain; }
.link-tile-logo.social-icon svg { width: 48px; height: 48px; }
.link-tile-text { font-family: 'Rajdhani', sans-serif; font-weight: 600; font-size: 1rem; }
/* Sponsor-Kacheln auf /links (Martins Wunsch): Logo füllt die ganze Kachel
   randlos aus, statt kleiner Icon-Box + Textzeile wie bei Community-Links.
   Die Kachelhöhe richtet sich nach dem jeweiligen Logo selbst (kein festes
   Seitenverhältnis mehr) - jedes Bild bestimmt bei voller Kachelbreite seine
   eigene Höhe, dadurch wirken schmale/breite Logos nicht mehr gestreckt oder
   mit Leerraum umrahmt. */
.link-tile-fill { display: block; padding: 0; background: var(--color-bg); overflow: hidden; }
.link-tile-fill img { display: block; width: 100%; height: auto; }

/* ---- Footer -------------------------------------------------------------- */
.site-footer { margin-top: 3rem; padding: 2.2rem 0; background: var(--color-primary); border-top: 1px solid var(--color-border); }
.footer-inner { display: flex; flex-wrap: wrap; gap: 1.2rem 2rem; align-items: center; justify-content: space-between; }
/* Logo + Mottos als Gruppe, eng beieinander (Martins Wunsch: Mottos näher
   am größeren Logo), Logo mind. doppelt so groß wie zuvor (34px -> 72px). */
.footer-brand-group { display: flex; align-items: center; gap: .9rem; }
.footer-brand { display: block; flex-shrink: 0; }
.footer-brand img { height: 72px; display: block; }
.footer-mottos { display: flex; flex-direction: column; justify-content: center; gap: .1rem; }
.motto-main { margin: 0; color: var(--color-accent); font-size: 1.1rem; line-height: 1.25; }
.motto-sub { margin: 0; color: var(--color-text-secondary); font-size: .85rem; font-weight: 500; line-height: 1.25; }
.footer-links a { margin-right: 1rem; font-size: .85rem; }
.footer-copy { width: 100%; text-align: center; color: var(--color-text-secondary); font-size: .8rem; margin-top: .4rem; }
@media (max-width: 600px) {
  .footer-brand img { height: 56px; }
  .footer-inner { justify-content: center; text-align: center; }
}

/* ---- Admin-Layout ---------------------------------------------------------- */
/* Martins Wunsch: Admin-Menü/-Inhalt etwas von der Kopfzeile absetzen, wie
   bei den Seiten-Headern (.page-header/.hero-banner nutzen dafür ebenfalls
   1.6rem Abstand zur Kopfzeile darüber - gleicher Wert hier übernommen). */
.admin-shell { display: flex; gap: 1.5rem; align-items: flex-start; margin-top: 1.6rem; }
.admin-nav { width: 220px; flex-shrink: 0; background: var(--color-card-bg); border: 1px solid var(--color-border); border-radius: 10px; padding: 1rem; }
.admin-nav a { display: block; padding: .4rem 0; font-size: .88rem; }
/* Martins Wunsch (18.09.26, Admin-Menü-Neusortierung): dezenter Trennstrich
   zwischen den nach Rollen gruppierten Menüpunkten (siehe $navGroups in
   templates/admin_header.php) - bewusst schmaler/unauffälliger als
   .section-break, da es sich nur um eine Navigationsliste handelt. */
.admin-nav-divider { border-top: 1px solid var(--color-border); margin: .5rem 0; opacity: .7; }
.admin-content { flex: 1; min-width: 0; }
.dashboard-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 1rem; }
.dashboard-card { background: var(--color-card-bg); border: 1px solid var(--color-border); border-radius: 10px; padding: 1rem; }
.dashboard-card .num { font-size: 2rem; color: var(--color-accent); }
@media (max-width: 800px) { .admin-shell { flex-direction: column; } .admin-nav { width: 100%; } }

/* ---- Design/Farben: Live-Vorschau der Startseite ----------------------
   Martins Wunsch (18.09.26): "live vor dem Speichern sehen, was sich wie
   verändert" - lädt die echte Startseite in einem iframe und skaliert sie
   über CSS transform:scale() auf eine kompakte Vorschaugröße herunter (die
   eigentliche Skalierung berechnet ein kleines Skript in admin/design.php
   responsiv anhand der verfügbaren Breite). overflow:hidden auf dem Wrapper
   schneidet den durch die Skalierung entstehenden Leerraum ab. */
.design-preview-frame-wrap {
  width: 100%; max-width: 480px; overflow: hidden; position: relative;
  border: 1px solid var(--color-border); border-radius: 10px; background: #000;
}
.design-preview-frame { width: 1400px; height: 900px; border: 0; display: block; transform-origin: top left; }
/* Martins Wunsch (18.09.26): Vorschau soll rechts neben den Farbreglern
   stehen und dabei sichtbar bleiben, statt dass man zum Vergleichen hoch-
   und runterscrollen muss - .design-preview-col bekommt daher
   position:sticky (top-Wert = gemessene Höhe der site-header-Kopfzeile
   [55.6px] + Abstand, damit die Vorschau nicht dahinter verschwindet,
   sobald man in der - meist längeren - Farbliste links weiterscrollt).
   Unter 900px Breite bricht das Layout auf Untereinander um (Formular
   zuerst), sticky ergäbe auf so schmalen Bildschirmen ohnehin keinen
   Sinn mehr. */
.design-live-layout { display: flex; gap: 2rem; align-items: flex-start; flex-wrap: wrap; }
.design-live-layout > #colorForm { flex: 1 1 380px; max-width: 480px; }
.design-preview-col { flex: 1 1 380px; max-width: 480px; position: sticky; top: 76px; }
@media (max-width: 900px) {
  .design-live-layout { flex-direction: column; flex-wrap: nowrap; }
  .design-live-layout > #colorForm, .design-preview-col { max-width: 100%; }
  .design-preview-col { position: static; }
}

/* ---- Speicherplatz-Gauge (Dashboard-Kachel + Speicher-Tab) ------------- */
.storage-gauge { width: 100%; height: 14px; border-radius: 999px; background: var(--color-bg); border: 1px solid var(--color-border); overflow: hidden; }
.storage-gauge-fill { height: 100%; background: var(--color-accent); border-radius: 999px; transition: width .3s ease; }
.storage-gauge-mini { height: 8px; margin-top: .5rem; }

/* ---- Seiten-Traffic-Widget (Dashboard, Martins Wunsch 27.09.26) -------
   Grün/Rot bewusst fest codiert statt über eine Design-Variable (gleiches
   Muster wie .flash-ok/.flash-err oben) - ein Trend-Pfeil soll unabhängig
   vom gewählten Farbschema immer eindeutig als "gut" bzw. "schlecht"
   erkennbar sein. #3fae4e = derselbe Grünton wie der bestehende
   Warenkorb-Badge-Startwert (siehe .nav-cart-badge), damit im Adminbereich
   ein einheitliches Grün verwendet wird. */
.traffic-stats { display: flex; gap: 1.5rem; flex-wrap: wrap; margin-top: .3rem; }
.traffic-stat { text-align: center; }
.traffic-stat .num { font-size: 1.6rem; }
.trend-up { color: #3fae4e; font-size: 1.1rem; }
.trend-down { color: var(--color-logout, #b23a3a); font-size: 1.1rem; }
.trend-same { color: var(--color-text-secondary); font-size: 1.1rem; }

/* ---- Kleine Chips (z. B. markierte Personen in der Galerie-Verwaltung) --- */
.pill { display: inline-block; background: var(--color-card-bg); border: 1px solid var(--color-border); border-radius: 999px; padding: .1rem .55rem; font-size: .75rem; }

/* ---- Cookie-/Datenschutz-Hinweis (rein informativ, siehe datenschutz.php
   Punkt 9 - keine einwilligungspflichtigen Cookies, daher nur "Verstanden"
   statt Opt-in/Opt-out). [hidden] hält den Banner unsichtbar, bis main.js
   entscheidet, dass er (mangels pmc_cookie_notice-Cookie) gezeigt werden
   soll - so blitzt er beim Laden nie kurz auf, wenn er eigentlich schon
   bestätigt wurde. */
.cookie-banner {
  position: fixed; left: 1rem; right: 1rem; bottom: 1rem; z-index: 90;
  max-width: 640px; margin: 0 auto;
  background: var(--color-card-bg); border: 1px solid var(--color-border);
  border-radius: 10px; box-shadow: 0 10px 30px rgba(0,0,0,.5);
  padding: 1rem 1.2rem; display: flex; flex-wrap: wrap; align-items: center; gap: .9rem;
}
.cookie-banner[hidden] { display: none; }

/* ---- Generischer Abschnitts-Trenner (Trennstrich + Label mittig) --------
   Ursprünglich für die Jahreszahl in der Galerie-Übersicht entstanden
   (ersetzte dort die alte "Andere Jahre"-Linkliste), inzwischen bewusst
   generisch benannt (nicht mehr .year-divider), weil Martin dasselbe
   Trennstrich-Design auch für andere Gruppierungen wiederverwendet haben
   möchte (z.B. Team-Seite: Team Member/Anwärter/Veteranen). */
/* grid-column:1/-1 lässt den Trenner innerhalb eines Grid-Elternelements
   (z.B. .grid.grid-4 in Galerie/Team) die volle Breite einnehmen, statt
   selbst eine Kachel-Spalte zu belegen - ohne Grid-Elternelement (z.B. auf
   der Galerie-Detailseite) hat die Eigenschaft schlicht keine Wirkung. */
.section-divider { display: flex; align-items: center; gap: 1rem; margin: .3rem 0 1.3rem; grid-column: 1 / -1; }
.section-divider-line { flex: 1; height: 1px; background: var(--color-border); }
.section-divider-label {
  font-family: 'Rajdhani', sans-serif; font-weight: 700; font-size: 1.7rem;
  letter-spacing: .05em; color: var(--color-accent); white-space: nowrap;
}
.cookie-banner p { margin: 0; flex: 1; min-width: 220px; font-size: .85rem; color: var(--color-text-secondary); }
.cookie-banner p a { color: var(--color-link); }

/* ---- Pillarbox-Unschärfe (Martins Wunsch 18.09.26) --------------------
   Kacheln/Titelbilder bei Galerie, Events und News sind für Querformat-
   Uploads ausgelegt (feste aspect-ratio + object-fit:cover). Immer mehr
   Nutzer laden aber Hochformat-Fotos hoch (z.B. Smartphone-Selfies) - die
   wurden dadurch seitlich beschnitten. Mit dieser Klasse bleibt das ganze
   Bild sichtbar (object-fit:contain statt cover), die links/rechts frei
   werdende Fläche füllt automatisch dasselbe Bild noch einmal, nur groß
   gezoomt, weichgezeichnet und abgedunkelt - bekannt aus TV/Kino bei
   Hochformat-Videos im Querformat-Rahmen ("Pillarbox"). Das jeweilige
   Bild kommt als CSS-Variable --pillarbox-img mit (per Inline-Style an
   der jeweiligen Stelle im PHP-Template gesetzt), weil an vielen dieser
   Stellen mehrere/wechselnde Bilder auftreten (Rotor-Diashow, Galerie-
   Karussell) und die Unschärze-Fläche pro Bild mitwechseln muss.

   Ursprünglich (18.09.26) bewusst NICHT für .card img gedacht - auf
   Martins Nachfrage (18.09.26, "News-Kacheln UND die kleinen Bildkacheln
   in den Galerien fehlen noch") aber gezielt auf GENAU diese beiden
   .card-Anwendungsfälle ausgeweitet: News-Übersicht (pages/news_list.php)
   und die Fotokacheln INNERHALB einer geöffneten Galerie
   (pages/gallery_detail.php). Bei gallery_detail.php besteht die Kachel
   NUR aus dem Bild (kein card-body danach), daher sitzt .pillarbox dort
   direkt auf der Kachel selbst (.card.pillarbox img unten). Bei
   news_list.php dagegen folgt unter dem Bild noch ein card-body mit
   Titel/Text - ECHTER BUG (v1.2.4, von Martin gemeldet): .pillarbox saß
   zunächst ebenfalls direkt auf der Kachel, wodurch die Weichzeichenebene
   die GESAMTE Kachelhöhe inkl. Text abdeckte und diesen unlesbar machte.
   Fix: dediziertes Bild-Wrapper-Element .card-cover (siehe .card-cover
   weiter unten) trägt dort die .pillarbox-Klasse, card-body bleibt
   Geschwister-Element daneben - exakt das Muster von .panel-feature-img.
   Team-Übersicht, Suche und Admin-Medienverwaltung nutzen .card weiterhin
   OHNE .pillarbox und bleiben unverändert beim bisherigen Zuschnitt (dort
   weiterhin gewünscht/unproblematisch, z.B. quadratische Profilbilder).
   Ebenfalls NICHT geändert: admin/galerie_detail.php (Admin-
   Verwaltungsansicht derselben Fotos, mit Personen-Markierung/Sortierung)
   - dort weiterhin Zuschnitt, da bisher nicht angefragt; bei Bedarf
   dieselbe .pillarbox-Klasse dort ergänzen (dann ebenfalls prüfen, ob ein
   card-body-Geschwister wie bei news_list.php einen eigenen Wrapper
   braucht, oder ob die Kachel wie bei gallery_detail.php bild-only ist).

   Spezifität: jede Override-Regel unten kombiniert .pillarbox IMMER
   zusätzlich mit der jeweiligen bestehenden Rahmen-Klasse auf demselben
   Element (z.B. .event-row-media.pillarbox), damit die Spezifität in
   JEDEM Fall über der des bestehenden object-fit:cover liegt - unabhängig
   von der Position im Stylesheet (bekanntes Bug-Muster in diesem Projekt
   bei gleich spezifischen Regeln, wo die Reihenfolge im Stylesheet statt
   die Absicht entscheidet).

   Stapelreihenfolge (kein z-index nötig): die ::before-Weichzeichenebene
   wird als generierter Inhalt IMMER als "erstes Kind" gerendert, das
   eigentliche <img> (bzw. bei Rotor-Diashows dessen bereits positionierter
   .rotor-Wrapper) kommt im Markup danach - dadurch malt der Browser das
   scharfe Bild automatisch über die Unschärfe-Ebene, ganz ohne explizite
   z-index-Werte, die mit bestehenden Overlays (Datums-Badge, Kachel-
   Beschriftung) kollidieren könnten. Voraussetzung ist nur, dass das
   sichtbare <img> selbst "positioniert" ist (position:relative) - bei
   den Rotor-Varianten übernimmt das bereits der bestehende .rotor-
   Wrapper, nur bei den drei "nackten" <img>-Fällen (News-Feature,
   Event-Detailseite, Galerie-Karussell) wird das hier extra gesetzt. */
/* Ersetzt das frühere reine <img style="aspect-ratio:16/9;object-fit:cover">
   auf der Event-Detailseite - brauchte einen echten Wrapper (statt nur
   Inline-Style am <img> selbst), da die Weichzeichenebene (::before) einen
   umgebenden, positionierten Container braucht. */
.event-detail-cover { display: block; width: 100%; aspect-ratio: 16/9; border-radius: 10px; overflow: hidden; margin: .6rem 0 1.2rem; background: var(--color-bg); }
.event-detail-cover img { width: 100%; height: 100%; display: block; }
.pillarbox { position: relative; }
.pillarbox::before {
  content: "";
  position: absolute;
  inset: -20px;
  background-image: var(--pillarbox-img);
  background-size: cover;
  background-position: center;
  filter: blur(18px) brightness(.5) saturate(1.15);
  transform: scale(1.15);
  /* Martins Feedback 18.09.26: der Wechsel zwischen den Unschärfe-
     Hintergründen der Rotor-Diashow (Events/kompakte Terminliste/Galerie-
     Übersicht) wirkte abrupt/ruckartig - anders als der sanfte 3D-Flip
     des scharfen Bildes daneben. Ursache: background-image lässt sich
     nicht direkt per CSS-Transition überblenden, main.js tauschte
     --pillarbox-img bisher einfach hart um. Fix: opacity kommt jetzt über
     eine eigene Variable (--pillarbox-opacity, main.js blendet kurz vor/
     nach dem Bildwechsel auf 0/1) UND ist hier transitioniert - dadurch
     blendet die Unschärzefläche kurz aus, wechselt unsichtbar das Bild
     und blendet wieder ein, synchron zum Flip des scharfen Bildes (siehe
     main.js goTo()). Wirkt sich NICHT auf das Galerie-Karussell aus (dort
     blendet bereits der ganze .pillarbox-frame inkl. Bild als Einheit
     sanft über, siehe .gallery-tile .pillarbox-frame weiter unten). */
  opacity: var(--pillarbox-opacity, 1);
  transition: opacity .35s ease;
}
/* Bild-Wrapper für News-Liste (ECHTER BUG-Fix, Martins Feedback 18.09.26,
   siehe ausführlicher Kommentar in pages/news_list.php): .pillarbox saß
   dort ursprünglich auf der GESAMTEN Kachel inkl. card-body (Titel/Text),
   wodurch die Weichzeichenebene auch über dem Text lag. .card-cover
   umschließt jetzt NUR das Bild (card-body bleibt Geschwister-Element
   daneben) und bekommt ein eigenes overflow:hidden, damit die
   Weichzeichenebene (inset:-20px) exakt an der Bildkante geclippt wird,
   statt in den Textbereich zu bluten. .card.pillarbox (ohne -cover) bleibt
   für gallery_detail.php bestehen, wo die Kachel wirklich NUR aus dem Bild
   besteht (kein card-body) - dort ist die Klasse direkt auf der Kachel
   korrekt und unverändert richtig. */
.card-cover { display: block; position: relative; overflow: hidden; }
/* "nackte" <img>-Fälle: News-Feature-Kachel (Startseite), Event-Detail-
   Titelbild, News-Liste - hier braucht das <img> zusätzlich
   position:relative, damit es (s.o.) über der generierten
   Weichzeichenebene liegt. */
.panel-feature-img.pillarbox img,
.event-detail-cover.pillarbox img,
.card-cover.pillarbox img,
.card.pillarbox img,
.shop-product-image.pillarbox img {
  object-fit: contain;
  position: relative;
}
/* Rotor-Diashow-Fälle (Events-Zeile, Events-Startseite/-Archiv, kompakte
   Terminliste, Galerie-Übersichtsseite): der umgebende .rotor-Wrapper ist
   bereits position:relative bzw. position:absolute - das genügt bereits
   für die richtige Stapelreihenfolge, hier muss nur noch object-fit auf
   contain umgestellt werden. */
.event-row-media.pillarbox .rotor-img,
.rotor.pillarbox .rotor-img,
.gallery-flip-tile.pillarbox .rotor-img {
  object-fit: contain;
}
/* Galerie-Karussell (Startseite): eine Kachel zeigt mehrere, sich per JS
   überblendende Bilder (.gallery-tile img.active) - jedes Bild bekommt
   hier einen eigenen kleinen Rahmen (.pillarbox-frame) mit eigener
   Unschärfe-Fläche, da jedes Bild ein anderes Foto (und damit einen
   anderen --pillarbox-img-Wert) zeigen kann. main.js findet die <img>-
   Elemente weiterhin zuverlässig (tile.querySelectorAll('img') durchsucht
   auch verschachtelte Elemente), das Überblenden (opacity/.active) bleibt
   dadurch unverändert funktionsfähig. */
/* Testrender ergab: würde die Überblendung (opacity) wie bisher nur auf
   dem <img> selbst laufen, bliebe die WEICHZEICHENEBENE (::before) des
   gerade UNSICHTBAREN Bildes trotzdem voll sichtbar (Pseudo-Elemente
   folgen nicht der opacity ihres Geschwister-<img>s) - bei mehreren
   überlagerten Rahmen hätte so am Ende immer die zuletzt im Markup
   stehende Unschärfe-Fläche alle anderen Bilder verdeckt. Deshalb läuft
   die Überblendung hier auf dem GESAMTEN Rahmen (.pillarbox-frame,
   Bild+Unschärfe zusammen) statt nur auf dem <img> - main.js schaltet
   dafür die active-Klasse auf dem Rahmen um (siehe dortiger Kommentar),
   das <img> selbst bleibt innerhalb seines Rahmens immer sichtbar. */
.gallery-tile .pillarbox-frame {
  position: absolute; inset: 0;
  opacity: 0; transition: opacity 1.6s ease-in-out;
}
.gallery-tile .pillarbox-frame.active { opacity: 1; }
.gallery-tile .pillarbox-frame img {
  position: relative;
  width: 100%; height: 100%;
  object-fit: contain;
  opacity: 1;
}
@media (prefers-reduced-motion: reduce) {
  .gallery-tile .pillarbox-frame { transition: none; }
}

/* ---- Farbauswahl auf der Shop-Produktseite (Martins Wunsch 25.09.26:
   "beim Erstellen eines Produkts noch Farben angeben, sodass der Kunde sich
   durch die Farben klicken kann"), pages/shop_product.php. Das eigentliche
   <input type="radio"> bleibt vorhanden (Formular-Semantik/Pflichtfeld-
   Prüfung durch den Browser, kein reines JS-Konstrukt), wird aber optisch
   ausgeblendet statt display:none - so bleibt es weiterhin per Tastatur
   fokussierbar (:focus-visible unten), nur eben unsichtbar. Ausgewählt wird
   rein über CSS (:checked + Geschwister-Element), keine eigene JS-Logik
   nötig. */
.color-swatches { display: flex; flex-wrap: wrap; gap: .8rem; margin-top: .4rem; }
/* ECHTER BUG gefixt (Martins Feedback 25.09.26, Handy-Screenshot: "was
   macht der gelbe Fleck da an der Farbe"): .color-swatch (Spezifität 0,1,0)
   wurde von der bestehenden, generischeren Regel "form.stack label"
   (Spezifität 0,1,2, siehe weiter oben) unabhängig von der Reihenfolge im
   Stylesheet überschrieben - bekanntes Bug-Muster in diesem Projekt (siehe
   Pillarbox-Kommentar weiter oben). display:inline-flex und margin-top:0
   gingen dadurch verloren, wodurch .color-swatch-dot (ein <span>, ignoriert
   ohne Flex-Elternteil seine width/height) auf einen Streifen zusammenfiel
   - übrig blieb sichtbar nur noch der goldene :focus-visible-Rahmen darum,
   das war der "gelbe Fleck". Fix: Selektor kombiniert zusätzlich die
   umgebende .color-swatches-Klasse (Spezifität 0,2,0), das schlägt
   form.stack label (0,1,2) in jedem Fall, da die Klassenanzahl vor der
   Elementanzahl verglichen wird. */
.color-swatches .color-swatch {
  display: inline-flex; flex-direction: column; align-items: center; gap: .3rem;
  width: auto; margin-top: 0; font-weight: normal; font-size: .78rem; color: var(--color-text-secondary);
  cursor: pointer; text-align: center;
}
.color-swatch input[type="radio"] {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
.color-swatch-dot {
  display: block; width: 2.3rem; height: 2.3rem; border-radius: 50%;
  border: 2px solid var(--color-border); box-shadow: 0 0 0 2px transparent;
  transition: border-color .15s, box-shadow .15s;
}
.color-swatch input:checked + .color-swatch-dot {
  border-color: var(--color-accent); box-shadow: 0 0 0 2px var(--color-accent);
}
.color-swatch input:focus-visible + .color-swatch-dot { outline: 2px solid var(--color-accent); outline-offset: 2px; }
/* Martins Feedback 25.09.26 (Nachtrag zum selben Screenshot): "Ich hätte
   statt Worte so farbige Punkte unter dem Produkt erwartet" - genau wie bei
   großen Shops (z.B. Amazon) üblich, nur noch reine Farbpunkte
   nebeneinander OHNE Textbeschriftung je Punkt. Der Name bleibt trotzdem
   für Screenreader vorhanden (per <label> weiterhin Teil des zugänglichen
   Namens des Radiobuttons) und wird zusätzlich oben als "Farbe: <Name>"
   eingeblendet (siehe .color-swatch-current, per JS beim Anklicken
   aktualisiert) - optisch bewusst versteckt statt display:none, damit es
   für Screenreader weiterhin vorgelesen wird. */
.color-swatch-label {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
.color-swatch-current { color: var(--color-text); font-weight: 600; }
.cookie-banner button { flex-shrink: 0; }
