/* ---------- Design tokens ---------- */

:root {
    --color-primary: #1d4e5f;
    --color-primary-hover: #163d49;
    --color-accent: #2f8f78;
    /* Darkened accent shade for hover/current-item states that used to
       switch to --color-primary(-hover) — hover effects deliberately no
       longer use the petroleum primary colour, see DEVELOPER.md.
       color-mix(), not a second fixed hex value: --color-accent is
       overridable per club (Tenant::$settings.accentColor), and a fixed
       hex here would silently stop tracking a club's own accent colour —
       exactly the bug a hardcoded value had before this. Same ~22%
       darkening ratio as --color-primary-hover has relative to
       --color-primary. */
    --color-accent-hover: color-mix(in srgb, var(--color-accent) 78%, black);
    --color-bg: #ffffff;
    --color-surface: #f5f7f7;
    --color-text: #202124;
    --color-text-muted: #5f6368;
    --color-border: #e0e3e2;
    --color-danger: #b0413e;
    /* Header colours default to the page colours, so a club only has to set
       them when the navigation should differ from the rest of the site. Both
       are overridable per club under /admin/settings. */
    --nav-bg: var(--color-bg);
    --nav-text: var(--color-text);
    /* Accent links inside the header (member login). Separate from
       --nav-text so the default stays the accent colour, but a club that
       picks a header text colour gets it applied here too — otherwise a
       petrol link on a petrol header would be invisible. */
    --nav-link: var(--color-primary);
    --radius-sm: 6px;
    --radius-md: 10px;
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, .06), 0 1px 1px rgba(0, 0, 0, .04);
    --shadow-md: 0 4px 16px rgba(0, 0, 0, .08);
    --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

/* ---------- Reset & base typography ---------- */

*,
*::before,
*::after {
    box-sizing: border-box;
}

body {
    margin: 0;
    font-family: var(--font-sans);
    color: var(--color-text);
    background: var(--color-bg);
    line-height: 1.55;
    /* .row--full-width (siehe unten) bricht per width:100vw aus der
       zentrierten Inhaltsspalte aus — auf manchen Browsern/Betriebssystemen
       zählt 100vw die Breite eines permanent sichtbaren Scrollbalkens mit,
       was sonst eine winzige horizontale Scrollmöglichkeit erzeugen würde. */
    overflow-x: hidden;
}

h1, h2, h3 {
    line-height: 1.25;
    margin: 0 0 0.6em;
}

p {
    margin: 0 0 1em;
}

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

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

img {
    max-width: 100%;
}

blockquote {
    margin: 0 0 1em;
    padding-left: 1em;
    border-left: 4px solid var(--color-border);
    color: var(--color-text-muted);
}

pre {
    margin: 0 0 1em;
    padding: 0.75em 1em;
    background: var(--color-surface);
    border-radius: var(--radius-sm);
    overflow-x: auto;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

table {
    border-collapse: collapse;
    margin: 0 0 1em;
    max-width: 100%;
}

th, td {
    border: 1px solid var(--color-border);
    padding: 0.4em 0.6em;
}

/* ---------- Site header / navigation ---------- */

/* Kontaktzeile (Tenant::$settings.navContactLine + verbundenes Instagram-
   Konto) — unabhängig vom Navigationstyp zuschaltbar. Erscheint zweimal im
   Markup (siehe _navigation.html.twig): als eigene Zeile hier oberhalb des
   Headers (--top, für Horizontal/Zentriert/Overlay) und zusätzlich fest im
   Sidebar-Header (--sidebar) — letztere per Default ausgeblendet. */
.site-contact-bar--sidebar {
    display: none;
}

.site-contact-bar--top {
    background: var(--color-surface);
    border-bottom: 1px solid var(--color-border);
}

.site-contact-bar-inner {
    /* Bewusst NICHT wie .site-header-inner/.site-main auf max-width:1100px
       plus margin:0 auto beschränkt — die Kontaktzeile soll ganz an den
       rechten Viewport-Rand reichen, nicht nur an den Rand der zentrierten
       Inhaltsspalte. */
    padding: 0.35rem 1.25rem;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.9rem;
    font-size: 0.8rem;
    color: var(--color-text-muted);
}

.site-contact-instagram {
    display: inline-flex;
    color: inherit;
}

.site-contact-instagram:hover {
    color: var(--color-accent);
}

/* position/z-index sorgen dafür, dass ein per --logo-overflow (siehe unten)
   über die Kopfzeile hinausragendes Logo sichtbar vor dem Seiteninhalt
   darunter liegt, unabhängig davon, was dort gerendert wird — bei
   --logo-overflow: 0 (Default) ohne sichtbaren Effekt. */
.site-header {
    position: relative;
    z-index: 10;
    border-bottom: 1px solid var(--color-border);
    background: var(--nav-bg);
}

.site-header-inner {
    /* Breite des Bands (Tenant::$settings.navMaxWidth, Pixelwert, Default
       1100px = bisheriger fester Wert). .site-header selbst (der äußere
       Rahmen) hat kein eigenes max-width/padding, ein größerer Wert lässt
       das Band also einfach bis dahin wachsen — anders als bei
       Row::$fullWidth reicht das allein, kein 100vw-Ausbrechen aus einem
       zentrierten Elternelement nötig. margin:0 auto zentriert dabei immer
       symmetrisch, "breiter" wirkt dadurch automatisch in beide Richtungen. */
    max-width: var(--nav-max-width, 1100px);
    margin: 0 auto;
    /* Höhe des Bands (Tenant::$settings.navBarPadding, Pixelwert, per
       TenantSettings::toCssVariables() in den :root-Override geschrieben,
       Default 14px = bisheriges 0.9rem). */
    padding: var(--nav-bar-padding, 14px) 1.25rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
}

.site-brand {
    font-size: 1.15rem;
    font-weight: 700;
    color: var(--nav-text);
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
}

.site-brand:hover {
    color: var(--color-accent);
}

/* Höhe/Margin per CSS-Variable (Tenant::$settings.navLogoOverflow, Pixelwert,
   per TenantSettings::toCssVariables() als --logo-overflow in den
   :root-Override in base.html.twig geschrieben, Default 0px = 40px wie
   bisher): wächst um --logo-overflow über die normalen 40px hinaus, ein
   halb so großer negativer vertikaler Margin gleicht den dadurch
   zusätzlichen Platzbedarf im Flex-Layout wieder aus — der Effekt ist rein
   optisch (Bild ragt über seine eigene Layout-Box hinaus), die Kopfzeile
   selbst wächst dadurch kaum. */
.site-brand-logo {
    height: calc(40px + var(--logo-overflow, 0px));
    width: auto;
    display: block;
    margin: calc(var(--logo-overflow, 0px) / -2) 0;
}

.site-nav-toggle {
    display: none;
    color: var(--nav-text);
    background: none;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    padding: 0.4em 0.6em;
    font-size: 1.1rem;
    line-height: 1;
    cursor: pointer;
}

/* Nur beim Typ "Overlay" sichtbar, siehe dortige Regeln unten — bei den
   anderen drei Typen wird .site-nav nie zu einem eigenen, den Rest der
   Seite verdeckenden Panel, ein Schließen-Button wäre dort ohne Funktion. */
.site-nav-close {
    display: none;
}

/* Nur mobil sichtbar (app.css weiter unten) — am Desktop öffnet weiterhin
   :hover das Flyout, kein Button nötig. */
.site-nav-expand {
    display: none;
}

.site-nav {
    display: flex;
    align-items: center;
    gap: 1.5rem;
    flex-wrap: wrap;
}

/* .site-nav-links is the <ul> that renders the page tree — it appears up to
   three times in _navigation.html.twig (the always-present full list inside
   .site-nav, plus two half-lists used only by the "centered" nav type below),
   so this is deliberately not scoped to ".site-nav ul" like before. */
.site-nav-links {
    list-style: none;
    display: flex;
    align-items: center;
    /* Abstand zwischen den Menüpunkten (Tenant::$settings.navGap, Pixelwert,
       Default 24px = bisheriges 1.5rem) — unabhängig von navDensity, siehe
       dort. */
    gap: var(--nav-gap, 24px);
    margin: 0;
    padding: 0;
    flex-wrap: wrap;
}

.site-nav-links li {
    position: relative;
    list-style: none;
}

.site-nav-links > li > a {
    text-decoration: none;
    color: var(--nav-text);
    font-weight: 600;
    /* Schriftgröße der Menüpunkte (Tenant::$settings.navFontSize, Pixelwert,
       Default 15px = bisheriges 0.95rem) — unabhängig von navDensity, siehe
       dort. */
    font-size: var(--nav-font-size, 15px);
    /* Etwas Fläche für die Hover-Effekte "Hintergrund-Highlight"/"Schräges
       Band" — vorher klebten die eng am Text, Dropdown-Links haben das
       bereits (siehe .dropdown a unten). */
    padding: 0.3em 0.5em;
}

/* margin:0 (kein Abstand zum auslösenden Link) ist bewusst so und nicht
   nur Geschmackssache: da .dropdown position:absolute ist, zählt sie nicht
   zur Höhe ihres eigenen <li> (das endet bei top:100%, also genau an ihrer
   eigenen Oberkante) — ein margin-top hätte eine "tote Zone" zwischen Link
   und Dropdown erzeugt, in der der Mauszeiger weder über dem <li> noch über
   der Dropdown selbst steht. Dort verliert :hover kurz den Kontakt, bevor
   der Zeiger die Dropdown überhaupt erreicht, und sie klappt wieder zu —
   bei "kompakt" (kleineres Link-Padding, also ein kleineres Hover-Ziel)
   sehr leicht reproduzierbar, tatsächlich aber unabhängig von der Dichte
   immer ein Bug gewesen. border/box-shadow trennen optisch weiterhin klar
   genug vom Link, auch ganz ohne Lücke. */
.site-nav-links .dropdown {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    list-style: none;
    flex-direction: column;
    gap: 0.15rem;
    min-width: 180px;
    margin: 0;
    padding: 0.5rem;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-md);
    z-index: 20;
}

