/* ==========================================================================
   SITE HEADER
   Liegt fest über dem Inhalt (position: fixed), nimmt daher keinen Platz
   im normalen Fluss ein. Der Ausgleich passiert über:
   - main > .section:first-child { padding-top: var(--header-height) }
   - .has-anchor-offset { scroll-margin-top: var(--header-height) }
   Beide Werte werden von assets/js/nav.js zur Laufzeit exakt mit der
   gerenderten Header-Höhe (inkl. padding-block) synchronisiert, siehe
   Vorgabe: "Abstand oben = Abstand unten" für die Anker-Positionierung.
   ========================================================================== */

.site-header {
  position: fixed;
  inset-inline: 0;
  top: 0;
  z-index: 100;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--space-md);
  margin-top: var(--space-sm);
  padding-block: var(--space-md);
  padding-inline: var(--container-padding);

  /* Glas-Look der Nav-Buttons (s. .site-header .btn unten), an Apple-
     "Liquid Glass" bzw. der nav-bar__container-Referenz auf
     itsnicethat.com orientiert. Blur/Sättigung/Schatten kommen aus den
     site-weiten --glass-*-Tokens (tokens.css, wird von html-start.php
     immer VOR dieser Datei geladen) — die --nav-btn-*-Aliase bleiben als
     Header-Stellschrauben erhalten (der pointer:coarse-Block unten
     drosselt z.B. nur den Header, nicht die anderen Glas-Flächen). */
  --nav-btn-bg-opacity: 90%;
  --nav-btn-blur: var(--glass-blur);
  --nav-btn-saturate: var(--glass-saturate);
  --nav-btn-shadow: var(--glass-shadow);
  /* Single Source of Truth für die Button-Höhe: von .site-header__nav-list
     .btn UND .site-header__menu-toggle referenziert (beide sollen exakt
     --nav-btn-height hoch sein, s. tokens.css), außerdem 1:1 im
     3.58-Faktor dort verrechnet (1 + 2 * 1.29). Änderung hier wirkt
     automatisch an allen drei Stellen – nur der Faktor in tokens.css bleibt
     manuell zu pflegen, da --nav-btn-height am :root definiert ist und ein
     dort ausgewertetes em nicht mehr --fs-nav-btn als Bezugsgröße hätte. */
  --nav-btn-padding-block: 1.29em;
}

/* Dark-Theme-Schatten kommt automatisch über --glass-shadow (tokens.css)
   mit — kein eigener Override mehr nötig. */

/* Touch-Geräte: abgeschwächter Glas-Look. backdrop-filter auf dem FIXED
   Header wird beim Scrollen jeden Frame über dem bewegten Untergrund neu
   gerendert — blur(26px) saturate(400%) ist auf Mobile-GPUs eine der
   teuersten Dauerlasten der Seite und trug messbar zum ruckeligen
   Scrollen bei. Reduzierte Werte behalten den Look, kosten aber einen
   Bruchteil (Blur-Kosten wachsen mit dem Radius). Desktop unverändert. */
@media (pointer: coarse) {
  .site-header {
    --nav-btn-blur: 10px;
    --nav-btn-saturate: 180%;
  }
}

/* position:relative + overflow:hidden kommen bereits von .color-swap
   (components.css) – das Video/die Farb-Ebenen werden dort exakt auf den
   Radius der Pille zugeschnitten.
   isolation: isolate als Firefox-Workaround gegen Mozilla Bug 1787089
   ("Fix interactions between root level mix-blend containers and
   backdrop-filter"): .site-header__logo-text (s.u.) setzt mix-blend-mode,
   hat aber selbst KEINEN Vorfahren, der laut Spec einen "backdrop root"
   bildet (kein filter/opacity/mask/backdrop-filter/isolation dazwischen) –
   seine Blend-Reichweite geht dadurch bis zum Wurzelelement hoch ("root
   level"), und genau das brachte laut Bugzilla Firefox' backdrop-filter für
   andere Elemente im selben (bei position:fixed vermutlich gemeinsam
   kompositierten) Layer durcheinander – hier konkret die Glas-Buttons
   .site-header__nav-list .btn, obwohl sie nur Geschwister sind, kein
   Nachfahre des Logos. Der frühere Ausgleich dafür (Deckkraft der Buttons
   NUR in Firefox auf 95% erhöht, s. Git-Historie) ist seit diesem Fix
   entfernt – Firefox nutzt jetzt denselben Wert wie alle anderen Browser
   (s. --nav-btn-bg-opacity: 75% oben).
   isolation: isolate erzeugt selbst einen offiziellen "backdrop root"
   (s. MDN backdrop-filter) direkt an diesem Element – das Blending bleibt
   dadurch auf den Inhalt INNERHALB der Pille begrenzt (Video/GIF/
   Farb-Ebenen, s. .color-swap-Kinder), reißt Firefox' backdrop-filter für
   die Nav-Buttons daneben also nicht mehr root-level mit. Nebeneffekt:
   der Invert-Effekt blendet dadurch gegen den Pillen-Hintergrund statt
   (wie bisher) durch den transparenten Header hindurch gegen die Seite
   dahinter – das ist gewollt hingenommen, s. .site-header__logo-text unten. */