.site-nav-links .dropdown a {
    display: block;
    padding: 0.35em 0.5em;
    border-radius: var(--radius-sm);
    font-weight: 500;
    text-decoration: none;
    color: var(--color-text);
}

.site-nav-links .dropdown a:hover {
    background: var(--color-surface);
    color: var(--color-accent);
}

/* Gleiche Begründung wie beim margin:0 oben, nur horizontal: kein
   margin-left, sonst dieselbe tote Zone beim seitlichen Übergang in die
   dritte Ebene. */
.site-nav-links .dropdown .dropdown {
    top: 0;
    left: 100%;
    margin: 0;
}

.site-nav-links li.has-dropdown:hover > .dropdown,
.site-nav-links li.has-dropdown:focus-within > .dropdown {
    display: flex;
}

.site-nav-members {
    display: flex;
    align-items: center;
    gap: 0.75rem;
}

.site-nav-members a:not(.button) {
    font-weight: 600;
    font-size: 0.9rem;
    text-decoration: none;
    color: var(--nav-link);
}

.site-nav-members .button {
    font-size: 0.85rem;
    padding: 0.4em 0.9em;
}

/* ---------- Navigationstyp (/admin/settings, Tenant::$settings.navType) ---------- */
/* "horizontal" (Default) braucht keine eigenen Regeln — es ist genau das
   oben definierte Basisverhalten. Die anderen drei Typen sind rein additiv:
   dasselbe Markup, nur über body[data-nav-type] umgeschaltet. */

.site-nav-desktop-half {
    display: none;
    align-items: center;
}

@media (min-width: 721px) {
    body[data-nav-type="centered"] .site-nav-desktop-half {
        display: flex;
        flex: 1;
    }

    body[data-nav-type="centered"] .site-nav-desktop-half--left {
        order: 1;
        justify-content: flex-end;
    }

    body[data-nav-type="centered"] .site-brand {
        order: 2;
    }

    body[data-nav-type="centered"] .site-nav-desktop-half--right {
        order: 3;
    }

    body[data-nav-type="centered"] .site-nav {
        order: 4;
    }

    body[data-nav-type="centered"] .site-nav-links--full {
        display: none;
    }
}

body[data-nav-type="sidebar"] .site-header {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    width: 260px;
    overflow-y: auto;
    border-bottom: none;
    border-right: 1px solid var(--color-border);
    z-index: 40;
}

body[data-nav-type="sidebar"] .site-header-inner {
    flex-direction: column;
    align-items: stretch;
    max-width: none;
}

/* Logo über die gesamte Breite der Sidebar, mittig (Tenant::$settings.navLogoFullWidth)
   — nur bei Typ "sidebar" wählbar, siehe settings-editor.js. .site-brand
   stapelt Logo/Name dafür senkrecht statt nebeneinander (align-items:center
   auf .site-header-inner sorgt sonst nur für Breite, nicht für eine
   sinnvolle Anordnung bei einem so breiten Bild). .site-brand-logo bekommt
   width:100% statt der sonst festen (ggf. per navLogoOverflow modifizierten)
   height — beide Größenangaben ergäben zusammen keinen Sinn, width:100%
   gewinnt hier einfach durch höhere Selektor-Spezifität. */
body[data-nav-type="sidebar"][data-nav-logo-full-width="1"] .site-brand {
    flex-direction: column;
    align-items: center;
    text-align: center;
    width: 100%;
}

body[data-nav-type="sidebar"][data-nav-logo-full-width="1"] .site-brand-logo {
    /* Größe (Tenant::$settings.navLogoFullWidthSize, Prozent der
       Sidebar-Breite, Default 100% = volle Breite). */
    width: var(--logo-full-width-size, 100%);
    height: auto;
    margin: 0;
}

body[data-nav-type="sidebar"] .site-contact-bar--top {
    display: none;
}

body[data-nav-type="sidebar"] .site-contact-bar--sidebar {
    display: flex;
    order: -1;
    justify-content: center;
    flex-wrap: wrap;
    gap: 0.5rem 0.9rem;
    padding-bottom: 0.75rem;
    margin-bottom: 0.75rem;
    /* Weder --color-text-muted (Text) noch --color-border (Trennlinie) sind
       für einen hellen Hintergrund gedacht — hier liegt beides direkt auf
       dem eingefärbten Sidebar-Band, nicht auf einer weißen Fläche wie die
       page-weite --top-Variante. --nav-text sonst kaum lesbar, ein
       durchscheinender weißer Trenner statt --color-border. */
    border-bottom: 1px solid rgba(255, 255, 255, 0.3);
    font-size: 0.8rem;
    color: var(--nav-text);
    text-align: center;
}

body[data-nav-type="sidebar"] .site-nav {
    flex-direction: column;
    align-items: stretch;
    gap: 1rem;
}

body[data-nav-type="sidebar"] .site-nav-links--full {
    flex-direction: column;
    align-items: stretch;
}

/* Unterebenen wachsen hier im normalen Fluss nach unten statt seitlich als
   Flyout — passender für eine schmale, feste Spalte als für eine Flyout,
   die neben der Navbar schweben würde. Gleicher Ansatz wie im Mobil-Menü
   unten (position:static, Einrückung per padding-left), inkl. desselben
   Effekts bei mehrfacher Verschachtelung: jede weitere Ebene rückt relativ
   zu ihrer schon eingerückten Elternebene zusätzlich ein. */
body[data-nav-type="sidebar"] .site-nav-links--full .dropdown {
    position: static;
    min-width: 0;
    box-shadow: none;
    border: none;
    background: none;
    padding: 0 0 0 1rem;
    margin: 0.25rem 0 0;
}

/* .dropdown a ist normalerweise dunkel (--color-text), weil ihr weißes
   Flyout-Kästchen das trägt (siehe .site-nav-links .dropdown oben) — in der
   Sidebar hat .dropdown aber background:none (direkt vorher überschrieben),
   der dunkle Text säße dann direkt auf dem eingefärbten Band und wäre kaum
   lesbar. Hier stattdessen dieselbe helle Farbe wie die Top-Level-Links. */
body[data-nav-type="sidebar"] .site-nav-links--full .dropdown a {
    color: var(--nav-text);
}

body[data-nav-type="sidebar"] .site-nav-members {
    flex-direction: column;
    align-items: stretch;
}

body[data-nav-type="sidebar"] .site-main {
    margin-left: calc(260px + 2rem);
}

body[data-nav-type="overlay"] .site-nav-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

body[data-nav-type="overlay"] .site-nav {
    display: none;
    position: fixed;
    inset: 0;
    background: var(--nav-bg);
    flex-direction: column;
    justify-content: center;
    align-items: center;
    gap: 2rem;
    z-index: 100;
}

body[data-nav-type="overlay"] .site-nav.is-open {
    display: flex;
}

body[data-nav-type="overlay"] .site-nav-links--full {
    flex-direction: column;
    align-items: center;
    gap: 1.5rem;
}

body[data-nav-type="overlay"] .site-nav-links--full > li > a {
    font-size: 1.5rem;
}

body[data-nav-type="overlay"] .site-nav-links--full .dropdown {
    position: static;
    box-shadow: none;
    border: none;
    background: none;
    align-items: center;
}

/* Gleiche Kontrast-Falle wie bei der Sidebar (siehe dortige Anmerkung in
   DEVELOPER.md): .dropdown a ist normalerweise dunkel, weil ihr weißes
   Flyout-Kästchen das trägt — hier direkt vorher aber background:none, der
   dunkle Text säße dann direkt auf dem eingefärbten Vollbild-Panel. */
body[data-nav-type="overlay"] .site-nav-links--full .dropdown a {
    color: var(--nav-text);
}

/* .site-nav-toggle (der Hamburger-Button) liegt außerhalb von .site-nav und
   damit unterhalb von dessen z-index:100 — beim geöffneten Vollbild-Panel
   ist er unerreichbar verdeckt, ohne eigenen Schließen-Button ließe sich
   das Menü dann gar nicht mehr schließen. */
body[data-nav-type="overlay"] .site-nav-close {
    display: block;
    position: absolute;
    top: 1.5rem;
    right: 1.5rem;
    background: none;
    border: none;
    color: var(--nav-text);
    font-size: 2.2rem;
    line-height: 1;
    cursor: pointer;
}

/* ---------- Hover-Effekt (Tenant::$settings.navHoverEffect) ---------- */
/* Jeder Effekt reagiert sowohl auf :hover als auch auf li.is-current (die
   aktuell aufgerufene Seite, siehe nav_items()-Makro in
   _navigation.html.twig) — bewusste Dopplung des Selektors statt einer
   gemeinsamen Klasse, weil beide Zustände dieselbe Deklaration auslösen,
   aber unabhängig voneinander eintreten können (auch die aktuelle Seite
   lässt sich noch hovern). */

body[data-nav-hover="color"] .site-nav-links a:hover,
body[data-nav-hover="color"] .site-nav-links li.is-current > a {
    color: var(--color-accent);
}

body[data-nav-hover="highlight"] .site-nav-links a {
    border-radius: var(--radius-sm);
    transition: background-color .15s ease;
}

body[data-nav-hover="highlight"] .site-nav-links a:hover,
body[data-nav-hover="highlight"] .site-nav-links li.is-current > a {
    background: var(--color-surface);
    /* Text wird die Bandfarbe selbst statt der (auf hellem Hintergrund oft
       kaum lesbaren) hellen --nav-text-Farbe — sieht aus wie ein Stempel in
       der Vereinsfarbe. Ergibt nur dann brauchbaren Kontrast, wenn ein
       Verein eine eigene, vom Standard-Weiß abweichende Bandfarbe
       (navBackground) gewählt hat — beim unveränderten weißen Band (Default)
       wäre der Text sonst kaum lesbar (fast weiß auf hellgrau). Gleiche
       Design-Annahme wie beim ribbon-Effekt weiter unten. */
    color: var(--nav-bg);
}

body[data-nav-hover="scale"] .site-nav-links a {
    display: inline-block;
    transition: transform .15s ease;
}

body[data-nav-hover="scale"] .site-nav-links a:hover,
body[data-nav-hover="scale"] .site-nav-links li.is-current > a {
    transform: scale(1.08);
}

body[data-nav-hover="underline"] .site-nav-links a {
    position: relative;
}

body[data-nav-hover="underline"] .site-nav-links a::after {
    content: '';
    position: absolute;
    left: 0.35em;
    right: 0.35em;
    bottom: -2px;
    height: 2px;
    background: currentColor;
    transform: scaleX(0);
    transition: transform .15s ease;
}

body[data-nav-hover="underline"] .site-nav-links a:hover::after,
body[data-nav-hover="underline"] .site-nav-links li.is-current > a::after {
    transform: scaleX(1);
}

/* Schräges Band hinter dem Linktext, wie bei einem Vereins-Wimpel/Reiter —
   ::before statt background direkt auf <a>, damit die Schräge (skewX) nicht
   auch den Text selbst verzerrt. */
body[data-nav-hover="ribbon"] .site-nav-links a {
    position: relative;
    z-index: 1;
}

body[data-nav-hover="ribbon"] .site-nav-links a::before {
    content: '';
    position: absolute;
    inset: 0;
    background: var(--color-bg);
    transform: skewX(-12deg);
    z-index: -1;
    opacity: 0;
    transition: opacity .15s ease;
}