.site-header__logo {
  isolation: isolate;
  justify-self: start;
  display: inline-flex;
  align-items: center;

  /* isolation: isolate oben rendert Video + invertierte Schrift (s.
     .site-header__logo-text) als fertig kompositiertes Bild, BEVOR filter
     hier greift (Spec-Reihenfolge: filter nach Blend/Compositing des
     Elements) – saturate() wirkt dadurch auf die fertig invertierten
     Mischfarben statt (wirkungslos) auf reines Weiß der Schrift selbst. */
  filter: saturate(120%);

  /* Schwarze Pille wie die aktiven Nav-Buttons (s. .site-header__nav-list
     .btn[aria-current="page"] unten) – kein Blur nötig, deckende Fläche.
     Bleibt als Fallback sichtbar, solange kein Panel-Video/-GIF gesetzt ist
     (s. --logoBackground, site-header.php). */
  background: var(--color-nav-button-active-background);
  border-radius: var(--radius-sm);
  /* WebKit-Bug: sobald das autoplay-<video> (s. .site-header__logo-media
     unten) auf eine eigene Compositing-Ebene gehoben wird, ignoriert Safari
     auf iOS den overflow:hidden-Radius-Clip von .color-swap – die Pille
     wirkt beim Reload kurz eckig, kurz nachdem sie korrekt rund gestartet
     ist. Jede -webkit-mask-image-Deklaration zwingt Safari, den Clip auch
     für die promotete Video-Ebene konsequent anzuwenden. */
  -webkit-mask-image: -webkit-radial-gradient(white, black);
  mask-image: radial-gradient(white, black);
  box-shadow: var(--nav-btn-shadow);
  font-family: var(--font-pixel-grotesk);
  font-weight: var(--fw-bold);
  /* --logo-scale/--logo-font-size (tokens.css, jetzt am :root statt lokal
     hier definiert – .section-heading__word in components.css referenziert
     denselben Wert, damit die Section-Heading-Badges immer exakt so groß
     wie das Logo laufen) wirkt NUR auf font-size (s.u.) – die Pille selbst
     bleibt exakt --nav-btn-height hoch, identisch zu den Nav-/Menu-Buttons
     (s. .site-header__nav-list .btn/.site-header__menu-toggle), nur die
     Schrift darin soll kleiner wirken als die volle Pillenhöhe. */
  font-size: var(--logo-font-size);
  line-height: 1;
  /* Seitlicher Luftraum links/rechts der Schrift – in em (relativ zur
     eigenen font-size oben), damit der Abstand mit --logo-scale mitskaliert
     statt bei jeder Größenänderung neu abgeglichen werden zu müssen. */
  padding-inline: 0.3em;
  /* Restfläche (--nav-btn-height minus Schrift) je zur Hälfte oben/unten als
     padding-block – die Pillenhöhe bleibt dadurch weiterhin exakt
     --nav-btn-height (padding-inline oben wirkt nur horizontal, ändert die
     Höhenrechnung hier also nicht).
     ±--font-pixel-grotesk-nudge (tokens.css) verschiebt dabei asymmetrisch
     nach unten – exakt dasselbe Muster wie bei .site-header .btn (s.
     padding-block dort): oben minus/unten plus derselbe Betrag hebt sich in
     der SUMME exakt auf (Gesamthöhe bleibt --nav-btn-height), nur der
     optische Vertikal-Ausgleich für --font-pixel-grotesk bleibt erhalten.
     Getrennte Variable statt direkt in padding-block, weil derselbe
     Half-Diff-Wert oben UND unten gebraucht wird. */
  --logo-vpad: calc((var(--nav-btn-height) - var(--logo-font-size)) / 2);
  padding-block: calc(var(--logo-vpad) - var(--font-pixel-grotesk-nudge)) calc(var(--logo-vpad) + var(--font-pixel-grotesk-nudge));
}

/* Unskalierter Wrapper um .site-header__logo-media (s.u.) – reine
   Positionierungshülle, deckt exakt die Pillenfläche ab (wie zuvor das
   Video/GIF selbst). Existiert nur, damit der Hero-Reveal-Clip (s.
   .logo-hero-reveal unten) unabhängig vom transform:scale() dort auf einer
   unskalierten Box ansetzen kann – sonst würde der Clip auf der SKALIERTEN
   Box greifen und dadurch bei jedem künftigen Scale-Wert wieder anders
   (meist zu abrupt) durch die Pille laufen. */