body[data-nav-hover="ribbon"] .site-nav-links a:hover,
body[data-nav-hover="ribbon"] .site-nav-links li.is-current > a {
    /* Bandfarbe statt --color-text, gleiche Begründung/Voraussetzung wie
       beim highlight-Effekt oben (Kontrast setzt eine vom Standard-Weiß
       abweichende navBackground voraus). */
    color: var(--nav-bg);
}

body[data-nav-hover="ribbon"] .site-nav-links a:hover::before,
body[data-nav-hover="ribbon"] .site-nav-links li.is-current > a::before {
    opacity: 1;
}

/* ---------- Ausrichtung/Dichte/Sticky (Tenant::$settings.navAlign/navDensity/navSticky) ---------- */
/* navAlign wirkt sichtbar nur beim Typ "horizontal" (bei "centered" ist die
   volle Liste ausgeblendet, bei "sidebar"/"overlay" bestimmt bereits deren
   eigene flex-direction die Anordnung) — die Regel bleibt trotzdem
   unbedingt, ein no-op ist kein Fehlerfall (gleiche Philosophie wie
   BLOCK_STYLE_PRESETS: Werte sind frei kombinierbar, nicht gegenseitig
   validiert). navDensity "normal" (Default) erzeugt bewusst keine eigene
   Regel — leer heißt "aktuelles Aussehen bleibt". */

/* .justify-content auf .site-nav-links--full allein reichte nicht: .site-nav
   selbst (ihr Elternelement) war nie breiter als sein Inhalt (kein flex:1),
   weil .site-header-inners justify-content:space-between .site-nav ohnehin
   schon an den rechten Rand schiebt — dadurch hatte .site-nav-links--full
   nie tatsächlich Platz, um sich innerhalb ihrer eigenen Box zu verschieben,
   und "Zentriert"/"Rechts" sahen optisch identisch zu "Links" aus. Beide
   Ebenen brauchen flex:1 (bewusst auf den Typ "horizontal" beschränkt,
   damit centered/sidebar/overlay ihr eigenes Layout behalten): .site-nav
   füllt damit den nach dem Logo verbleibenden Platz in der Kopfzeile,
   .site-nav-links--full darin wiederum den nach .site-nav-members
   verbleibenden Platz — erst in dieser (jetzt tatsächlich variablen) Box
   hat justify-content eine sichtbare Wirkung. */
body[data-nav-type="horizontal"] .site-nav {
    flex: 1;
}

body[data-nav-type="horizontal"] .site-nav-links--full {
    flex: 1;
}

body[data-nav-align="left"] .site-nav-links--full {
    justify-content: flex-start;
}

body[data-nav-align="center"] .site-nav-links--full {
    justify-content: center;
}

body[data-nav-align="right"] .site-nav-links--full {
    justify-content: flex-end;
}

/* gap/font-size gehören nicht mehr hierher — eigene Regler navGap/
   navFontSize, siehe .site-nav-links/.site-nav-links > li > a oben (früher
   setzten diese Presets beides zusätzlich fest mit, das konkurrierte mit
   den heutigen dedizierten Werten). navDensity bestimmt nur noch den
   Innenabstand der einzelnen Links. */
body[data-nav-density="compact"] .site-nav-links a {
    padding: 0.15rem 0.3rem;
}

body[data-nav-density="spacious"] .site-nav-links a {
    padding: 0.5rem 0.75rem;
}

/* :not([data-nav-type="sidebar"]) — eine fixe Sidebar ist bereits dauerhaft
   sichtbar, "sticky" wäre dort redundant und würde mit deren eigenem
   position:fixed kollidieren (beide setzen "position", "top" u. a.). */
body[data-nav-sticky="1"]:not([data-nav-type="sidebar"]) .site-header {
    position: sticky;
    top: 0;
    z-index: 40;
}

@media (max-width: 720px) {
    .site-header-inner > * {
        order: initial;
    }

    .site-nav-toggle {
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }

    /* Mobil sieht die Navigation für alle vier Typen wie das
       Vollbild-Overlay aus (auf Wunsch — vorher gab es hier ein eigenes,
       schlichteres Hamburger-Dropdown-Panel). Das repliziert im Kern exakt
       die unconditional body[data-nav-type="overlay"]-Regeln weiter oben,
       nur ohne die data-nav-type-Einschränkung. .site-nav-close (siehe
       dort) ist dafür seit Kurzem ohnehin immer im Markup vorhanden, nur
       standardmäßig display:none — hier wird sie einfach für alle Typen
       sichtbar statt nur für "overlay". */
    .site-nav-close {
        display: block;
        position: absolute;
        top: 1.5rem;
        right: 1.5rem;
        background: none;
        border: none;
        color: var(--nav-text);
        font-size: 2.2rem;
        line-height: 1;
        cursor: pointer;
    }

    /* body[data-nav-type] (Attribut vorhanden, unabhängig vom Wert) statt
       schlichtem .site-nav — dieselbe Selektor-"Stärke" wie z. B. die
       Sidebar-eigene, sonst unconditional geltende
       body[data-nav-type="sidebar"] .site-nav-Regel (die auch am Desktop
       gilt), damit die hiesige Regel bei gleicher Spezifität durch die
       spätere Position im Stylesheet gewinnt — ganz ohne !important. */
    body[data-nav-type] .site-nav {
        display: none;
        position: fixed;
        inset: 0;
        background: var(--nav-bg);
        flex-direction: column;
        justify-content: center;
        align-items: center;
        gap: 2rem;
        z-index: 100;
    }

    body[data-nav-type] .site-nav.is-open {
        display: flex;
    }

    body[data-nav-type] .site-nav-links {
        flex-direction: column;
        align-items: center;
        gap: 1.5rem;
    }

    .site-nav-links > li > a {
        font-size: 1.5rem;
    }

    body[data-nav-type] .site-nav-links .dropdown {
        position: static;
        box-shadow: none;
        border: none;
        background: none;
        align-items: center;
        padding: 0;
        margin: 0.5rem 0 0;
        /* Erzwingt einen Zeilenumbruch innerhalb des gleich flex-wrap:wrap
           gesetzten <li> (siehe li.has-dropdown unten) — dadurch landet die
           Unterebene garantiert unterhalb von Link + Pfeil-Button, egal wie
           viel Platz in deren gemeinsamer Zeile noch übrig wäre. */
        flex-basis: 100%;
    }

    /* Gleiche Kontrast-Falle wie bei Sidebar/Overlay am Desktop (siehe
       deren Anmerkungen weiter oben): .dropdown a ist normalerweise
       dunkel, weil ihr weißes Flyout-Kästchen das trägt — hier direkt
       vorher aber background:none, der dunkle Text säße dann direkt auf
       dem eingefärbten Vollbild-Panel. Jetzt für alle Typen relevant, weil
       mobil jetzt immer diese Panel-Optik gilt. */
    .site-nav-links .dropdown a {
        color: var(--nav-text);
    }

    /* <li> wird zur Flex-Zeile aus Link + Pfeil-Button; die Unterebene ist
       darin per flex-basis:100% (siehe oben) trotzdem im normalen Fluss
       gefangen und rutscht so automatisch in eine eigene, neue Zeile
       darunter — kein zusätzliches Wrapper-Element nötig, ".site-nav-links
       > li > a" (Direktkind-Selektor weiter oben) bleibt dadurch gültig. */
    .site-nav-links li.has-dropdown {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        justify-content: center;
        gap: 0.4rem;
    }

    /* Standardmäßig eingeklappt — erst der Pfeil-Button (nav_controller.js#toggleSubmenu)
       setzt is-expanded auf das jeweilige <li> und blendet dessen eigene
       Unterebene ein, unabhängig von allen anderen Punkten. */
    .site-nav-links li.has-dropdown .dropdown {
        display: none;
    }

    .site-nav-links li.has-dropdown.is-expanded .dropdown {
        display: flex;
    }

    .site-nav-expand {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        background: none;
        border: none;
        color: var(--nav-text);
        font-size: 1.1rem;
        padding: 0.2rem;
        cursor: pointer;
        transition: transform .15s ease;
    }

    .site-nav-links li.is-expanded > .site-nav-expand {
        transform: rotate(180deg);
    }

    .site-nav-members {
        flex-direction: column;
        align-items: center;
        gap: 0.75rem;
    }

    /* Sidebar/Sticky ergeben auf schmalen Bildschirmen keinen Sinn (kein
       Platz für eine feste Spalte, der Header ist ohnehin schon oben) —
       die Kopfzeile selbst läuft mobil bei allen vier Typen auf dieselbe
       einzeilige Leiste mit Hamburger-Button hinaus (das Vollbild-Panel
       oben betrifft nur das *geöffnete Menü*, nicht die Kopfzeile). */
    body[data-nav-type="sidebar"] .site-header {
        position: static;
        width: auto;
        border-right: none;
        border-bottom: 1px solid var(--color-border);
    }

    body[data-nav-type="sidebar"] .site-header-inner {
        flex-direction: row;
    }

    body[data-nav-type="sidebar"] .site-main {
        margin-left: 0;
    }

    /* Kontaktzeile folgt auf Mobile ebenfalls dem einheitlichen Verhalten
       aller Typen — die page-weite --top-Zeile statt der Sidebar-Variante. */
    body[data-nav-type="sidebar"] .site-contact-bar--top {
        display: block;
    }

    body[data-nav-type="sidebar"] .site-contact-bar--sidebar {
        display: none;
    }

    .site-contact-bar-inner {
        justify-content: center;
        text-align: center;
    }
}