.site-header__logo-media-wrap {
  position: absolute;
  inset: 0;
}

/* Video/GIF aus dem Panel (Design > Header > "Logo-Hintergrund"), füllt die
   Pille komplett, liegt hinter der Schrift. Läuft beim Hovern WEITER, wird
   per .color-swap__layer (components.css) nur eingefärbt. */
.site-header__logo-media {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Video wird 5x vergrößert dargestellt, bleibt aber durch overflow:hidden
     der .color-swap-Pille (components.css) auf den Rahmen begrenzt. */
  transform: scale(1);
  transform-origin: center;
  /* Kein <video controls> – ohne pointer-events bliebe das Video trotzdem
     als eigenes Touch-Ziel ansprechbar (Long-Press/Doppel-Tap kann auf
     einigen mobilen Browsern ein natives Vollbild-/Cast-Kontextmenü
     anbieten, s. auch .hero__video in hero.css). pointer-events: none lässt
     Taps zum umschließenden <a class="site-header__logo"> durchfallen, der
     Klick zur Startseite bleibt dadurch unangetastet funktionsfähig. */
  pointer-events: none;

}

/* --- Logo Hero-Reveal (Test-Feature, Ein/Aus: $logoHeroRevealEnabled in
   site-header.php) -----------------------------------------------------
   Solange .site-header die Klasse "logo-hero-reveal" trägt (s.o.), bleibt
   das Panel-Video/-GIF per clip-path verdeckt – übrig bleibt die schwarze
   Fallback-Fläche (--color-nav-button-active-background, s.
   .site-header__logo oben) mit weißer Schrift (--color-white, s.
   .site-header__logo-text oben), identisch zu den aktiven Header-Buttons.
   nav.js beobachtet #hero und setzt "is-past-hero", sobald der Hero unter
   den Header durchgescrollt ist (oder sofort auf Seiten ohne #hero) – der
   Clip fährt dann von rechts nach 0 zurück, das Video wischt sichtbar von
   links nach rechts ins Logo (gleiches Prinzip wie das translateX der
   Hover-Farbebenen, .color-swap__layer oben, nur per clip-path statt
   transform, da hier eine wachsende Fläche statt einer festen Ebene
   einblendet). Bleibt so, bis zurück zum Hero gescrollt wird. */
.logo-hero-reveal .site-header__logo-media-wrap {
  clip-path: inset(0 100% 0 0);
  transition: clip-path var(--duration-slow) var(--ease-standard);
}

.logo-hero-reveal.is-past-hero .site-header__logo-media-wrap {
  clip-path: inset(0 0 0 0);
}

/* Bewegungsreduktion: kein Wipe, Video bleibt (wie ohne dieses Feature)
   einfach dauerhaft sichtbar. */
@media (prefers-reduced-motion: reduce) {
  .logo-hero-reveal .site-header__logo-media-wrap {
    clip-path: none;
    transition: none;
  }
}

/* Weiß + mix-blend-mode: difference, dauerhaft (auch im Hover-State, s.u.):
   |255 - Hintergrund| ergibt bei weißer Textfarbe eine exakte Invertierung
   des jeweils darunterliegenden Hintergrunds (Panel-Video/-GIF oder die
   schwarze Fallback-Fläche) – die Schrift bleibt dadurch unabhängig vom
   Bewegtbild dahinter lesbar, auf Wunsch auch beim Hovern. Bewusste
   Abweichung von der components.css-Vorgabe ".color-swap OHNE
   mix-blend-mode" (dortiger Kommentar: ein Blend-Mode ≠ normal kann in
   Firefox das backdrop-filter/blur der Glas-Buttons sitewide brechen) –
   hier in Kauf genommen, da der Invert-Effekt explizit gewünscht ist. */
.site-header__logo-text {
  color: var(--color-white);
  mix-blend-mode: difference;
}

/* Touch-Geräte (Attribut s. html-start.php): identisch zu Desktop – ein Tap
   macht :hover klebend, difference bleibt dauerhaft aktiv, das Logo sieht in
   Ruhe- und Tap-Zustand identisch aus. Explizite Regel bleibt bestehen, um
   iOS Safari keinen Blend-Wechsel im Moment der beginnenden
   Link-Navigation auszulösen (baut sonst den Compositing-Baum der Pille
   (isolation:isolate + overflow:hidden + border-radius) neu auf und malt
   die Schrift dabei sichtbar nach unten rechts versetzt, zusammen mit den
   ebenfalls tap-getriggerten Scramble-/Color-Swap-Animationen, die nav.js
   auf Touch deshalb gar nicht erst bindet). */