/* ---------- Page container ---------- */

.site-main {
    max-width: 1100px;
    margin: 0 auto;
    padding: 2rem 1.25rem 4rem;
}

/* Verschoben aus page/show.html.twigs Inline-Style — die feste
   Gutter-Margin (-0.5rem) lässt sich sonst von keiner noch so
   spezifischen CSS-Klasse überschreiben (Inline-Style schlägt jede
   externe Regel außer !important), .row--full-width unten braucht das
   aber, um die Ausbrechen-Margin stattdessen zu setzen. Reiner
   Ersatz ohne Verhaltensänderung für bestehende Reihen. */
.row {
    display: flex;
    flex-wrap: wrap;
    margin: 0 -0.5rem 1rem;
}

/* Bricht die Reihe aus .site-mains zentrierter max-width-Spalte aus,
   z. B. für ein randloses Header-Bild (Tenant-unabhängig, pro Reihe via
   Row::$fullWidth). Die Formel wirkt unabhängig von .site-mains
   padding/max-width, weil symmetrisches Padding die horizontale Mitte
   nicht verschiebt — sie trifft die Viewport-Mitte unabhängig davon, wie
   tief die Reihe in zentrierte Container verschachtelt ist. Block-Gutter
   (0.5rem Innenabstand pro Block) bleibt unangetastet, auch bei mehreren
   nebeneinanderliegenden Blöcken in einer Vollbreite-Reihe soll der
   Abstand zwischen ihnen erhalten bleiben. */
.row.row--full-width {
    width: 100vw;
    margin-left: calc(50% - 50vw);
    margin-right: calc(50% - 50vw);
}

/* ---------- Buttons & form fields (element selectors so every block/
   form template picks these up without being touched individually) ---------- */

button,
.button,
input[type="submit"] {
    display: inline-block;
    font-family: inherit;
    font-size: 0.95rem;
    font-weight: 600;
    color: #fff;
    background: var(--color-primary);
    border: none;
    border-radius: var(--radius-sm);
    padding: 0.6em 1.2em;
    cursor: pointer;
    transition: background-color 0.15s ease;
    text-decoration: none;
}

button:hover,
.button:hover,
input[type="submit"]:hover {
    background: var(--color-accent-hover);
}

button:disabled {
    background: var(--color-text-muted);
    cursor: not-allowed;
}

input:not([type="submit"]):not([type="checkbox"]):not([type="radio"]),
select,
textarea {
    font-family: inherit;
    font-size: 0.95rem;
    color: var(--color-text);
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    padding: 0.55em 0.7em;
}

input:focus,
select:focus,
textarea:focus {
    outline: 2px solid var(--color-primary);
    outline-offset: 1px;
}

label {
    font-weight: 600;
    font-size: 0.9rem;
}

.form-honeypot {
    position: absolute;
    left: -9999px;
}

.form-success,
.form-error {
    padding: 0.75em 1em;
    border-radius: var(--radius-sm);
    margin: 0 0 1em;
}

.form-success {
    color: #1f6b4f;
    background: #eafaf3;
    border: 1px solid #b8e6d1;
}

.form-error {
    color: var(--color-danger);
    background: #fceceb;
    border: 1px solid #f2c6c2;
}

/* ---------- Fixtures / standings tables ---------- */

.fixtures-table,
.standings-table {
    width: 100%;
    border-collapse: collapse;
    background: var(--color-bg);
}

.fixtures-table td {
    padding: 0.6em 0.5em;
    border-bottom: 1px solid var(--color-border);
    vertical-align: top;
}

.fixtures-table tr:nth-child(even) {
    background: var(--color-surface);
}

.fixtures-date {
    white-space: nowrap;
}

.fixtures-time,
.fixtures-competition {
    font-size: 0.85em;
    opacity: 0.7;
}

.fixtures-own {
    font-weight: bold;
    color: var(--color-primary);
}

.fixtures-score {
    text-align: right;
    white-space: nowrap;
    font-weight: bold;
}

.standings-table th,
.standings-table td {
    padding: 0.5em;
    border-bottom: 1px solid var(--color-border);
    text-align: center;
}