html[data-device="touch"] .site-header__logo:hover .site-header__logo-text {
  mix-blend-mode: difference;
}

.site-header__nav {
  justify-self: center;
}

.site-header__nav-list {
  display: flex;
  /* 50% kleiner als ursprünglich (* 2 → * 1, s. auch --space-2xs). */
  gap: var(--space-2xs);
  flex-wrap: wrap;
  justify-content: center;
}

.site-header__nav-list .btn {
  text-transform: uppercase;
  font-size: var(--fs-nav-btn);
  /* Eigener Wert als das generische .btn-Padding (1.2em) – gilt nur für
     die Nav-Items (class="btn", s. site-header.php);
     .site-header__menu-toggle bleibt bewusst beim generischen
     Inline-Padding. Bewusst kein min-width (s. Git-Historie): mit einem
     min-width, der die Content+Padding-Breite überschreitet, würde jede
     Änderung an padding-inline unsichtbar bleiben, weil dann allein
     min-width die Box-Breite bestimmt. Buttons sind daher
     content-getrieben unterschiedlich breit. */
  padding-inline: 1rem;
  /* Asymmetrisch statt "padding-block: var(--nav-btn-padding-block)" — s.
     --font-pixel-grotesk-nudge (tokens.css). Summe bleibt 2 ×
     --nav-btn-padding-block, --nav-btn-height (tokens.css) bleibt dadurch
     weiterhin exakt. */
  padding-block: calc(var(--nav-btn-padding-block) - var(--font-pixel-grotesk-nudge)) calc(var(--nav-btn-padding-block) + var(--font-pixel-grotesk-nudge));
  text-align: center;
  /* Macht die Button-Höhe exakt berechenbar für --nav-btn-height (s.
     tokens.css), statt von font-metrikabhängigem line-height:normal
     abzuhängen. */
  line-height: 1;
}

/* Glas-Optik (Transparenz + Schatten) für ALLE Header-Buttons (Nav-Items
   UND Menu-Toggle, teilen sich .btn) statt der vollflächigen Button-Farbe
   aus components.css – Referenz: nav-bar__container auf itsnicethat.com.
   Scoped auf .site-header .btn statt global, damit Buttons außerhalb des
   Headers (Tags, Slider-Pfeile, CTA …) unangetastet bleiben. */
.site-header .btn {
  background: color-mix(in srgb, var(--color-nav-button-background) var(--nav-btn-bg-opacity), transparent);
  -webkit-backdrop-filter: blur(var(--nav-btn-blur)) saturate(var(--nav-btn-saturate));
  backdrop-filter: blur(var(--nav-btn-blur)) saturate(var(--nav-btn-saturate));
  box-shadow: var(--nav-btn-shadow);
}

/* Radius-System der Nav-Buttons: bewusst UMGEKEHRT zum generischen .btn
   (components.css: sm in Ruhe, md bei Hover). Prinzip bleibt gleich –
   "Hover zeigt eine Vorschau auf den jeweils ANDEREN Zustand" – nur die
   Werte sind vertauscht:
     • Aktiver Button:   Ruhe = --radius-sm, Hover = --radius-md
     • Inaktive Buttons: Ruhe = --radius-md, Hover = --radius-sm
   D.h. ein inaktiver Button zeigt beim Hovern eine Vorschau auf den
   "aktiv"-Radius (sm); der aktive Button zeigt beim Hovern (oder sobald
   IRGENDEIN anderer Button gehovert wird) eine Vorschau auf den
   "inaktiv"-Radius (md) – exakt gespiegelte Logik zu vorher, nur mit
   vertauschten Zielwerten.
   Scoped auf .site-header .btn (nicht nur .site-header__nav-list): der
   Menu-Toggle-Button (Mobile-Burger) ist ein Geschwister-Element der
   Nav-List (s. site-header.php), kein aria-current, zählt also ebenfalls
   als "inaktiver" Button und soll denselben großen Ruhe-Radius tragen. */
.site-header .btn:not([aria-current="page"]) {
  border-radius: var(--radius-md);
}

/* Höhere Spezifität als das generische .btn:hover (components.css,
   sonst würde dort md gewinnen) nötig, damit der Hover hier wirklich
   auf sm zurückschrumpft statt (wirkungslos) bei md zu bleiben. */
.site-header .btn:not([aria-current="page"]):hover {
  border-radius: var(--radius-sm);
}

/* Schwarze Variante (aktiver Nav-Punkt): bewusst OHNE Transparenz, wie
   gefordert – eigener Panel-Farbwert (--color-nav-button-active-background,
   s. tokens.css/html-start.php – standardmäßig identisch zu Font Dark),
   kein Blur (unnötig unter deckendem Hintergrund). Schatten bleibt für
   denselben "Lift" wie die transparenten Buttons. Radius s. Kommentar
   oben: der aktive Button trägt in Ruhe bewusst den KLEINEN Radius. */