.standings-table th {
    color: var(--color-text-muted);
    font-size: 0.85em;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.standings-table tr:nth-child(even) {
    background: var(--color-surface);
}

.standings-table th:nth-child(2),
.standings-table td:nth-child(2) {
    text-align: left;
}

.standings-own {
    font-weight: bold;
    color: var(--color-primary);
}

@media (max-width: 480px) {
    .standings-hide-small {
        display: none;
    }
}

/* ---------- News block ---------- */

.news-list {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.news-list-item {
    display: flex;
    gap: 1rem;
    align-items: flex-start;
    text-decoration: none;
    color: inherit;
    padding: 0.75rem;
    border-radius: var(--radius-md);
    transition: box-shadow 0.15s ease;
}

.news-list-item:hover {
    box-shadow: var(--shadow-sm);
}

.news-list-image {
    width: 120px;
    height: 90px;
    object-fit: cover;
    flex-shrink: 0;
    border-radius: var(--radius-sm);
}

.news-list-date {
    font-size: 0.85em;
    opacity: 0.7;
}

.news-list-title {
    margin: 0.2em 0;
}

.news-list-teaser {
    margin: 0;
}

.news-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 1.25rem;
}

.news-grid-item {
    display: flex;
    flex-direction: column;
    flex: 1 1 220px;
    max-width: 320px;
    text-decoration: none;
    color: inherit;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    overflow: hidden;
    box-shadow: var(--shadow-sm);
    transition: box-shadow 0.15s ease, transform 0.15s ease;
}

.news-grid-item:hover {
    box-shadow: var(--shadow-md);
    transform: translateY(-2px);
}

.news-grid-item .news-list-body {
    padding: 0.9rem 1rem;
}

.news-grid-image {
    width: 100%;
    height: 160px;
    object-fit: cover;
}

.news-article-date {
    font-size: 0.9em;
    opacity: 0.7;
}

.news-article-image {
    max-width: 100%;
    height: auto;
    display: block;
    margin: 1rem 0;
    border-radius: var(--radius-md);
}

/* ---------- Kurse (courses) block ---------- */

.course-list {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.course-list-item {
    display: flex;
    gap: 1rem;
    align-items: flex-start;
    text-decoration: none;
    color: inherit;
    padding: 0.75rem;
    border-radius: var(--radius-md);
    transition: box-shadow 0.15s ease;
}

.course-list-item:hover {
    box-shadow: var(--shadow-sm);
}

.course-list-image {
    width: 120px;
    height: 90px;
    object-fit: cover;
    flex-shrink: 0;
    border-radius: var(--radius-sm);
}

.course-list-date {
    font-size: 0.85em;
    opacity: 0.7;
}

.course-list-title {
    margin: 0.2em 0;
}

.course-list-teaser {
    margin: 0;
}

.course-list-price {
    font-size: 0.85em;
    font-weight: bold;
    color: var(--color-primary);
}

.course-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 1.25rem;
}

.course-grid-item {
    display: flex;
    flex-direction: column;
    flex: 1 1 220px;
    max-width: 320px;
    text-decoration: none;
    color: inherit;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    overflow: hidden;
    box-shadow: var(--shadow-sm);
    transition: box-shadow 0.15s ease, transform 0.15s ease;
}

.course-grid-item:hover {
    box-shadow: var(--shadow-md);
    transform: translateY(-2px);
}

.course-grid-item .course-list-body {
    padding: 0.9rem 1rem;
}

.course-grid-image {
    width: 100%;
    height: 160px;
    object-fit: cover;
}

.course-article-image {
    max-width: 100%;
    height: auto;
    display: block;
    margin: 1rem 0;
    border-radius: var(--radius-md);
}

.course-article-meta {
    background: var(--color-surface);
    border-radius: var(--radius-md);
    padding: 1rem 1.25rem;
    margin: 1rem 0;
}

.course-article-sessions {
    margin: 0.2em 0 1rem;
}

.course-booking-full {
    color: var(--color-danger);
    font-weight: bold;
}

/* ---------- Ressourcen (Räume/Plätze/Gegenstände) ---------- */

.resource-list {
    display: flex;
    flex-wrap: wrap;
    gap: 1.25rem;
}

.resource-list-item {
    display: flex;
    flex-direction: column;
    flex: 1 1 220px;
    max-width: 320px;
    text-decoration: none;
    color: inherit;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    overflow: hidden;
    box-shadow: var(--shadow-sm);
    transition: box-shadow 0.15s ease, transform 0.15s ease;
}

.resource-list-item:hover {
    box-shadow: var(--shadow-md);
    transform: translateY(-2px);
}

.resource-list-image {
    width: 100%;
    height: 160px;
    object-fit: cover;
}

.resource-list-body {
    display: flex;
    flex-direction: column;
    padding: 0.9rem 1rem;
}

.resource-list-location {
    font-size: 0.85em;
    opacity: 0.7;
}

.resource-list-title {
    margin: 0.2em 0;
}

.resource-list-price {
    font-size: 0.85em;
    font-weight: bold;
    color: var(--color-primary);
}

.resource-article-image {
    max-width: 100%;
    height: auto;
    display: block;
    margin: 1rem 0;
    border-radius: var(--radius-md);
}

.resource-article-meta {
    background: var(--color-surface);
    border-radius: var(--radius-md);
    padding: 1rem 1.25rem;
    margin: 1rem 0;
}

.resource-slot-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.resource-slot-item {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: 0.85rem 1rem;
}

.resource-slot-time {
    font-weight: bold;
    margin-bottom: 0.3em;
}

.resource-slot-full-notice {
    color: var(--color-danger);
    font-weight: bold;
}

/* ---------- Ansprechpartner (contacts) block + course-article contacts ---------- */

.contacts-list,
.course-article-contacts {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.contacts-item,
.course-article-contact {
    display: flex;
    gap: 0.85rem;
    align-items: center;
}

.contacts-photo,
.course-article-contact-photo {
    width: 64px;
    height: 64px;
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
}

.contacts-body,
.course-article-contact-body {
    display: flex;
    flex-direction: column;
}

.contacts-name,
.course-article-contact-name {
    font-weight: bold;
}

.contacts-role,
.course-article-contact-role {
    font-size: 0.9em;
    opacity: 0.75;
}

.contacts-detail,
.course-article-contact-detail {
    font-size: 0.9em;
    color: inherit;
}

/* ---------- Slider block ---------- */

.slider-block {
    position: relative;
}

.slider-slides {
    position: relative;
    width: 100%;
    height: 400px;
    overflow: hidden;
    border-radius: var(--radius-md);
    /* Only visible as letterboxing behind a "contain" image — cover always
       fills the frame and hides it. */
    background: var(--color-surface);
}

.slider-slide {
    position: absolute;
    inset: 0;
    opacity: 0;
    pointer-events: none;
}

.slider-slide.is-active {
    opacity: 1;
    pointer-events: auto;
}

/* 'none' — hard cut, no transition at all. */

/* 'fade' (default) — the crossfade this block always had before the
   transition choice existed, kept as its own rule so existing sliders
   (config defaults to 'fade') look exactly the same as before. */
.slider-block-transition-fade .slider-slide {
    transition: opacity 0.4s ease;
}

/* 'slide' — horizontal push. Slides stay stacked exactly like 'fade'/'none'
   (position: absolute, inset: 0, same box) — only opacity is forced to 1 and
   each slide gets its own translateX offset from the Stimulus controller
   instead of toggling opacity. Deliberately NOT a flex track with a single
   transform on the container: that approach hit a repaint bug in real
   Chrome/Edge (computed styles were correct — 100% width, correct
   translateX — but the image never actually repainted, leaving stale
   pixels on screen). Animating each already-absolutely-positioned slide
   individually reuses the exact stacking this block already had before
   transitions existed, which is known to repaint correctly.
*/
.slider-block-transition-slide .slider-slide {
    opacity: 1;
    transition: transform 0.5s ease;
}

.slider-slide-image {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/* "Vollständig" — whole image visible, no cropping. Overrides the default
   cover above; letterboxing shows .slider-slides' background. */
.slider-block-fit-contain .slider-slide-image {
    object-fit: contain;
}

.slider-slide-text {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    margin: 0;
    padding: 0.75em 1em;
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
}

/* "Neben dem Bild" variant — text sits beside the image instead of
   overlaid on top of it. flex: 1 1 0 (not a fixed 50%) so a slide with
   only an image or only text naturally takes the full width. */
.slider-block-side .slider-slide {
    display: flex;
    align-items: stretch;
    background: var(--color-surface);
}

/* Der Lightbox-Trigger-Button (nicht mehr das <img> direkt) ist jetzt das
   direkte Flex-Kind von .slider-slide — die Flex-Eigenschaften wandern
   deshalb dorthin, das <img> darin füllt einfach 100% davon (siehe
   .slider-slide-image-trigger weiter oben). */
.slider-block-side .slider-slide-image-trigger {
    flex: 1 1 0;
    width: auto;
    min-width: 0;
}

.slider-block-side .slider-slide-text {
    flex: 1 1 0;
    min-width: 0;
    position: static;
    margin: 0;
    padding: 1.5rem;
    background: none;
    color: var(--color-text);
    overflow-y: auto;
}

@media (max-width: 720px) {
    .slider-block-side .slider-slide {
        flex-direction: column;
        overflow-y: auto;
    }

    .slider-block-side .slider-slide-image-trigger {
        flex: 0 0 45%;
        width: 100%;
    }
}

.slider-nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 2.2em;
    height: 2.2em;
    border: none;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.4);
    color: #fff;
    font-size: 1.4em;
    line-height: 1;
    cursor: pointer;
    transition: background-color 0.15s ease;
}

.slider-nav:hover {
    background: rgba(0, 0, 0, 0.65);
}

.slider-nav-prev {
    left: 0.5em;
}

.slider-nav-next {
    right: 0.5em;
}

.slider-dots {
    position: absolute;
    bottom: 0.6em;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    gap: 0.4em;
}

.slider-dot {
    width: 0.6em;
    height: 0.6em;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.6);
    cursor: pointer;
    transition: background-color 0.15s ease;
}

.slider-dot:hover {
    background: rgba(255, 255, 255, 0.85);
}

.slider-dot.is-active {
    background: #fff;
}

/* ---------- Kalender (calendar) block ---------- */

.calendar-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.calendar-list-item {
    border-bottom: 1px solid var(--color-border);
    padding-bottom: 1rem;
}

.calendar-list-date {
    font-size: 0.85em;
    opacity: 0.7;
}

.calendar-list-title {
    margin: 0.2em 0;
}

.calendar-list-description {
    margin: 0.2em 0;
}

.calendar-list-location {
    margin: 0.2em 0;
    font-size: 0.9em;
    opacity: 0.85;
}

.calendar-list-tags {
    margin: 0;
    font-size: 0.85em;
    opacity: 0.7;
}

.calendar-grid {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 1.25rem;
}

.calendar-grid-item {
    flex: 1 1 220px;
    max-width: 320px;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: 0.9rem 1rem;
    box-shadow: var(--shadow-sm);
    transition: box-shadow 0.15s ease, transform 0.15s ease;
}

.calendar-grid-item:hover {
    box-shadow: var(--shadow-md);
    transform: translateY(-2px);
}

.calendar-badge-members {
    font-size: 0.7em;
    font-weight: 600;
    background: var(--color-accent);
    color: #fff;
    padding: 0.15em 0.6em;
    border-radius: 999px;
    vertical-align: middle;
}

/* ---------- Downloads block ---------- */

.downloads-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.downloads-list-item {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
}

.downloads-list-link {
    display: block;
    padding: 0.75rem 1rem;
    text-decoration: none;
    color: var(--color-text);
    font-weight: 600;
}

.downloads-list-link:hover {
    background: var(--color-surface);
    color: var(--color-accent);
}

/* ---------- Audio/Video blocks ---------- */

.audio-block-title,
.video-block-title {
    margin: 0 0 0.4em;
    font-weight: 600;
}

.audio-block-player {
    width: 100%;
}

.audio-block-embed {
    width: 100%;
    height: 180px;
    border: 0;
    border-radius: var(--radius-md);
}

.video-block-player {
    width: 100%;
    display: block;
    border-radius: var(--radius-md);
}

/* Classic responsive-iframe technique: the wrapper reserves 16:9 space via
   aspect-ratio, the iframe itself fills it via absolute positioning — needed
   because a YouTube/Vimeo embed URL has no intrinsic size of its own the way
   an <img>/<video> has, unlike .video-block-player above. */
.video-block-embed-wrapper {
    position: relative;
    width: 100%;
    aspect-ratio: 16 / 9;
}

.video-block-embed {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border: 0;
    border-radius: var(--radius-md);
}

/* ---------- Gallery block ---------- */

.gallery-grid {
    display: grid;
    gap: 1rem;
}

/* One class per breakpoint instead of a single column count — editors pick
   mobile/tablet/desktop values independently (Gallery::$columnsMobile/
   Tablet/Desktop, same three-tier idea and the same 768px/1024px
   breakpoints as Block::$widthMobile/Tablet/Desktop). Base (unprefixed)
   rules are the mobile tier — mobile-first, no media query needed for it. */
.gallery-grid-cols-mobile-1 {
    grid-template-columns: repeat(1, 1fr);
}

.gallery-grid-cols-mobile-2 {
    grid-template-columns: repeat(2, 1fr);
}

.gallery-grid-cols-mobile-3 {
    grid-template-columns: repeat(3, 1fr);
}

.gallery-grid-cols-mobile-4 {
    grid-template-columns: repeat(4, 1fr);
}

@media (min-width: 768px) {
    .gallery-grid-cols-tablet-1 {
        grid-template-columns: repeat(1, 1fr);
    }

    .gallery-grid-cols-tablet-2 {
        grid-template-columns: repeat(2, 1fr);
    }

    .gallery-grid-cols-tablet-3 {
        grid-template-columns: repeat(3, 1fr);
    }

    .gallery-grid-cols-tablet-4 {
        grid-template-columns: repeat(4, 1fr);
    }
}

@media (min-width: 1024px) {
    .gallery-grid-cols-desktop-1 {
        grid-template-columns: repeat(1, 1fr);
    }

    .gallery-grid-cols-desktop-2 {
        grid-template-columns: repeat(2, 1fr);
    }

    .gallery-grid-cols-desktop-3 {
        grid-template-columns: repeat(3, 1fr);
    }

    .gallery-grid-cols-desktop-4 {
        grid-template-columns: repeat(4, 1fr);
    }
}

.gallery-figure {
    margin: 0;
}

/* A button, not a div, so the lightbox is reachable by keyboard — the
   default button chrome is reset away here. */
.gallery-item {
    display: block;
    width: 100%;
    padding: 0;
    border: none;
    border-radius: var(--radius-md);
    overflow: hidden;
    background: var(--color-surface);
    cursor: pointer;
}

/* Fixed square crop: club photos come in wildly different aspect ratios,
   and without it every row of the grid would have a different height. */