.site-header__nav-list .btn[aria-current="page"] {
  background: var(--color-nav-button-active-background);
  color: var(--color-font-bright);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border-radius: var(--radius-sm);
}

/* Beim Hovern des aktiven Buttons läuft die Abrundung zum "inaktiv"-Radius
   (--radius-md) – Vorschau auf den Ruhezustand der anderen Buttons (s.
   Kommentar oben). Höhere Spezifität als .btn:hover nötig, damit dieser
   Wert dort gewinnt.
   :not(.is-hover-locked): wurde der Button per Klick aktiv, während die
   Maus schon draufliegt (s. nav.js), soll der Radius NICHT sofort auf
   --radius-md zurückspringen — er bleibt bis zum echten mouseleave beim
   abgerundeten --radius-sm. */
.site-header__nav-list .btn[aria-current="page"]:hover:not(.is-hover-locked) {
  border-radius: var(--radius-md);
}

/* Hovert man IRGENDEINEN anderen Button in der Liste, fällt der aktive
   Button (Hintergrund/Farbe bleiben wie gewohnt) ebenfalls auf den
   "inaktiv"-Radius --radius-md zurück – er wirkt für die Dauer des
   fremden Hovers "deaktiviert". :has() mit :not(:hover) auf dem aktiven
   Button selbst nötig, damit der eigene Hover-Fall oben (Zurückspringen
   bei is-hover-locked) unberührt bleibt. */
.site-header__nav-list:has(.btn:hover:not([aria-current="page"]))
  .btn[aria-current="page"]:not(:hover) {
  border-radius: var(--radius-md);
}

.site-header__menu-toggle {
  display: none;
}

/* Elevator-Zeile + Connect/Mail (s. site-header.php, gleiche Klassen wie
   .hero__elevator/.hero__connect im Hero) tauchen nur im geöffneten
   Mobile-Overlay auf – auf dem Desktop bliebe der horizontale
   .site-header__nav-list sonst durch diesen zusätzlichen Listeneintrag
   verstopft. */
.site-header__nav-extra {
  display: none;
}

.site-header__lang {
  justify-self: end;
  font-family: var(--font-pixel-serif);
}

/* Sprach-Toggle: zeigt nur die aktive Sprache (2 Zeichen), gleiche
   Schrift/Größe wie die Nav-Buttons (--fs-nav-btn). Kein eigener
   Button-Hintergrund – ein Klick navigiert direkt zur jeweils anderen
   Sprach-URL (s. site-header.php). */
.lang-toggle {
  position: relative;
  display: inline-flex;
  font-family: var(--font-pixel-grotesk);
  font-size: var(--fs-nav-btn);
  line-height: 1;
  text-transform: uppercase;
  color: var(--color-font-dark);
}

/* Rahmen beim Hovern: kein eigener Hintergrund wie bei .btn (s.o.), nur
   ein Rand in der Font-Farbe – als ::before statt echtem border/padding,
   damit die Buchstaben nicht springen. Vertikaler Inset = negatives
   padding-block der Nav-Buttons (1.2em, s. .site-header__nav-list .btn),
   damit der Rahmen exakt --nav-btn-height hoch wird. Radius bleibt
   statisch (kein Übergang wie bei .btn:hover) – hier bewusst nicht
   animiert. */
/* .lang-toggle::before {
  content: "";
  position: absolute;
  inset: -1.2em -1.2em;
  border: 1px solid white;
  border-radius: var(--radius-sm);
  opacity: 0;
  transition: opacity var(--duration-fast) var(--ease-standard);
  pointer-events: none;
}

.lang-toggle:hover::before {
  opacity: 0.5;
} */

/* Die generische .btn:hover-Anhebung (s. components.css) wird für alle
   Header-Buttons deaktiviert (Logo/Nav/Menu-Toggle/Sprache bleiben beim
   Hovern auf Position). */
.site-header .btn:hover {
  transform: none;
}