.gallery-image {
    display: block;
    width: 100%;
    aspect-ratio: 1;
    object-fit: cover;
    transition: transform 0.2s ease;
}

.gallery-item:hover .gallery-image {
    transform: scale(1.04);
}

.gallery-caption {
    margin: 0.4rem 0 0;
    font-size: 0.85rem;
    color: var(--color-text-muted);
}

/* "Vollständig" — whole image visible, no cropping. Letterboxing shows
   .gallery-item's own background (var(--color-surface), set above). */
.gallery-block-fit-contain .gallery-image {
    object-fit: contain;
}

/* Masonry: plain CSS multi-column layout instead of CSS grid — tiles keep
   their natural height, so photos of different proportions don't get
   cropped into a uniform square. Reuses the existing .gallery-grid-cols-*
   classes for the column count rather than adding a second set. */
.gallery-block-masonry .gallery-grid {
    display: block;
    column-gap: 1rem;
}

.gallery-block-masonry .gallery-grid-cols-mobile-1 {
    columns: 1;
}

.gallery-block-masonry .gallery-grid-cols-mobile-2 {
    columns: 2;
}

.gallery-block-masonry .gallery-grid-cols-mobile-3 {
    columns: 3;
}

.gallery-block-masonry .gallery-grid-cols-mobile-4 {
    columns: 4;
}

@media (min-width: 768px) {
    .gallery-block-masonry .gallery-grid-cols-tablet-1 {
        columns: 1;
    }

    .gallery-block-masonry .gallery-grid-cols-tablet-2 {
        columns: 2;
    }

    .gallery-block-masonry .gallery-grid-cols-tablet-3 {
        columns: 3;
    }

    .gallery-block-masonry .gallery-grid-cols-tablet-4 {
        columns: 4;
    }
}

@media (min-width: 1024px) {
    .gallery-block-masonry .gallery-grid-cols-desktop-1 {
        columns: 1;
    }

    .gallery-block-masonry .gallery-grid-cols-desktop-2 {
        columns: 2;
    }

    .gallery-block-masonry .gallery-grid-cols-desktop-3 {
        columns: 3;
    }

    .gallery-block-masonry .gallery-grid-cols-desktop-4 {
        columns: 4;
    }
}

.gallery-block-masonry .gallery-figure {
    /* Keeps a tile from splitting across two columns. */
    break-inside: avoid;
    margin: 0 0 1rem;
}

.gallery-block-masonry .gallery-image {
    /* Overrides the grid layout's fixed square crop — the point of masonry
       is that tiles keep their own proportions. Makes imageFit moot here:
       with no fixed box to crop into, cover and contain look identical. */
    aspect-ratio: auto;
    height: auto;
}

.gallery-lightbox {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 50;
    background: rgba(0, 0, 0, 0.85);
    padding: 3rem 1rem;
    text-align: center;
}

.gallery-lightbox.is-open {
    display: block;
}

.gallery-lightbox-image {
    max-width: 100%;
    max-height: calc(100vh - 8rem);
    object-fit: contain;
    border-radius: var(--radius-sm);
}

.gallery-lightbox-caption {
    margin: 0.75rem 0 0;
    color: #fff;
}

.gallery-lightbox-close,
.gallery-lightbox-nav {
    position: absolute;
    background: rgba(0, 0, 0, 0.4);
    color: #fff;
    border: none;
    border-radius: var(--radius-sm);
    font-size: 1.75rem;
    line-height: 1;
    padding: 0.2em 0.5em;
    cursor: pointer;
}

.gallery-lightbox-close {
    top: 1rem;
    right: 1rem;
}

.gallery-lightbox-nav {
    top: 50%;
    transform: translateY(-50%);
}

.gallery-lightbox-prev {
    left: 1rem;
}

.gallery-lightbox-next {
    right: 1rem;
}


/* ---------- Shared lightbox (image + slider blocks) ---------- */
/* Content-wise a copy of .gallery-lightbox* above under a neutral name —
   the gallery block keeps its own, unrelated .gallery-lightbox* rules
   untouched, this is what lightbox_controller.js (image/slider blocks)
   targets instead. See DEVELOPER.md for why this isn't shared with the
   gallery block's own controller/CSS. */

.image-block-trigger,
.slider-slide-image-trigger {
    display: block;
    width: 100%;
    height: 100%;
    padding: 0;
    border: none;
    background: none;
    cursor: pointer;
    overflow: hidden;
}

.image-block-trigger img {
    transition: transform 0.2s ease;
}

.image-block-trigger:hover img {
    transform: scale(1.02);
}

.lightbox-overlay {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 50;
    background: rgba(0, 0, 0, 0.85);
    padding: 3rem 1rem;
    text-align: center;
}

.lightbox-overlay.is-open {
    display: block;
}

.lightbox-image {
    max-width: 100%;
    max-height: calc(100vh - 8rem);
    object-fit: contain;
    border-radius: var(--radius-sm);
}

.lightbox-caption {
    margin: 0.75rem 0 0;
    color: #fff;
}

.lightbox-caption:empty {
    margin: 0;
}

.lightbox-close,
.lightbox-nav {
    position: absolute;
    background: rgba(0, 0, 0, 0.4);
    color: #fff;
    border: none;
    border-radius: var(--radius-sm);
    font-size: 1.75rem;
    line-height: 1;
    padding: 0.2em 0.5em;
    cursor: pointer;
}

.lightbox-close {
    top: 1rem;
    right: 1rem;
}

.lightbox-nav {
    top: 50%;
    transform: translateY(-50%);
}

.lightbox-prev {
    left: 1rem;
}

.lightbox-next {
    right: 1rem;
}

/* ---------- Instagram block: profile header ---------- */

/* Modelled on Instagram's own profile layout: round avatar left, handle,
   counts, name and bio right. Everything shown here is served from this
   server — the avatar is downloaded during the sync like the posts. */
.instagram-profile {
    display: flex;
    align-items: flex-start;
    gap: 1.5rem;
    padding-bottom: 1.25rem;
    margin-bottom: 1.25rem;
    border-bottom: 1px solid var(--color-border);
}

.instagram-avatar {
    flex: none;
    width: 96px;
    height: 96px;
    border-radius: 50%;
    object-fit: cover;
    display: block;
}

.instagram-profile-body {
    min-width: 0;
}

.instagram-profile-head {
    display: flex;
    align-items: center;
    gap: 1rem;
    flex-wrap: wrap;
}

.instagram-username {
    font-size: 1.15rem;
    font-weight: 600;
}

.instagram-follow {
    font-size: 0.85rem;
    padding: 0.35em 1em;
}

.instagram-stats {
    display: flex;
    gap: 1.5rem;
    margin: 0.6rem 0;
    color: var(--color-text-muted);
    font-size: 0.95rem;
}

.instagram-stats strong {
    color: var(--color-text);
}

.instagram-name {
    font-weight: 600;
}

.instagram-bio {
    margin: 0.25rem 0 0;
}

.instagram-website {
    font-weight: 600;
    font-size: 0.95rem;
}

@media (max-width: 480px) {
    .instagram-profile {
        gap: 1rem;
    }

    .instagram-avatar {
        width: 64px;
        height: 64px;
    }

    .instagram-stats {
        gap: 1rem;
        font-size: 0.9rem;
    }
}