/* bp: --bp-tablet (1024px) */
@media (max-width: 1024px) {
  .site-header {
    grid-template-columns: auto 1fr auto;
    padding-block: var(--space-sm);
  }

  .site-header__nav {
    justify-self: end;
    /* Vertauscht mit .site-header__lang (order: 2 dort, s. unten): Menu-
       Button steht mobil jetzt rechts außen, Sprache davor. */
    order: 3;
  }

  /* Auf sehr schmalen Screens verschwindet die Nav-Liste hinter dem
     Burger-Button und öffnet sich als zentriertes Vollbild-Overlay
     (Toggle-Logik in nav.js). Die Buttons behalten dabei ihre normale
     Form (radius/Farbe aus .btn), werden aber größer skaliert.
     Hintergrund/Farbe bewusst NICHT das generische Glas von .site-header
     .btn (components.css), sondern dasselbe deckende Schwarz wie der
     aktive Nav-Button (s. .site-header__nav-list .btn[aria-current="page"]
     oben) – in BEIDEN Zuständen ("Menu" geschlossen UND "Close" offen,
     s. .is-open weiter unten, das dafür keine eigene Farbe mehr braucht). */
  .site-header__menu-toggle {
    display: inline-flex;
    text-transform: uppercase;
    /* Gleiche font-size/Padding-Block/line-height wie
       .site-header__nav-list .btn, damit dieser Button ebenfalls exakt
       --nav-btn-height hoch ist (s. --nav-btn-padding-block oben /
       tokens.css). */
    font-size: var(--fs-nav-btn);
    /* Asymmetrisch statt "padding-block: var(--nav-btn-padding-block)" —
       s. --font-pixel-grotesk-nudge (tokens.css), gleicher Grund wie bei
       .site-header__nav-list .btn oben. */
    padding-block: calc(var(--nav-btn-padding-block) - var(--font-pixel-grotesk-nudge)) calc(var(--nav-btn-padding-block) + var(--font-pixel-grotesk-nudge));
    line-height: 1;
  }

  /* Spezifität wie .site-header .btn (components.css-Glas, oben) nötig –
     eine einzelne .site-header__menu-toggle-Klasse (Spezifität 0,1,0)
     verliert sonst gegen dessen zwei Klassen (0,2,0) und der Hintergrund
     bliebe trotz dieser Regel beim hellen Glas (nur "color" gewann vorher
     zufällig, weil dort dieselbe Spezifität galt und diese Regel später im
     Stylesheet steht). */
  .site-header .site-header__menu-toggle {
    background: var(--color-nav-button-active-background);
    color: var(--color-font-bright);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }

  /* Immer im Layout (fixed, volle Viewport-Fläche) statt display:none —
     nötig, damit clip-path unten überhaupt animieren kann (ein Wechsel von
     display:none auf flex lässt sich nicht übergangsweise animieren).
     visibility:hidden übernimmt stattdessen das Ausblenden im Ruhezustand:
     nimmt die Menüpunkte aus der Tab-Reihenfolge/Klick-Fläche, exakt wie
     vorher display:none, blendet dabei aber (anders als opacity) nicht die
     clip-path-Transition selbst aus. */
  .site-header__nav-list {
    display: flex;
    position: fixed;
    inset: 0;
    /* padding-block statt nur padding-top: symmetrisches Polster oben UND
       unten hält den Mittelpunkt der Buttons exakt bei 50% Viewport-Höhe
       (justify-content: center zentriert sonst nur innerhalb der Content-
       Box, die durch einseitiges padding-top nach unten verschoben wäre). */
    padding-block: var(--header-height);
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-md);
    background: var(--color-background);
    z-index: 90;
    visibility: hidden;
    /* Kreisförmiger Reveal: Ursprung sitzt exakt auf dem Burger-Button
       (--menu-reveal-x/-y, per JS synchronisiert, s. nav.js
       updateMenuRevealOrigin) — Fallback 100% 0% (obere rechte Ecke) greift
       nur, falls JS noch nicht gelaufen ist. 150vmax statt eines
       Prozentwerts, damit der Kreis auch bei sehr breiten/hohen
       Seitenverhältnissen garantiert bis in die gegenüberliegende (untere
       linke) Ecke reicht, unabhängig von der %-Bezugsformel von clip-path
       circle(). */
    -webkit-clip-path: circle(0px at var(--menu-reveal-x, 100%) var(--menu-reveal-y, 0%));
    clip-path: circle(0px at var(--menu-reveal-x, 100%) var(--menu-reveal-y, 0%));
    transition: clip-path var(--duration-slow) var(--ease-standard),
      visibility 0s linear var(--duration-slow);
  }

  /* Overlay-Hintergrund reicht bis zum oberen Viewport-Rand (inset: 0),
     Logo/Toggle/Lang bleiben dank höherem z-index sichtbar darüber; die
     Menüpunkte selbst rutschen per padding-block unter die Header-Zeile. */
  .site-header__nav.is-open .site-header__nav-list {
    visibility: visible;
    -webkit-clip-path: circle(150vmax at var(--menu-reveal-x, 100%) var(--menu-reveal-y, 0%));
    clip-path: circle(150vmax at var(--menu-reveal-x, 100%) var(--menu-reveal-y, 0%));
    transition: clip-path var(--duration-slow) var(--ease-standard);
  }

  .site-header__logo,
  .site-header__menu-toggle,
  .site-header__lang {
    position: relative;
    z-index: 91;
  }

  /* Gleicher Auf-und-Einblend-Effekt wie .fade-in-up (animations.css:
     opacity 0→1 + translateY(2rem)→0), hier aber nicht scroll-getriggert
     (reveal.js/IntersectionObserver würde bei diesem fixed inset:0-Overlay
     sofort beim Laden auslösen, lange bevor das Menü je geöffnet wird),
     sondern direkt an .is-open gekoppelt – läuft also bei jedem
     Öffnen/Schließen erneut. transform statt der generischen .btn-Hover-
     Anhebung: Header-Buttons deaktivieren .btn:hover-transform ohnehin
     komplett (s. .site-header .btn:hover oben), der Wert steht also hier
     exklusiv für den Reveal zur Verfügung.
     Gilt einzeln für .btn UND (gescoped auf den Nav-Overlay-Kontext, s.
     Selektor-Präfix .site-header__nav-list) .hero__elevator/.hero__connect
     statt gebündelt über den umschließenden .site-header__nav-extra-Wrapper
     — der bleibt reiner Positionierungsrahmen (s.u.) ohne eigene
     Opacity/Transform, sonst würden Elevator+Connect optisch als EIN Block
     gemeinsam ein-/ausblenden statt jeweils für sich.
     .btn bekommt border-radius separat in der eigenen transition-Zeile
     wieder mit (identisch zu components.css .btn) – die "transition"-
     Shorthand hier ERSETZT sonst komplett die dort geerbte Deklaration
     (inkl. border-radius-Übergang für den Hover-Rundungswechsel, s.
     .site-header .btn:not([aria-current="page"]):hover oben) statt sie nur
     um opacity/transform zu ergänzen – ohne diese Zeile bliebe der
     Radius-Hover im Menü-Overlay dadurch ungewollt sprunghaft. */
  .site-header__nav-list .btn {
    /* Unconditional statt nur unter .is-open (s. Git-Historie) — dieselbe
       Logik wie bei .site-header__nav-extra oben: die Überschrift-Größe/
       -Breite des Overlay-Buttons darf sich beim Schließen nicht
       schlagartig auf die kompakte Desktop-Listen-Form (font-size:
       --fs-nav-btn, padding-inline: 1rem, s. .site-header__nav-list .btn
       weiter oben) zurücksetzen, während der Button dank permanentem
       Rendering (kein display:none mehr) noch sichtbar ausblendet — genau
       das passierte, solange dieser Block nur unter
       .site-header__nav.is-open galt. */
    font-size: 1.15rem;
    min-width: 14em;
    /* 80% weniger seitliches Padding als das generische .btn-Padding
       (1.2em, components.css) – nur im Overlay, da min-width hier ohnehin
       für die Breite sorgt. */
    padding-inline: 0.24em;
    opacity: 0;
    transform: translateY(2rem);
    transition: opacity var(--duration-base) var(--ease-standard),
      transform var(--duration-base) var(--ease-standard),
      border-radius calc(var(--duration-fast) * 4) var(--ease-standard);
  }

  .site-header__nav-list .hero__elevator,
  .site-header__nav-list .hero__connect {
    opacity: 0;
    /* Negatives translateY statt wie bei .btn positiv (s.o.) — Elevator/
       Connect sollen von OBEN nach unten ins Bild rutschen, entgegengesetzt
       zur Aufwärtsbewegung der Buttons. */
    transform: translateY(-2rem);
    transition: opacity var(--duration-base) var(--ease-standard),
      transform var(--duration-base) var(--ease-standard);
  }

  /* Gestaffelter Versatz statt einer einzigen gemeinsamen Verzögerung:
     Work/Service/Contact bekommen --stagger-index 0..2 (Reihenfolge = von
     oben nach unten in der zentrierten Liste) und blenden dadurch sichtbar
     NACHEINANDER ein statt alle gleichzeitig. Elevator/Connect (unten
     links/rechts, s. hero.css) bekommen bewusst denselben Index wie der
     ERSTE Button (0) — sie sollen zeitgleich mit diesem erscheinen, nicht
     als eigene Stufe vor oder nach der Kaskade. --menu-reveal-delay startet die
     Kaskade bewusst nah am Beginn des kreisförmigen Hintergrund-Reveals
     (s. .site-header__nav-list oben), statt (wie ursprünglich) auf dessen
     Fast-Fertig-Zustand zu warten — 250ms statt der früheren 500ms lässt
     dem Kreis nur einen winzigen Vorsprung, bevor der erste Button (und
     damit die ganze Staffelung) lostritt. --stagger-step addiert pro Index
     einen leichten weiteren Versatz obendrauf. */
  .site-header__nav-list {
    --menu-reveal-delay: 250ms;
    --stagger-step: 90ms;
  }

  .site-header__nav-list > li:nth-child(1) .btn {
    --stagger-index: 0;
  }
  .site-header__nav-list > li:nth-child(2) .btn {
    --stagger-index: 1;
  }
  .site-header__nav-list > li:nth-child(3) .btn {
    --stagger-index: 2;
  }
  .site-header__nav-list .hero__elevator,
  .site-header__nav-list .hero__connect {
    --stagger-index: 0;
  }

  /* transition-delay hat hier 3 Werte (statt nur einem), weil .btn OBEN 3
     Properties transitioniert (opacity, transform, border-radius) — ein
     einzelner Delay-Wert würde per Spec auf ALLE 3 wiederholt (kürzere
     transition-delay-Liste zykelt auf die Länge von transition-property),
     der Hover-Rundungswechsel (border-radius) hätte dadurch denselben
     ~500-860ms-Stagger-Delay wie der Reveal geerbt und beim Hovern
     spürbar verzögert reagiert. border-radius bleibt daher explizit bei
     0s, nur opacity/transform bekommen den gestaffelten Delay. */
  .site-header__nav.is-open .site-header__nav-list .btn {
    opacity: 1;
    transform: translateY(0);
    transition-delay: calc(var(--menu-reveal-delay) + (var(--stagger-index, 0) * var(--stagger-step))),
      calc(var(--menu-reveal-delay) + (var(--stagger-index, 0) * var(--stagger-step))),
      0s;
  }

  .site-header__nav.is-open .site-header__nav-list .hero__elevator,
  .site-header__nav.is-open .site-header__nav-list .hero__connect {
    opacity: 1;
    transform: translateY(0);
    transition-delay: calc(var(--menu-reveal-delay) + (var(--stagger-index, 0) * var(--stagger-step)));
  }

  /* Reiner Positionierungsrahmen, KEIN eigener Flex-Flow: .hero__elevator/
     .hero__connect (hero.css) bleiben unverändert position:absolute mit
     left/right: var(--container-padding) + bottom: calc(...) – exakt wie
     im Hero unten links/rechts. position:absolute + inset:0 hier macht
     diesen <li> selbst zum containing block dafür (deckt denselben
     Viewport-Bereich ab wie das .is-open-Overlay, s.o.) UND nimmt ihn aus
     dem Flex-Gap der Nav-Liste heraus, statt als leeres 0-Höhe-Flex-Item
     eine Extra-Lücke zu erzeugen.
     pointer-events: none nötig, weil dieser <li> als positioniertes
     Element sonst (trotz unsichtbarem Hintergrund) über den gesamten
     inset:0-Bereich VOR den eigentlichen Menüpunkten läge und deren Klicks
     abfängt – erst auf den beiden Tag-Boxen selbst (s.u.) wieder auf
     "auto" zurückgesetzt, wo tatsächlich klickbare Links liegen.
     display:block unconditionally (statt nur unter .is-open, s. Git-
     Historie) – überschreibt hier das globale display:none (s.o., Zeile
     ~296) durchgehend innerhalb dieses Breakpoints: Sichtbarkeit im
     Ruhezustand übernimmt stattdessen visibility:hidden, geerbt von
     .site-header__nav-list (s.o.). Ein Element, das erst im selben Moment
     wie sein Opacity-Zielwert von display:none auf block wechselt, hat
     keinen vorherigen gerenderten Frame, von dem aus die Opacity-
     Transition starten könnte – der Fade von .hero__elevator/.hero__connect
     (s.o.) sprang dadurch OHNE display:block hier sofort auf 1, statt
     einzublenden. */
  .site-header__nav-list .site-header__nav-extra {
    display: block;
    position: absolute;
    inset: 0;
    pointer-events: none;
  }

  .site-header__nav-extra .hero__elevator,
  .site-header__nav-extra .hero__connect {
    pointer-events: auto;
  }

  .site-header__lang {
    /* Vertauscht mit .site-header__nav (order: 3 dort, s. oben): Sprache
       steht mobil jetzt vor dem Menu-Button statt danach. */
    order: 2;
  }
}

/* Verhindert Hintergrund-Scroll, solange das Mobile-Overlay offen ist
   (Klasse wird von nav.js auf <body> gesetzt/entfernt). */
body.site-header-nav-open {
  overflow: hidden;
}

/* Barrierefreiheit: Bewegungsreduktion respektieren (gleiches Prinzip wie
   .fade-in-up, s. animations.css) — Kreis-Reveal und verzögertes
   Button-Einblenden entfallen, das Overlay springt stattdessen direkt in
   den jeweiligen End-Zustand. */
@media (max-width: 1024px) and (prefers-reduced-motion: reduce) {
  .site-header__nav-list,
  .site-header__nav-list .btn,
  .site-header__nav-list .hero__elevator,
  .site-header__nav-list .hero__connect {
    transition: none;
  }
}
