/* ==========================================================================
   components.css — DA-Studio-Media-Service
   Benannte Komponenten (Kap. 6). Registriert im Muster-Register der CLAUDE.md.
   Nur Tokens aus tokens.css; keine Roh-Farbwerte, keine arbitrary values.
   ========================================================================== */

/* --------------------------------------------------------------------------
   skip-link — Sprungmarke zum Inhalt (Kap. 9)
   -------------------------------------------------------------------------- */

.skip-link {
  position: absolute;
  left: var(--space-gutter);
  top: var(--space-inline);
  z-index: var(--z-skip);
  transform: translateY(-200%);
  display: inline-flex;
  align-items: center;
  padding: var(--space-tight) var(--space-comp);
  border-radius: var(--radius-pill);
  background: var(--color-action);
  color: var(--color-action-text);
  font-size: var(--text-sm);
  font-weight: var(--weight-extrabold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  text-decoration: none;
  transition: transform 0.2s ease;
}

.skip-link:focus-visible {
  transform: translateY(0);
  color: var(--color-action-text);
}

/* --------------------------------------------------------------------------
   site-header — fixer Kopfbereich, in jede Seite byte-identisch dupliziert
   -------------------------------------------------------------------------- */

.site-header {
  position: fixed;
  inset-inline: 0;
  top: 0;
  z-index: var(--z-nav-panel);
  /* Ohne JS traegt der Kopf die vollstaendige verschachtelte Liste (Kap. 8) */
  max-height: 100vh;
  overflow-y: auto;
  background: var(--color-overlay-strong);
  border-bottom: var(--border-hair) solid var(--color-line);
}

.js .site-header {
  overflow-y: visible;
}

@supports (backdrop-filter: blur(1px)) {
  .site-header {
    background: var(--color-overlay);
    backdrop-filter: blur(var(--s-3)) saturate(150%);
  }
}

.site-header__inner {
  display: flex;
  align-items: center;
  gap: var(--space-comp);
  min-height: var(--header-h);
  padding-inline: var(--space-gutter);
  padding-block: var(--space-inline);
}

.site-header__logo {
  flex: none;
  display: flex;
  align-items: center;
  text-decoration: none;
}

.site-header__logo img {
  display: block;
  width: auto;
  height: var(--s-8);
}

@media (min-width: 1025px) {
  .site-header__logo img {
    height: var(--s-10);
  }
}

/* --------------------------------------------------------------------------
   btn — Schaltflaechen-CTA: Unterscheidung ueber Form/Flaeche, kein Underline
   -------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-inline);
  min-height: var(--s-11);
  padding: var(--space-tight) var(--space-group);
  border: var(--border-hair) solid transparent;
  border-radius: var(--radius-pill);
  font-size: var(--text-base);
  font-weight: var(--weight-extrabold);
  letter-spacing: var(--tracking-normal);
  text-decoration: none;
  text-align: center;
  cursor: pointer;
  transition: background 0.3s ease, color 0.3s ease, border-color 0.3s ease,
    transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);
}

.btn:hover,
.btn:focus-visible {
  transform: translateY(calc(var(--s-1) * -0.5));
}

.btn--primary {
  background: var(--color-action);
  color: var(--color-action-text);
}

.btn--primary:hover,
.btn--primary:focus-visible {
  background: var(--color-action-hover);
  color: var(--color-action-text);
}

.btn--ghost {
  border-color: var(--color-line-strong);
  background: transparent;
  color: var(--color-text);
  font-weight: var(--weight-bold);
}

.btn--ghost:hover,
.btn--ghost:focus-visible {
  border-color: var(--color-text);
  background: var(--color-control-bg-faint);
  color: var(--color-text);
}

/* --------------------------------------------------------------------------
   eyebrow — gesperrte Kapitel-/Bereichszeile ueber einer Ueberschrift
   -------------------------------------------------------------------------- */

.eyebrow {
  margin: 0 0 var(--space-block);
  font-size: var(--text-sm);
  font-weight: var(--weight-extrabold);
  letter-spacing: var(--tracking-widest);
  line-height: var(--leading-normal);
  text-transform: uppercase;
  color: var(--color-eyebrow);
}

.script-line {
  margin: 0;
  font-family: var(--font-script);
  font-size: var(--text-display);
  line-height: var(--leading-tight);
  color: var(--color-script);
}

/* --------------------------------------------------------------------------
   hero — Auftakt mit vollflaechigem Bild und Verlaufs-Abdeckung
   -------------------------------------------------------------------------- */

.hero {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-group);
  min-height: 100vh;
  min-height: 100svh;
  padding: calc(var(--header-h) + var(--space-group)) var(--space-gutter)
    var(--space-group);
  overflow: hidden;
  background: var(--color-surface);
  text-align: center;
}

.hero__media {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.hero__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 46%;
}

.hero__scrim {
  position: absolute;
  inset: 0;
  background: radial-gradient(
    120% 96% at 50% 40%,
    var(--color-overlay-soft) 0%,
    var(--color-overlay) 62%,
    var(--color-overlay-strong) 100%
  );
}

.hero__scrim--top {
  position: absolute;
  inset: 0 0 auto;
  height: 34%;
  background: linear-gradient(180deg, var(--color-overlay), var(--color-overlay-none));
}

.hero__body {
  position: relative;
  flex: none;
  max-width: var(--container-wide);
}

.hero__title {
  margin: 0;
  font-size: var(--text-display-lg);
  font-weight: var(--weight-black);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display);
  text-transform: uppercase;
}

.hero__title-sub {
  display: block;
  margin-top: var(--space-inline);
  font-size: var(--text-2xl);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-tight);
  text-transform: none;
  color: var(--color-brand);
}

.hero__lead {
  margin: var(--space-group) auto 0;
  max-width: var(--measure-narrow);
  font-size: var(--text-md);
  line-height: var(--leading-normal);
  color: var(--color-text-muted);
}

.hero__hint {
  position: relative;
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-inline);
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.hero__hint-line {
  display: block;
  width: var(--border-hair);
  height: var(--s-8);
  background: linear-gradient(180deg, var(--color-text-muted), var(--color-overlay-none));
}

@media (prefers-reduced-motion: no-preference) {
  .hero__hint-line {
    animation: hero-hint 2s ease infinite;
  }
}

@keyframes hero-hint {
  0%, 100% { transform: translateY(0); opacity: 0.5; }
  50% { transform: translateY(var(--s-2)); opacity: 1; }
}

/* --------------------------------------------------------------------------
   statement — grosse Haltungsaussage auf heller Flaeche
   -------------------------------------------------------------------------- */

.statement {
  padding: var(--space-section) var(--space-gutter);
  background: var(--color-surface);
  color: var(--color-text);
}

.statement__claim {
  margin: 0;
  font-size: var(--text-display);
  font-weight: var(--weight-extrabold);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  text-wrap: pretty;
  hyphens: auto;
  overflow-wrap: break-word;
}

.statement__aside {
  margin-top: var(--space-group);
  max-width: var(--measure-narrow);
}

@media (min-width: 1025px) {
  .statement__aside {
    margin-left: auto;
  }
}

/* --------------------------------------------------------------------------
   aperture — Signatur-Section: Kreis-Aufblende beim Scrollen
   Ohne JS bzw. bei reduzierter Bewegung: normale Section, Bild voll sichtbar.
   -------------------------------------------------------------------------- */

.aperture {
  position: relative;
  background: var(--color-surface);
}

.aperture__stage {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 80vh;
  padding: var(--space-section) var(--space-gutter);
  overflow: hidden;
}

.aperture__media {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.aperture__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.aperture__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    0deg,
    var(--color-overlay) 2%,
    var(--color-overlay-faint) 55%,
    var(--color-overlay-soft) 100%
  );
}

.aperture__body {
  position: relative;
  max-width: var(--container);
  text-align: center;
}

.aperture__title {
  margin: 0;
  font-size: var(--text-display);
  font-weight: var(--weight-black);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display);
  text-wrap: balance;
}

/* Angepinnte Inszenierung nur, wenn der Viewport sie ohne Beschnitt traegt */
@media (min-height: 600px) {
  .js-motion .aperture {
    height: 240vh;
  }

  .js-motion .aperture__stage {
    position: sticky;
    top: var(--v2-mat-gap);
    height: calc(100vh - var(--v2-panel-inset));
    height: calc(100svh - var(--v2-panel-inset));
    min-height: 0;
  }

  .js-motion .aperture__media {
    clip-path: circle(calc(17% + 88% * var(--aperture-e, 1)) at 50% 50%);
  }

  /* Die Buehne ist das Panel (siehe unten) und laesst deshalb auch angepinnt
     den Passepartout-Abstand oben und unten frei. */
  .js-motion[data-layout="v02"] .aperture__stage {
    top: var(--v2-mat-gap);
    height: calc(100vh - 2 * var(--v2-mat-gap));
    height: calc(100svh - 2 * var(--v2-mat-gap));
  }

  /* „Version 02": Das Bild faehrt von 1.34 auf 0.86 zurueck — am Ende steht es
     kleiner im Kasten und die Grundflaeche bildet den Rahmen.

     Der Zoom steht als **eine** Rolle am Kasten, weil zwei Elemente ihn brauchen:
     das `picture` und der Verlauf. Zwei getrennte Rechnungen wuerden beim naechsten
     Edit auseinanderlaufen. */
  .js-motion[data-layout="v02"] .aperture__media {
    --ap-zoom: calc(1.34 - var(--aperture-p, 0) * 0.3 * var(--v2-fx));
  }

  .js-motion[data-layout="v02"] .aperture__media picture {
    transform: scale(var(--ap-zoom));
    will-change: transform;
  }

  /* Der Verlauf gehoert auf das Bild, nicht auf den Kasten: er faehrt deshalb mit
     derselben Kurve zurueck und uebernimmt die Rundung. Ohne das bleibt er in
     Buehnengroesse stehen und deckt am Ende den Rahmen ab, den das geschrumpfte
     Bild freigibt — auf `/hochzeiten` und `/unternehmen/leistungen` ist die
     Grundflaeche durchsichtig, dort lag er also als Schleier auf dem
     Passepartout. Mobil fiel es zuerst auf, weil die Buehne dort schmaler ist und
     der Rahmen entsprechend breiter wirkt. */
  .js-motion[data-layout="v02"] .aperture__scrim {
    transform: scale(var(--ap-zoom));
    border-radius: var(--v2-panel-radius);
  }

  /* Die Textsaeule steigt 6vh, waechst um 6 % und blendet ab 62 % aus.

     `will-change` nennt **nur** `transform`, nicht `opacity` — und das ist keine
     Nachlaessigkeit: `will-change: opacity` macht das Element zum Backdrop-Root,
     und dann verschwimmt der `backdrop-filter` der Tag-Pillen darin ins Leere
     (Kap. 7). Mit `transform` allein bleibt der Root aus, solange die Deckkraft
     genau 1 ist — also die ersten 62 % der Buehne, in denen die Pillen sichtbar
     sind. Danach faellt `--aperture-copy-p` unter 1, der Root entsteht und die
     Unschaerfe entfaellt; zu diesem Zeitpunkt blendet die Saeule ohnehin aus. */
  .js-motion[data-layout="v02"] .aperture__body {
    transform: translateY(calc(-1 * var(--aperture-p, 0) * 6vh * var(--v2-fx)))
      scale(calc(1 + var(--aperture-p, 0) * 0.06 * var(--v2-fx)));
    opacity: var(--aperture-copy-p, 1);
    will-change: transform;
  }

  /* Sobald etwas in der Textsaeule den Fokus hat, steht sie voll da - dieselbe
     Regel und derselbe Grund wie beim Vorhang weiter unten (Kundenwunsch
     03.08.2026). Sie ist noetig, seit hier ein Knopf steht: Die Saeule blendet
     ab 62 % der Buehne aus, ein Element mit `opacity: 0` bleibt aber
     fokussierbar. Beim Tabben scrollt der Browser die **Layout**-Position des
     Knopfes ins Bild, und die aendert an der Scrollkurve nichts - ohne diese
     Regel waere der Knopf fokussiert und unsichtbar.

     `transform: none` bringt die Saeule auf ihre Layout-Position, `opacity: 1`
     macht sie lesbar. Ohne Fokus gilt wieder die Scrollkurve. */
  .js-motion[data-layout="v02"] .aperture:has(:focus-visible) .aperture__body {
    opacity: 1;
    transform: none;
  }

}

/* --------------------------------------------------------------------------
   tag-list — Aufzaehlung als Pillen (Leistungsspektrum)
   -------------------------------------------------------------------------- */

.tag-list {
  list-style: none;
  margin: var(--space-block) auto 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-inline);
  max-width: var(--measure);
}

/* Die Pillen stehen ueber einem Bild und tragen deshalb ihre eigene dunkle
   Flaeche (Kundenwunsch 03.08.2026). Sie ist themenunabhaengig: derselbe
   Abschnitt ist auf `/unternehmen` dunkel und auf `/unternehmen/leistungen` hell
   gethemt — ohne festen Wert waeren die Pillen dort hell auf hell. */
.tag {
  display: inline-flex;
  align-items: center;
  padding: var(--space-inline) var(--space-comp);
  border: var(--border-hair) solid var(--color-line-strong);
  border-radius: var(--radius-pill);
  background: var(--color-tag-bg);
  color: var(--color-tag-text);
  font-size: var(--text-base);
  font-weight: var(--weight-bold);
}

/* --------------------------------------------------------------------------
   duo — zwei Wege, ein Bild je Weg (Mikrointeraktion, reines CSS)
   -------------------------------------------------------------------------- */

.duo {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-block);
  min-height: 100vh;
  min-height: 100svh;
  padding: var(--space-section) var(--space-gutter);
  overflow: hidden;
  background: var(--color-surface);
  text-align: center;
}

.duo__media {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.duo__layer {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: opacity 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}

.duo__layer--left,
.duo__layer--right {
  opacity: 0;
}

.duo:has(.duo__cta--left:hover) .duo__layer--left,
.duo:has(.duo__cta--left:focus-visible) .duo__layer--left,
.duo:has(.duo__cta--right:hover) .duo__layer--right,
.duo:has(.duo__cta--right:focus-visible) .duo__layer--right {
  opacity: 1;
}

.duo__scrim {
  position: absolute;
  inset: 0;
  background: radial-gradient(
    120% 98% at 50% 42%,
    var(--color-overlay-soft) 0%,
    var(--color-overlay) 60%,
    var(--color-overlay-strong) 100%
  );
}

.duo__body {
  position: relative;
  max-width: var(--container);
}

.duo__title {
  margin: 0;
  font-size: var(--text-display-lg);
  font-weight: var(--weight-black);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display);
  text-transform: uppercase;
}

.duo__lead {
  margin: var(--space-block) auto 0;
  max-width: var(--measure-narrow);
  font-size: var(--text-md);
  line-height: var(--leading-normal);
  color: var(--color-text-muted);
}

.duo__grid {
  display: grid;
  gap: var(--space-group);
  margin: var(--space-group) auto 0;
  max-width: var(--measure-wide);
  text-align: left;
}

@media (min-width: 640px) {
  .duo__grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* Die beiden Spalten tragen unterschiedlich lange Texte; ohne das haengen die
   Knoepfe auf verschiedenen Hoehen (Kundenwunsch 03.08.2026). Jede Spalte wird
   eine Flex-Saeule, und `margin-top: auto` am Knopf schiebt ihn an den Fuss —
   damit stehen beide auf einer Linie, egal wie viele Zeilen der Text darueber
   braucht. Rasterkinder sind von Haus aus gleich hoch (`align-items: stretch`),
   die Bezugshoehe stimmt also.

   `align-items: flex-start` haelt den Knopf auf seiner Inhaltsbreite — als
   gestreckte Flex-Zeile waere die Pill sonst so breit wie die Spalte. */
.duo__grid > * {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.duo__col-title {
  margin: 0 0 var(--space-tight);
  font-size: var(--text-lg);
  font-weight: var(--weight-extrabold);
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-tight);
}

.duo__col-text {
  margin: 0 0 var(--space-comp);
  color: var(--color-text-muted);
  line-height: var(--leading-normal);
}

.duo__cta {
  /* Schiebt den Knopf an den Fuss der Spalte — siehe `.duo__grid > *`. Der
     untere Rand von `.duo__col-text` bleibt als Mindestabstand wirksam. */
  margin-top: auto;
  font-size: var(--text-sm);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}

.duo__arrow {
  transition: transform 0.4s cubic-bezier(0.2, 0.8, 0.2, 1);
}

.duo__cta:hover .duo__arrow,
.duo__cta:focus-visible .duo__arrow {
  transform: translateX(var(--s-2));
}

/* --------------------------------------------------------------------------
   steps — Ablauf in vier Schritten (Signatur-Section)
   Ohne JS: normale Section, alle Schritte und ein Bild sichtbar.
   -------------------------------------------------------------------------- */

.steps {
  position: relative;
  background: var(--color-surface);
}

.steps__stage {
  display: grid;
  gap: var(--space-group);
  align-items: center;
  padding: var(--space-section) var(--space-gutter);
  max-width: var(--container-wide);
  margin-inline: auto;
}

@media (min-width: 1025px) {
  .steps__stage {
    grid-template-columns: 1fr 1fr;
  }
}

.steps__list {
  list-style: none;
  margin: var(--space-block) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-block);
}

.steps__item {
  display: grid;
  grid-template-columns: var(--s-10) 1fr;
  gap: var(--space-comp);
  align-items: baseline;
  transition: opacity 0.3s ease, transform 0.3s ease;
}

.steps__num {
  font-variant-numeric: tabular-nums;
  font-size: var(--text-base);
  font-weight: var(--weight-extrabold);
  color: var(--color-brand);
}

.steps__title {
  margin: 0;
  font-size: var(--text-2xl);
  font-weight: var(--weight-black);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  hyphens: auto;
  overflow-wrap: break-word;
}

.steps__text {
  margin: var(--space-inline) 0 0;
  max-width: var(--measure-narrow);
  color: var(--color-text-muted);
  line-height: var(--leading-normal);
}

.steps__progress {
  display: flex;
  align-items: center;
  gap: var(--space-tight);
  margin-top: var(--space-block);
  max-width: var(--s-40);
}

.steps__track {
  flex: 1 1 auto;
  display: block;
  height: var(--border-thick);
  background: var(--color-line);
}

.steps__bar {
  display: block;
  height: 100%;
  width: calc(100% * var(--steps-p, 0.25));
  background: var(--color-brand);
}

.steps__count {
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wide);
  font-variant-numeric: tabular-nums;
  color: var(--color-text-muted);
}

.steps__frames {
  position: relative;
  height: 60vh;
  min-height: var(--s-40);
  overflow: hidden;
  background: var(--color-surface-alt);
}

.steps__frame {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: opacity 0.4s ease;
}

.steps__frame:nth-of-type(1) { z-index: 4; }
.steps__frame:nth-of-type(2) { z-index: 3; }
.steps__frame:nth-of-type(3) { z-index: 2; }
.steps__frame:nth-of-type(4) { z-index: 1; }

/* Der Ablauf bleibt die eine Szene, die **nicht** mobil laeuft. Gerechnet fuer
   390 x 844 px: 160 px Polsterung plus 60vh Bildstapel (506 px) lassen 178 px
   fuer Eyebrow, vier Schritte und die Fortschrittszeile — das passt nicht in
   eine Bildschirmhoehe, der Inhalt wuerde abgeschnitten. Ihn mobil zu
   inszenieren hiesse, die Buehne neu zu gestalten (kleinerer Bildstapel,
   weniger Polsterung); das ist eine Designentscheidung, keine Umschaltung. */
@media (min-width: 1025px) and (min-height: 720px) {
  .js-motion .steps {
    height: 320vh;
  }

  .js-motion .steps__stage {
    position: sticky;
    top: var(--v2-mat-gap);
    height: calc(100vh - var(--v2-panel-inset));
    height: calc(100svh - var(--v2-panel-inset));
    align-content: center;
    padding-block: var(--space-block);
  }
}

/* --------------------------------------------------------------------------
   split-cta — Abschluss-Aufforderung zwischen zwei Bildhaelften
   -------------------------------------------------------------------------- */

.split-cta {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  min-height: 100svh;
  padding: var(--space-section) var(--space-gutter);
  overflow: hidden;
  background: var(--color-surface);
  text-align: center;
}

.split-cta__stage {
  display: contents;
}

.split-cta__media {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
}

.split-cta__half {
  position: relative;
  overflow: hidden;
}

.split-cta__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.split-cta__scrim {
  position: absolute;
  inset: 0;
  background: radial-gradient(
    110% 96% at 50% 50%,
    var(--color-overlay-mid) 0%,
    var(--color-overlay) 55%,
    var(--color-overlay-strong) 100%
  );
}

.split-cta__body {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-block);
  max-width: var(--container);
}

.split-cta__title {
  margin: 0;
  font-size: var(--text-display-lg);
  font-weight: var(--weight-black);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display);
  text-wrap: balance;
}

.split-cta__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-tight);
}

/* --------------------------------------------------------------------------
   chapter-rail — Abschnittsnavigation der Langseite (nur Desktop)
   Ohne JS voll funktionsfaehig: echte Sprungmarken.
   -------------------------------------------------------------------------- */

.chapter-rail {
  display: none;
}

@media (min-width: 1025px) {
  .chapter-rail {
    position: fixed;
    left: var(--space-block);
    top: 50%;
    transform: translateY(-50%);
    z-index: var(--z-rail);
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-tight);
  }
}

.chapter-rail__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-tight);
}

.chapter-rail__dot {
  display: block;
  width: var(--s-11);
  min-height: var(--s-11);
  padding: var(--s-4) 0;
  text-decoration: none;
}

/* Der dunkle Saum liegt an **allen** Zustaenden, nicht nur am Hover: Die
   Schiene ist `fixed` und laeuft ueber helle wie dunkle Panels. Ohne ihn
   verschwindet der aktive Punkt genauso — gold-500 auf bone-050 sind
   2,26 : 1 und damit unter der Grenze fuer Bedienelemente (1.4.11). */
.chapter-rail__dot::before {
  content: "";
  display: block;
  width: var(--s-3);
  height: var(--s-3);
  border: var(--border-hair) solid var(--color-line-strong);
  border-radius: var(--radius-pill);
  background: transparent;
  box-shadow: 0 0 0 var(--border-hair) var(--color-rail-halo);
  transition: background 0.3s ease, transform 0.3s ease, border-color 0.3s ease;
}

/* Kundenwunsch 06.08.2026: weisse Outline, beige Fuellung. Vorher trug der
   Hover `--color-text` in Rahmen **und** Flaeche — auf den hellen Panels ist
   das bone-050 auf bone-050, der Punkt verschwand beim Hover schlicht.
   Beide Farben sind themenunabhaengig (`--color-rail-*`), weil die Schiene
   ueber beiden Themen liegt; sichtbar bleibt der Punkt auf hellem Grund ueber
   den Saum der Basisregel. */
.chapter-rail__dot:hover::before,
.chapter-rail__dot:focus-visible::before {
  border-color: var(--color-rail-line);
  background: var(--color-rail-fill);
}

.chapter-rail__dot[aria-current="true"]::before {
  border-color: var(--color-brand);
  background: var(--color-brand);
  transform: scale(1.6);
}

/* --------------------------------------------------------------------------
   reveal — das eine Entrance-Muster der Site (Kap. 15)
   Die verbergende Klasse wird per JS hinzugefuegt, nie als CSS-Default.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
  .js-motion .is-reveal {
    opacity: 0;
    transform: translateY(var(--s-5));
  }

  .js-motion .is-reveal.is-revealed {
    opacity: 1;
    transform: none;
    transition: opacity 0.7s ease, transform 0.7s cubic-bezier(0.2, 0.8, 0.2, 1);
  }
}

/* --------------------------------------------------------------------------
   site-footer — in jede Seite byte-identisch dupliziert
   -------------------------------------------------------------------------- */

.site-footer {
  padding: var(--space-section) var(--space-gutter) var(--space-group);
  background: var(--color-surface);
}

/* Das Bewertungssiegel des Widgets liegt fix am unteren Bildschirmrand und
   ueberdeckt dort die letzten Fusslinks. Es ist Fremdcode, seine Position ist
   nicht steuerbar — also wird darunter Platz freigehalten und die Seite scrollt
   entsprechend weiter. 70 px ist das Siegel hoch, 100 px (`--s-25`) sind der
   Sicherheitsabstand (Kundenvorgabe 03.08.2026).

   `padding` und nicht `margin`: die Flaeche des Fusses soll den freigehaltenen
   Bereich mit abdecken, sonst blitzt darunter der Seitengrund durch. */
@media (max-width: 1024px) {
  .site-footer {
    padding-bottom: calc(var(--space-group) + var(--s-25));
  }
}

.site-footer__grid {
  display: grid;
  gap: var(--space-group);
  max-width: var(--container-wide);
  margin-inline: auto;
}

@media (min-width: 640px) {
  .site-footer__grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 1025px) {
  .site-footer__grid {
    grid-template-columns: repeat(4, 1fr);
  }
}

/* Social-Verweis mit Zeichen davor. Das Icon ist ein Inline-SVG: eine Icon-Datei
   waere ein zusaetzlicher Request, eine Icon-Schrift ein externer (Kap. 13). Es
   erbt die Textfarbe ueber `currentColor` und folgt damit Theme und Hover. */
.social-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-inline);
}

.icon-instagram {
  flex: none;
  width: var(--s-5);
  height: var(--s-5);
}

/* Der Anker braucht eine eigene Box, sonst hat der Fokusring nichts zu umfassen:
   ein `display: block`-Kind in einem inline-Anker laesst dessen Inline-Box leer
   zurueck, und die globale `:focus-visible`-Outline zeichnet ins Nichts.
   Dieselbe Loesung wie beim Logo im Kopfbalken (`.branch-chrome__logo`). */
.site-footer__logo {
  display: inline-flex;
  text-decoration: none;
}

.site-footer__logo img {
  display: block;
  width: auto;
  height: var(--s-8);
  margin-bottom: var(--space-comp);
}

.site-footer__heading {
  margin: 0 0 var(--space-comp);
  font-size: var(--text-sm);
  font-weight: var(--weight-extrabold);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--color-brand);
}

.site-footer__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-tight);
}

.site-footer__list a {
  color: var(--color-text-muted);
  font-size: var(--text-base);
}

.site-footer__list a:hover,
.site-footer__list a:focus-visible {
  color: var(--color-text);
}

/* Aktivzustand im Fuss — dieselbe Logik wie im Menue: Farbe plus Schriftschnitt,
   damit nicht die Farbe allein den Zustand traegt (1.4.1). Der Fuss ist hell
   gethemt, gold-800 gegen bone-050 sind 5,57 : 1. */
.site-footer__list a[aria-current="page"] {
  color: var(--color-brand);
  font-weight: var(--weight-bold);
}

.site-footer__address {
  margin: 0;
  font-style: normal;
  font-size: var(--text-base);
  line-height: var(--leading-relaxed);
  color: var(--color-text-muted);
}

/* Maskierte E-Mail: sichtbarer Fallback vor dem Skript (Kap. 11) */
.gsInline {
  display: inline;
  overflow-wrap: anywhere;
}

/* --------------------------------------------------------------------------
   prose — Rechtstexte und andere reine Textseiten
   -------------------------------------------------------------------------- */

/* Rechtstexte: Kundenwunsch 03.08.2026 „ueber die volle Rasterbreite arbeiten".
   Der Fliesstext steigt von `--measure` (65ch) auf `--measure-wide` (76ch) — das
   ist die breiteste Stufe, die noch lesbar ist. Ganz ohne Begrenzung waeren es
   auf einem 1920er Bildschirm rund 1400 px Zeilenlaenge; die volle Breite nutzen
   deshalb die beiden Modifier darunter, indem sie **Spalten** bilden statt die
   Zeilen zu verlaengern. */
.prose {
  max-width: var(--measure-wide);
  margin-inline: auto;
}

/* Kurze, blockweise Dokumente (Impressum, Sitemap) fuellen das Raster in zwei
   Spalten. Nur fuer wenige kurze Bloecke: bei einem langen Dokument muesste man
   je Spalte einmal hoch und runter scrollen. */
/* Begrenzt auf dieselbe Rasterbreite wie der Fuss (`--container-wide`) und
   zentriert. Ohne das liefe der Text auf breiten Bildschirmen bis an die Gutter
   und stand haltlos im Raum (Kundenhinweis 03.08.2026). */
.prose--columns {
  max-width: var(--container-wide);
  margin-inline: auto;
}

.prose--columns > * {
  break-inside: avoid;
}

@media (min-width: 1025px) {
  .prose--columns {
    columns: 2;
    column-gap: var(--space-gutter);
  }

  /* Die Seitenueberschrift steht ueber beiden Spalten, nicht in der ersten. */
  .prose--columns > h1 {
    column-span: all;
  }
}

/* Zweispaltig mit Beiblatt: links der Text, rechts das Formular. Die beiden
   Kinder sind Wrapper-`div`s, die Typografie kommt weiter aus `.prose`. */
.prose--split {
  max-width: var(--container-wide);
  margin-inline: auto;
  display: grid;
  gap: var(--space-group);
  align-items: start;
}

@media (min-width: 1025px) {
  .prose--split {
    grid-template-columns: 1fr 1fr;
    gap: var(--space-gutter);
  }
}

/* Am Spaltenanfang schlaegt keine Ueberschrift Abstand auf — der kommt dort aus
   dem Raster. */
.prose--split > * > :first-child {
  margin-top: 0;
}

.prose h1 {
  margin: 0 0 var(--space-block);
  font-size: var(--text-display);
  font-weight: var(--weight-black);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  hyphens: auto;
  overflow-wrap: break-word;
}

.prose h2 {
  margin: var(--space-group) 0 var(--space-comp);
  font-size: var(--text-2xl);
  font-weight: var(--weight-extrabold);
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-tight);
  hyphens: auto;
  overflow-wrap: break-word;
}

.prose h3 {
  margin: var(--space-block) 0 var(--space-tight);
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  line-height: var(--leading-snug);
}

.prose h4 {
  margin: var(--space-block) 0 var(--space-tight);
  font-size: var(--text-md);
  font-weight: var(--weight-bold);
  line-height: var(--leading-snug);
}

.prose p,
.prose li {
  line-height: var(--leading-relaxed);
}

.prose ul,
.prose ol {
  padding-left: var(--space-block);
}

.prose a {
  overflow-wrap: anywhere;
}

.prose__intro {
  margin: 0 0 var(--space-group);
  font-size: var(--text-md);
  color: var(--color-text-muted);
}

/* --------------------------------------------------------------------------
   form — Formix-Formular (Feldzusammenstellung projektspezifisch,
   Integrationsmechanik verbatim nach Firmen-Javascripts-Doku)
   -------------------------------------------------------------------------- */

.form {
  display: grid;
  gap: var(--space-comp);
  max-width: var(--measure);
}

/* Die Felder stecken laut Firmen-Skelett jeweils in einem `<p>` (Kap. 10). Es
   gibt projektweit keinen Heading-/Absatz-Reset, also trug jedes davon die
   Browser-Vorgabe `margin: 1em 0` — und in einem Raster **addieren** sich die
   Raender der Kinder zum `gap`, sie kollabieren nicht. Zwischen zwei Feldern
   standen damit 16 + 16 + 16 = 48 px statt der gemeinten 16.

   Bewusst `> p` und nicht `> *`: der Honeypot ist ein `input` und braucht seinen
   negativen Rand (Kap. 10). Ein `> *` mit gleicher Spezifitaet wuerde ihn je nach
   Reihenfolge im Stylesheet ueberschreiben und das Feld sichtbar machen. */
.form > p {
  margin: 0;
}

.form label {
  display: block;
  margin-bottom: var(--space-inline);
  font-weight: var(--weight-bold);
}

.form input,
.form textarea {
  width: 100%;
  padding: var(--space-tight) var(--space-comp);
  border: var(--border-hair) solid var(--color-line-strong);
  border-radius: var(--radius-s);
  background: var(--color-surface-alt);
  color: var(--color-text);
}

.form input::placeholder,
.form textarea::placeholder {
  color: var(--color-text-muted);
}

.form__field {
  display: block;
}

.form__hint {
  display: block;
  margin-top: var(--space-inline);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

.form__error {
  display: flex;
  align-items: flex-start;
  gap: var(--space-inline);
  margin-top: var(--space-inline);
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  color: var(--color-danger);
}

.form__error::before {
  content: "\26A0";
}

/* Honeypot des Firmen-Bausteins: fuer Menschen unsichtbar, fuer Bots ein Feld.
   Klassenname snake_case verbatim nach Formix-Vorgabe.

   Versteckt ueber einen **negativen Rand** — Hausmittel, weil Bots darauf am
   haeufigsten hereinfallen. `display: none`, `visibility: hidden`, das
   `hidden`-Attribut oder `type="hidden"` erkennen sie und lassen das Feld leer;
   damit waere der Honeypot wertlos.

   `position: absolute` bleibt stehen, hat aber nur einen Zweck: das Feld aus dem
   Raster von `.form` zu nehmen. Als Rasterkind schlaegt es sonst eine eigene
   Zeile plus `gap` auf — 25 px Leerraum ueber dem ersten Feld, und mit einem
   negativen Rand allein bekommt man die nicht weg (eine Rasterzeile wird nie
   kleiner als 0, der `gap` bleibt).

   Der Rand zaehlt ab der eigenen Position im Layout, **nicht** ab dem Seitenrand
   — deshalb -10240px (`--s-40` x -64) und nicht ein knapper Wert: Das Formular
   steht auf der Kontaktseite in der rechten Spalte, ein Versatz von -640px
   liesse das Feld auf breiten Bildschirmen wieder im Bild landen. Nach links
   ueberstehender Inhalt erzeugt in LTR keinen Scrollbalken, ist also nicht
   erreichbar. */
.gotcha_field {
  position: absolute;
  margin-left: calc(var(--s-40) * -64);
  width: var(--border-hair);
  height: var(--border-hair);
  min-height: 0;
  padding: 0;
  border: 0;
  overflow: hidden;
}

.form-privacy-note {
  margin: 0;
  max-width: var(--measure);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

[data-captcha] {
  min-height: var(--s-11);
}

/* --------------------------------------------------------------------------
   facts — "Auf den Punkt": die Kernaussagen direkt unter dem Auftakt
   -------------------------------------------------------------------------- */

.facts {
  padding: var(--space-group) var(--space-gutter);
  background: var(--color-surface-alt);
}

.facts__list {
  list-style: none;
  margin: 0 auto;
  padding: 0;
  display: grid;
  gap: var(--space-block);
  max-width: var(--container-wide);
}

@media (min-width: 640px) {
  .facts__list { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 1025px) {
  .facts__list { grid-template-columns: repeat(4, 1fr); }
}

.facts__item {
  display: flex;
  flex-direction: column;
  gap: var(--space-inline);
  padding-top: var(--space-tight);
  border-top: var(--border-thick) solid var(--color-brand);
  line-height: var(--leading-normal);
}

.facts__num {
  font-size: var(--text-sm);
  font-weight: var(--weight-extrabold);
  letter-spacing: var(--tracking-wide);
  font-variant-numeric: tabular-nums;
  color: var(--color-brand);
}

/* --------------------------------------------------------------------------
   text-section — Kapitel mit Ueberschrift, Fliesstext und optionalem Bild
   -------------------------------------------------------------------------- */

.text-section {
  padding: var(--space-section) var(--space-gutter);
  background: var(--color-surface);
  color: var(--color-text);
}

.text-section__inner {
  display: grid;
  gap: var(--space-group);
  max-width: var(--container);
  margin-inline: auto;
}

.text-section--split .text-section__inner {
  align-items: center;
}

@media (min-width: 1025px) {
  .text-section--split .text-section__inner {
    grid-template-columns: 1fr 1fr;
  }
}

/* Kontaktseite: Text und Kontaktdaten links, Formular rechts. Basis ist
   `text-section--split`; abweichend ist nur die Ausrichtung — dort sind zwei
   etwa gleich hohe Bloecke zentriert, hier ist die rechte Spalte (das Formular)
   deutlich hoeher als die linke und beide sollen oben beginnen. */
.text-section--contact .text-section__inner {
  align-items: start;
}

.text-section__title {
  margin: 0 0 var(--space-block);
  font-size: var(--text-2xl);
  font-weight: var(--weight-extrabold);
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-tight);
  hyphens: auto;
  overflow-wrap: break-word;
}

/* Eine Stufe hoeher fuer Seiten, deren h1 im Abschnitt steht und nicht im
   Auftakt — sonst waere die Seitenueberschrift so gross wie die Abschnitts-
   ueberschrift daneben. */
.text-section__title--page {
  font-size: var(--text-3xl);
}

/* Eine Ueberschrift gehoert zu dem, was unter ihr steht: der Abstand nach oben
   muss groesser sein als der nach unten (--space-group gegen --space-block).
   Sonst liest sie sich als Schluss des Absatzes darueber.
   Nach der Kapitelzeile gilt das nicht — dort gehoeren beide zusammen, und der
   Abstand kommt aus deren unterem Rand.

   Der Wert haengt bewusst am **Vorgaenger** und nicht an einer `mt-*`-Utility am
   Element: `.text-section__title` setzt die Kurzschreibweise `margin`, und
   `components.css` laedt nach `utilities.css` — `class="… mt-group"` war dort
   wirkungslos, die Ueberschrift stand ohne oberen Abstand. */
:not(.eyebrow) + .text-section__title {
  margin-top: var(--space-group);
}

.text-section__lead {
  margin: 0;
  max-width: var(--measure);
  font-size: var(--text-md);
  line-height: var(--leading-relaxed);
  color: var(--color-text-muted);
}

/* `margin: 0` ist richtig, solange der Lead auf eine Ueberschrift folgt — deren
   unterer Rand traegt den Abstand. Folgt er auf einen Absatz, braucht er ihn
   selbst. Auch hier am Vorgaenger und nicht per `mt-*`-Utility, die von der
   Kurzschreibweise darueber geschlagen wuerde. */
p + .text-section__lead {
  margin-top: var(--space-block);
}

.text-section__lead strong {
  color: var(--color-text);
  font-weight: var(--weight-bold);
}

.text-section__figure {
  margin: 0;
}

.text-section__figure img {
  display: block;
  width: 100%;
  height: auto;
}

.check-list {
  list-style: none;
  margin: var(--space-block) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-tight);
  max-width: var(--measure);
}

.check-list li {
  display: flex;
  gap: var(--space-tight);
  line-height: var(--leading-relaxed);
  color: var(--color-text-muted);
}

.check-list li::before {
  content: "\2014";
  flex: none;
  color: var(--color-brand);
}

/* --------------------------------------------------------------------------
   gallery — Bildstrecke mit Bildunterschriften
   -------------------------------------------------------------------------- */

.gallery {
  padding: var(--space-section) var(--space-gutter);
  background: var(--color-surface);
}

.gallery__grid {
  list-style: none;
  margin: var(--space-block) auto 0;
  padding: 0;
  display: grid;
  gap: var(--space-comp);
  max-width: var(--container-wide);
}

@media (min-width: 640px) {
  .gallery__grid { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 1025px) {
  .gallery__grid--wide { grid-template-columns: repeat(4, 1fr); }
  .gallery__grid--three { grid-template-columns: repeat(3, 1fr); }
}

.gallery__figure {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-tight);
}

.gallery__figure img {
  display: block;
  width: 100%;
  height: auto;
  background: var(--color-surface-alt);
}

.gallery__caption {
  display: flex;
  gap: var(--space-tight);
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
  color: var(--color-text-muted);
}

.gallery__index {
  flex: none;
  font-weight: var(--weight-extrabold);
  font-variant-numeric: tabular-nums;
  color: var(--color-brand);
}

/* Bilder unterschiedlicher Formate in einer Reihe: ein gemeinsames
   Seitenverhaeltnis haelt die Kacheln gleich hoch, `object-fit: cover`
   schneidet zu, statt zu verzerren. 4:3 ist bewusst das Format der Mehrheit -
   die beiden Querformate liegen exakt darauf und werden gar nicht
   beschnitten, nur das Hochformat bekommt einen Ausschnitt. Der sitzt bei 25 %
   und nicht mittig, weil mittig den Blitzkopf oben abschneiden wuerde. */
.gallery__grid--uniform .gallery__figure img {
  aspect-ratio: 4 / 3;
  object-fit: cover;
  object-position: center 25%;
}

/* --------------------------------------------------------------------------
   benefits — drei bis vier Vorteils-Karten
   -------------------------------------------------------------------------- */

.benefits {
  padding: var(--space-section) var(--space-gutter);
  background: var(--color-surface);
}

.benefits__grid {
  list-style: none;
  margin: var(--space-block) auto 0;
  padding: 0;
  display: grid;
  gap: var(--space-block);
  max-width: var(--container);
}

@media (min-width: 640px) {
  .benefits__grid { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 1025px) {
  .benefits__grid--three { grid-template-columns: repeat(3, 1fr); }
}

/* Vier Karten erst ab 1300px (Kundenwunsch 06.08.2026) — darunter bleiben es
   die zwei Spalten der Basisregel. Zwischen 1025 und 1299px standen sie zu
   viert und damit zu schmal fuer ihren Text; das Raster faellt jetzt eine Stufe
   frueher. Der Dreier bleibt bei 1025px, drei Spalten passen dort.

   Bewusst ein komponenteneigener Umbruch und kein neuer globaler Breakpoint:
   Er haengt an der Kartenbreite dieses einen Rasters, nicht am Layout der
   Seite. Betrifft `/unternehmen/leistungen` und `/hochzeiten/hochzeitsfotos`,
   die beiden Seiten mit vier Karten. */
@media (min-width: 1300px) {
  .benefits__grid--four { grid-template-columns: repeat(4, 1fr); }
}

.benefits__card {
  padding: var(--space-block);
  border: var(--border-hair) solid var(--color-line);
  border-radius: var(--v2-panel-radius);
  background: var(--color-surface-alt);
}

.benefits__title {
  margin: 0 0 var(--space-tight);
  font-size: var(--text-lg);
  font-weight: var(--weight-extrabold);
  line-height: var(--leading-snug);
}

.benefits__text {
  margin: 0;
  color: var(--color-text-muted);
  line-height: var(--leading-relaxed);
}

/* --------------------------------------------------------------------------
   partners — Logo-Reihe der Partnerbetriebe (Kundenwunsch 06.08.2026)

   Der Abschnitt ist **immer hell gethemt**, und das ist keine Geschmacksfrage:
   Die drei gelieferten Logos sind auf Weiss gerechnet — eines liegt magenta auf
   transparentem Grund, eines steht auf einer weissen Flaeche, eines ist eine
   gelbe Vollflaeche. Auf dunklem Grund waere das erste kaum zu sehen und die
   beiden anderen wuerden als weisse bzw. gelbe Kasten aus der Seite stechen.

   Jedes Logo sitzt deshalb auf einer eigenen weissen Kachel gleicher Hoehe;
   `object-fit: contain` haelt die drei unterschiedlichen Seitenverhaeltnisse
   (300x86, 300x147, 300x169) unverzerrt.
   -------------------------------------------------------------------------- */

.partners {
  padding: var(--space-section) var(--space-gutter);
  background: var(--color-surface);
}

.partners__lead {
  margin: var(--space-block) 0 0;
  max-width: var(--measure);
  color: var(--color-text-muted);
  line-height: var(--leading-relaxed);
}

.partners__grid {
  list-style: none;
  margin: var(--space-group) auto 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: var(--space-block);
  max-width: var(--container);
}

/* Die Kachel ist der Link — die ganze Flaeche ist damit ein Ziel, nicht nur die
   bedruckten Pixel des Logos. */
.partners__link {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: var(--s-28);
  padding: var(--space-block);
  border: var(--border-hair) solid var(--color-line);
  border-radius: var(--v2-panel-radius);
  /* Dieselbe Flaeche wie der Abschnitt, die Kachel definiert allein die
     Haarlinie. `--color-surface-alt` waere hier falsch: Es ist im hellen Theme
     bone-100, also warm getoent — der weisse Eigenhintergrund des zweiten Logos
     staende dann als hellerer Kasten darauf. */
  background: var(--color-surface);
  text-decoration: none;
  transition: border-color 0.3s ease, box-shadow 0.3s ease, transform 0.3s ease;
}

/* Keine Geometrie im Hover (Kap. 9, Regel vom 03.08.2026) — nur Rahmen,
   Schatten und Anhebung. */
.partners__link:hover,
.partners__link:focus-visible {
  border-color: var(--color-brand);
  box-shadow: var(--glass-shadow);
  transform: translateY(calc(-1 * var(--s-1)));
}

.partners__logo {
  display: block;
  width: auto;
  max-width: 100%;
  height: auto;
  max-height: var(--s-18);
}

/* --------------------------------------------------------------------------
   faq — Disclosure ueber <details>/<summary>: ohne JS vollstaendig bedienbar
   -------------------------------------------------------------------------- */

.faq {
  padding: var(--space-section) var(--space-gutter);
  background: var(--color-surface);
}

.faq__list {
  max-width: var(--measure);
  margin: var(--space-block) auto 0;
}

.faq__item {
  border-bottom: var(--border-hair) solid var(--color-line);
}

.faq__question {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-comp);
  min-height: var(--s-11);
  padding: var(--space-tight) 0;
  font-size: var(--text-md);
  font-weight: var(--weight-bold);
  line-height: var(--leading-snug);
  color: var(--color-text);
  cursor: pointer;
  list-style: none;
}

.faq__question::-webkit-details-marker {
  display: none;
}

.faq__question::after {
  content: "";
  flex: none;
  width: var(--s-2);
  height: var(--s-2);
  border-right: var(--border-thick) solid var(--color-brand);
  border-bottom: var(--border-thick) solid var(--color-brand);
  transform: rotate(45deg);
  transition: transform 0.2s ease;
}

.faq__item[open] > .faq__question::after {
  transform: rotate(225deg);
}

.faq__answer {
  margin: 0;
  padding: 0 0 var(--space-block);
  max-width: var(--measure);
  color: var(--color-text-muted);
  line-height: var(--leading-relaxed);
}

/* Auf- und Zufahren statt Aufploppen (Kundenwunsch 06.08.2026) — **ohne JS**,
   das Disclosure bleibt das native `details`/`summary` (Kap. 9).

   `::details-content` ist der Kasten, den der Browser um den Inhalt eines
   `<details>` legt; er ist erst seit kurzem ansprechbar. Zwei Zutaten braucht
   die Kurve:

   - `interpolate-size: allow-keywords` erlaubt den Uebergang auf `auto` — ohne
     das ist die Zielhoehe kein interpolierbarer Wert und der Kasten springt.
     Die Eigenschaft vererbt, sie steht deshalb am `details` und nicht global.
   - `content-visibility` muss **mit** in die Kurve, sonst nimmt der Browser den
     Inhalt beim Schliessen sofort aus dem Rendering und es faehrt nur beim
     Oeffnen. `allow-discrete` schaltet ihn stattdessen am Ende der Kurve.

   Faellt sauber zurueck: Browser ohne `::details-content` ignorieren den ganzen
   Block, das Disclosure oeffnet wie bisher sofort. Bei
   `prefers-reduced-motion: reduce` ebenso — Kap. 9. */
@media (prefers-reduced-motion: no-preference) {
  .faq__item {
    interpolate-size: allow-keywords;
  }

  .faq__item::details-content {
    block-size: 0;
    overflow: hidden;
    transition: block-size 0.35s var(--gate-ease-soft),
      content-visibility 0.35s allow-discrete;
  }

  .faq__item[open]::details-content {
    block-size: auto;
  }
}

/* --------------------------------------------------------------------------
   film-grid — Beispielfilme hinter der CMP (Kap. 12/13)
   Kartenlook nach "Version 02 - Hochzeitsvideos": Rahmen 16:9 mit Radius,
   Abspielknopf mittig, Laufzeit-Plakette unten rechts, Titel unter dem Rahmen,
   der erste Film ueber die volle Breite.
   Drei Zustaende: ohne JS und ohne Einwilligung das lokale Standbild mit dem
   Link auf YouTube; nach Einwilligung setzt die CMP den src und der Player
   deckt Standbild, Abspielknopf und Plakette ab.
   -------------------------------------------------------------------------- */

/* Kopfzeile wie in der Vorlage: Kapitelzeile und leiser Hinweis auf einer
   Grundlinie. Gleiche Aufteilung wie .gallery-x__head auf der Fotoseite —
   die Ueberschrift ist das Eyebrow, der Hinweis ein Absatz. */
.film-grid__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-tight);
  margin-bottom: var(--space-comp);
}

.film-grid__hint {
  margin: 0;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

.film-grid__arrow {
  color: var(--color-brand);
}

.film-grid {
  list-style: none;
  margin: var(--space-group) auto 0;
  padding: 0;
  display: grid;
  gap: var(--space-block);
  max-width: var(--container-wide);
}

@media (min-width: 640px) {
  .film-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 1025px) {
  .film-grid { grid-template-columns: repeat(3, 1fr); }
}

/* Der erste Film laeuft ueber die volle Breite (Vorlage) */
.film-grid__item--featured {
  grid-column: 1 / -1;
  margin-bottom: var(--space-comp);
}

/* Mobil ist die Galerie ein waagerechter Wischbereich (Kundenwunsch 03.08.2026).
   Sechs 16:9-Karten uebereinander sind auf dem Telefon eine lange Strecke;
   seitwaerts liegen sie auf einem Bildschirm, und die angeschnittene naechste
   Karte zeigt, dass es weitergeht.

   Bewusst **ohne Slider-Skript**: Ein `overflow-x: auto`-Bereich trennt die
   Achsen selbst — waagerecht wischen bewegt den Streifen, senkrecht wischen
   scrollt die Seite. Genau daran war die Bildstrecke der Fotoseite mit einem
   Transform gescheitert (Kap. 20). Eine Bibliothek waere ein externer Request
   (QA-Check 11), muesste lokal liegen und mitlizenziert werden, und braeuchte JS
   fuer etwas, das der Browser von sich aus kann — ohne JS bliebe der Streifen
   sonst unbedienbar.

   Die Schwelle ist die bestehende 640-px-Grenze des Rasters, kein neuer
   Breakpoint: darueber stehen zwei bzw. drei Spalten und passen. */
@media (max-width: 639px) {
  /* `overflow-y: hidden` ist Pflicht, nicht Kosmetik (Kundenhinweis
     06.08.2026: mobil stand neben dem waagerechten auch ein senkrechter
     Balken). Steht **eine** Achse auf `auto`, rechnet der Browser die andere
     von `visible` auf `auto` hoch — der Streifen war also in beide Richtungen
     scrollbar. Ueberhang gab es auch wirklich: Die Karten tragen
     `data-reveal`, und solange eine Karte nicht aufgedeckt ist, steht sie
     `--s-5` (20px) tiefer. Karten ausserhalb des Streifens sind vom
     Scrollcontainer geklippt, kreuzen also den Viewport nie und bleiben
     dauerhaft versetzt — der senkrechte Balken blieb damit stehen. */
  .film-grid {
    grid-auto-flow: column;
    grid-auto-columns: 82%;
    gap: var(--space-comp);
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    scroll-padding-inline: var(--space-gutter);
    overscroll-behavior-x: contain;
  }

  .film-grid__item {
    scroll-snap-align: center;
  }

  /* Im Streifen sind alle Karten gleich breit — die Vollbreite des ersten Films
     hat dort keinen Sinn. Seine groessere Titelstufe behaelt er. */
  .film-grid__item--featured {
    grid-column: auto;
    margin-bottom: 0;
  }
}

.film {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-tight);
}

.film__frame {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: var(--radius-m);
  background: var(--color-surface-alt);
}

.film__poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Das Standbild ist ein echter Link auf YouTube. Die Vorlage oeffnet dort eine
   Lightbox, die es hier nicht gibt (Kap. 9) — ein Abspielknopf, der nichts tut,
   waere die schlechtere Loesung. So ist der Film auch ohne JS und ohne
   Einwilligung mit einem Klick erreichbar. */
.film__cover {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Der Link fuellt den Rahmen genau aus, und der Rahmen klippt (overflow: hidden).
   Der Fokusring der globalen Regel liegt ausserhalb des Kastens und waere damit
   unsichtbar — er wird deshalb nach innen gelegt und folgt der Rundung. */
.film__cover:focus-visible {
  outline-offset: calc(-1 * (var(--border-thick) + var(--radius-s)));
  border-radius: var(--radius-m);
}

/* Sobald die CMP den src gesetzt hat, spielt der Player im Rahmen; der Link
   waere dann ein Tabstopp hinter dem iframe. Ohne :has()-Unterstuetzung bleibt
   er stehen und wird vom Player verdeckt. */
.film__frame:has(.film__player[src]) .film__cover {
  display: none;
}

.film__play {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--s-16);
  height: var(--s-11);
  border-radius: var(--radius-m);
  background: var(--color-overlay-mid);
  box-shadow: var(--glass-shadow-lift);
  transition: background 0.3s ease;
}

/* Das Dreieck ist ein Ausschnitt, kein Rahmen-Trick: es folgt damit der
   Textfarbe des Themes und braucht keine Pixelwerte. */
.film__play::before {
  content: "";
  width: 38%;
  height: 46%;
  background: var(--color-text);
  clip-path: polygon(0 0, 100% 50%, 0 100%);
}

.film-grid__item--featured .film__play {
  width: var(--s-20);
  height: var(--s-14);
}

.film__frame:hover .film__play,
.film__frame:focus-within .film__play {
  background: var(--color-overlay);
}

.film__duration {
  position: absolute;
  right: var(--space-inline);
  bottom: var(--space-inline);
  padding: var(--s-1) var(--space-inline);
  border-radius: var(--radius-s);
  background: var(--color-overlay);
  color: var(--color-text);
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  font-variant-numeric: tabular-nums;
}

.film__player {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

/* Ohne gesetzten src (keine Einwilligung) bleibt das Standbild sichtbar */
.film__player:not([src]) {
  display: none;
}

.film__caption {
  display: flex;
  flex-direction: column;
  gap: var(--space-inline);
}

.film__title {
  font-weight: var(--weight-bold);
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-tight);
}

.film-grid__item--featured .film__title {
  font-size: var(--text-md);
}

.film__meta {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

/* Mikrointeraktion (Kap. 9): Standbild und Abspielknopf treten unter dem Zeiger
   leicht hervor. Der Rahmen ist ein Link, die Reaktion also nicht irrefuehrend. */
@media (prefers-reduced-motion: no-preference) {
  .film__poster {
    transition: transform 0.7s cubic-bezier(0.2, 0.8, 0.2, 1);
  }

  .film__play {
    transition: background 0.3s ease,
      transform 0.4s cubic-bezier(0.2, 0.8, 0.2, 1);
  }

  .film__frame:hover .film__poster,
  .film__frame:focus-within .film__poster {
    transform: scale(1.04);
  }

  .film__frame:hover .film__play,
  .film__frame:focus-within .film__play {
    transform: scale(1.06);
  }
}

/* --------------------------------------------------------------------------
   scroll-progress — Lesefortschritt am Seitenkopf (dekorativ)
   Teil des Header-Wiederholblocks; ohne JS bleibt der Balken bei 0.
   -------------------------------------------------------------------------- */

/* Der Lesefortschritt liegt seit dem 31.07.2026 auf Dokumentebene, nicht in der
   Chrome: Ein positioniertes Element mit `z-index` bildet einen eigenen
   Stapelkontext, aus dem ein Kind nicht herausreichen kann — der Balken lag
   deshalb hinter dem Menue-Button. */
.scroll-progress {
  position: fixed;
  inset: 0 0 auto;
  z-index: var(--z-progress);
  height: var(--border-thick);
  background: var(--color-control-bg);
}

.scroll-progress__bar {
  display: block;
  height: 100%;
  width: calc(100% * var(--scroll-p, 0));
  background: var(--color-brand);
}

/* --------------------------------------------------------------------------
   film-grain — feines Filmkorn ueber der Seite (dekorativ, kein Request)
   -------------------------------------------------------------------------- */

.film-grain {
  position: fixed;
  inset: 0;
  z-index: var(--z-grain);
  pointer-events: none;
  opacity: 0.05;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/></filter><rect width='160' height='160' filter='url(%23n)'/></svg>");
}

/* --------------------------------------------------------------------------
   Inszenierung der vier Signatur-Momente des Entwurfs
   Alle scrollgetriebenen Werte kommen aus main.js als Custom Properties und
   greifen nur bei html.js-motion und ausreichend grossem Viewport.
   -------------------------------------------------------------------------- */

@media (min-height: 600px) {
  /* Auftakt: Bild zieht beim Verlassen leicht auf und dunkelt ab */
  .js-motion .hero__media img {
    transform: scale(calc(1 + 0.14 * var(--hero-p, 0)));
    filter: brightness(calc(1 - 0.3 * var(--hero-p, 0)));
    will-change: transform, filter;
  }

  .js-motion .hero__hint {
    opacity: calc(1 - 2 * var(--hero-p, 0));
  }

  /* Waagerechte Bildstrecke: die Buehne klebt, der Streifen faehrt seitwaerts.
     Der Weg ist die eigene Ueberbreite — `100 %` ist die Streifenbreite, `100vw`
     der Bildschirm. JS schreibt nur den Fortschritt, gemessen wird nichts. */
  /* Die Hoehe haengt an der Kachelzahl und ist keine freie Zahl: Der Weg ist die
     Ueberbreite des Streifens, die Scrollstrecke ist `Hoehe - 100vh`. Aus beidem
     ergibt sich, wie viele Pixel Bildlauf ein Pixel Scrollen bringt — zu wenige,
     und die Strecke laeuft zaeh; zu viele, und sie huscht vorbei.

     Gerechnet fuer 1440 x 900 (alle Kachelbreiten sind dort im fluiden Bereich
     ihrer clamp()-Werte, die Buehne ist 1399,68 px breit):

       acht Kacheln  (bis 26.08.2026): Ueberbreite 2390 px, 190vh -> **2,95 px/px**
       sechzehn Kacheln (seit 26.08.2026): Ueberbreite 6938 px, 350vh -> 3,08 px/px

     190vh mit sechzehn Kacheln waeren 8,6 px/px gewesen — die Strecke waere in
     einem Wisch durchgelaufen. Der historische Beleg fuer dieselbe Rechnung
     steht in CLAUDE.md Kap. 9: ein zeitweise verdoppelter Streifen lief mit
     330vh, also bei rund 3,0 px/px.

     **Wer die Kachelzahl aendert, zieht diese Hoehe mit.** Mobil ist sie ohne
     Belang — dort steht `height: auto` (Block ganz unten). */
  .js-motion .gallery-x {
    height: 350vh;
  }

  /* Angepinnt ist die Buehne bildschirmhoch und ihr Inhalt darin **mittig**
     (Kundenwunsch 07.08.2026). Ohne das ist sie nur inhaltshoch: sie klebte
     bei `top` und die Strecke hing waehrend der ganzen Seitwaertsfahrt am
     oberen Rand, darunter drei Viertel leerer Passepartout.

     `min-height` und nicht `height`: Waere der Inhalt einmal hoeher als der
     Bildschirm — hohes Hochformat auf einem flachen Fenster —, wuerde `height`
     ihn oben und unten abschneiden (`overflow: hidden` traegt die Buehne).
     Mit `min-height` waechst sie stattdessen. Gerechnet fuer 1440 x 900: rund
     652 px Inhalt gegen 880 px Buehne, es zentriert also wirklich.

     Der Abzug ist `--v2-panel-inset` (zwei Rahmen), passend zu `top`, das einen
     abzieht — damit steht unten derselbe Rand wie oben. Ausserhalb der
     Inszenierung bleibt die Buehne inhaltshoch: dort waere der leere Kasten der
     Fehler, den der Kundenwunsch vom 31.07.2026 abgestellt hat. */
  .js-motion .gallery-x__stage {
    position: sticky;
    top: var(--v2-mat-gap);
    min-height: calc(100vh - var(--v2-panel-inset));
    min-height: calc(100svh - var(--v2-panel-inset));
    justify-content: center;
  }

  /* `width: max-content` ist der Kern: Als Flex-Container im Fluss war der
     Streifen nur so breit wie die Buehne — `100 %` in `translateX` entsprach
     also dem Bildschirm, die Differenz war null und er fuhr nicht. Mit
     `max-content` ist `100 %` die Summe der Kacheln.

     Der Weg ist die Ueberbreite gegenueber der **Buehne**, nicht gegenueber
     dem Bildschirm: die Buehne ist um das Passepartout schmaler. `100cqw`
     liefert genau ihre Breite; die `vw`-Zeile darueber ist der Vorschaltwert. */
  .js-motion .gallery-x__stage {
    container-type: inline-size;
  }

  .js-motion .gallery-x__strip {
    width: max-content;
    overflow-x: visible;
    transform: translateX(calc(-1 * var(--strip-p, 0) * (100% - 100vw)));
    transform: translateX(calc(-1 * var(--strip-p, 0) * (100% - 100cqw)));
    will-change: transform;
  }

  /* Abschluss: die beiden Bildhaelften fahren auseinander */
  .js-motion .split-cta {
    display: block;
    height: 220vh;
    padding: 0;
    /* ZWINGEND: overflow:hidden auf der Section macht die Buehne darin zum
       Inhalt eines nicht scrollenden Kastens — position:sticky bleibt dann
       wirkungslos und die Bilder scrollen aus dem Viewport. `clip` klippt
       zwar (fuer die runden Ecken), ist aber kein Scrollport — sticky bleibt
       wirksam. Ohne Unterstuetzung greift der Vorschaltwert `visible`. */
    overflow: visible;
    overflow: clip;
  }

  .js-motion .split-cta__stage {
    position: sticky;
    top: var(--v2-mat-gap);
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: calc(100vh - var(--v2-panel-inset));
    height: calc(100svh - var(--v2-panel-inset));
    padding-inline: var(--space-gutter);
    overflow: hidden;
  }

  .js-motion .split-cta__half {
    transform: translate3d(calc(var(--dir, 1) * var(--split-p, 0) * 100%), 0, 0);
    will-change: transform;
  }

  .js-motion .split-cta__half--left { --dir: -1; }
  .js-motion .split-cta__half--right { --dir: 1; }

  /* „Version 02": Vorhang — Weg 104 %, Zoom auf 1.08. Die Haelften stehen in
     normaler Saettigung und bleichen beim Aufziehen aus (Kundenwunsch statt der
     Abdunkelung des Entwurfs). Die Textsaeule folgt einer eigenen Kurve. */
  .js-motion[data-layout="v02"] .split-cta__half {
    transform: translate3d(calc(var(--dir, 1) * var(--split-p, 0) * 104%), 0, 0)
      scale(calc(1 + var(--split-p, 0) * 0.08));
    filter: brightness(calc(1 + var(--split-p, 0) * var(--split-pale-bright)))
      saturate(calc(1 - var(--split-p, 0) * var(--split-pale-sat)));
    will-change: transform, filter;
  }

  .js-motion[data-layout="v02"] .split-cta {
    height: 240vh;
  }

  /* Der Entwurf hat hier keinen Scrim: der Vorhang gibt die dunkle Grundflaeche
     frei, und darauf steht der helle Text. Also folgt der Scrim der Textkurve —
     im Ruhezustand 0 (Bilder in normaler Saettigung), und sobald der Text
     auftaucht, deckt er die noch nicht abgefahrenen Bildreste ab. Ueber der
     freigelegten Grundflaeche ist er unsichtbar: dieselbe Farbe. */
  .js-motion[data-layout="v02"] .split-cta__scrim {
    opacity: var(--split-copy-p, 1);
  }

  /* Die Textsaeule setzt erst ein, wenn der Vorhang schon laeuft:
     Fortschritt 0.14 bis 0.64 der Buehne. */
  .js-motion[data-layout="v02"] .split-cta__body {
    opacity: var(--split-copy-p, 1);
    transform: translateY(calc((1 - var(--split-copy-p, 1)) * 46px * var(--v2-fx)))
      scale(calc(0.94 + var(--split-copy-p, 1) * 0.06));
    will-change: transform, opacity;
  }

  /* Sobald etwas in der Textsaeule den Fokus hat, steht sie voll da: deckend,
     unverschoben, mit Scrim (Kundenwunsch 03.08.2026).

     Grund ist ein Konflikt zwischen Tastatur und Inszenierung: Die Buehne ist
     `sticky` in einer 220vh hohen Section, die Saeule wird per `transform`
     verschoben und faehrt erst ab 14 % der Buehne ein. Beim Tabben scrollt der
     Browser die **Layout**-Position des Knopfes ins Bild — und die liegt am
     Anfang der Section, wo `--split-copy-p` noch 0 ist. Der Knopf war deshalb
     halb sichtbar und die Schrift unsichtbar.

     `transform: none` bringt die Saeule auf ihre Layout-Position, also genau
     dorthin, wohin der Browser gescrollt hat; `opacity: 1` macht sie lesbar. Der
     Scrim kommt mit, sonst stuende der helle Text auf dem unabgedunkelten Bild
     (Kontraste in Kap. 19). Ohne Fokus gilt wieder die Scrollkurve. */
  .js-motion[data-layout="v02"] .split-cta:has(:focus-visible) .split-cta__body {
    opacity: 1;
    transform: none;
  }

  .js-motion[data-layout="v02"] .split-cta:has(:focus-visible) .split-cta__scrim {
    opacity: 1;
  }
}

/* duo: Bildwechsel nach Zeigerposition (Mikrointeraktion, JS-Ergaenzung) */
.duo.is-left .duo__layer--left,
.duo.is-right .duo__layer--right {
  opacity: 1;
}

.duo.is-left .duo__layer--both,
.duo.is-right .duo__layer--both {
  opacity: 0;
}

/* ==========================================================================
   gate — Vorschaltseite, 1:1 nach „Version 02 - Vorschaltseite"
   Ein Bildschirm, kein Scrollen, eigene Chrome (Ecken-Logo + Kreis-Menue).
   Werte aus dem Block „6 · Vorschaltseite" in tokens.css.
   ========================================================================== */

[data-page="gate"],
[data-page="gate"] body {
  height: 100%;
}

.js[data-page="gate"],
.js[data-page="gate"] body {
  overflow: hidden;
}

/* Ohne JS bleibt die Seite scrollbar, damit das Menue erreichbar ist (Kap. 8).
   Den Ein-Bildschirm-Riegel des Entwurfs setzt erst .gate--js. */
.gate {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--gate-stack-gap);
  width: 100%;
  min-height: 100vh;
  min-height: 100dvh;
  padding: var(--gate-pad-top) 0 var(--gate-pad-bottom);
  background: var(--color-surface);
  color: var(--color-text);
  font-size: var(--gate-text);
  line-height: var(--leading-relaxed);
}

.gate--js {
  width: 100vw;
  height: 100vh;
  height: 100dvh;
  min-height: 0;
  overflow: hidden;
}

/* --- Section 1: Bildebenen mit Verlaufs-Abdeckung --- */

.gate__media {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.gate__layer {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 50%;
  transform-origin: 50% 50%;
  transition: opacity 0.68s var(--gate-ease-swap),
    transform 0.95s var(--gate-ease-swap);
}

.gate__layer--left {
  opacity: 0;
  transform: scale(1.04);
  transform-origin: 38% 50%;
}

.gate__layer--right {
  opacity: 0;
  transform: scale(1.04);
  transform-origin: 62% 50%;
}

.gate.is-left .gate__layer--left,
.gate.is-right .gate__layer--right {
  opacity: 1;
  transform: scale(1);
}

.gate.is-left .gate__layer--both,
.gate.is-right .gate__layer--both {
  opacity: 0;
  transform: scale(1.04);
}

.gate__scrim {
  position: absolute;
  inset: 0;
  background: radial-gradient(
    120% 96% at 50% 40%,
    var(--color-scrim-50) 0%,
    var(--color-scrim-80) 62%,
    var(--color-scrim-97) 100%
  );
}

.gate__scrim--top {
  position: absolute;
  inset: 0 0 auto;
  height: 34%;
  background: linear-gradient(180deg, var(--color-scrim-82), var(--color-scrim-00));
}

.gate .film-grain {
  z-index: var(--z-base);
}

/* --- Section 2: Ecken-Logo --- */

.gate__logo {
  position: absolute;
  left: var(--gate-chrome-x);
  top: var(--gate-logo-y);
  z-index: var(--z-raised);
  display: flex;
  align-items: center;
  text-decoration: none;
}

.gate__logo img {
  display: block;
  width: auto;
  height: var(--gate-logo-h);
}

/* --- Section 3: Menue-Button mit Balken, die zum X werden --- */

.mainmenu__button {
  display: flex;
  align-items: center;
  gap: var(--space-tight);
  min-height: var(--s-11);
  /* Weder Flaeche noch Rahmen (Kundenwunsch 31.07.2026) — beides muss explizit
     aus, sonst zeichnet der Browser seine Vorgabe fuer <button>. */
  padding: 0;
  border: 0;
  background: none;
  color: var(--color-text);
  pointer-events: auto;
  font-family: inherit;
  font-size: var(--gate-menu-label);
  font-weight: var(--weight-extrabold);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  cursor: pointer;
  /* `color` gehoert in die Liste: der Hover unten wechselt sie, und ohne den
     Eintrag springt sie hart um (Kundenhinweis 07.08.2026). */
  transition: transform 0.45s var(--gate-ease-soft), box-shadow 0.45s ease,
    background 0.45s ease, color 0.3s ease;
}

.mainmenu__button:hover,
.mainmenu__button:focus-visible {
  color: var(--color-brand);
}

.js .mainmenu__button {
  display: flex;
}

.mainmenu__bars {
  position: relative;
  display: block;
  width: var(--s-5);
  height: var(--s-3);
  flex: none;
}

.mainmenu__bar {
  position: absolute;
  left: 0;
  width: var(--s-5);
  height: var(--border-thick);
  background: var(--color-text);
  transition: transform 0.5s var(--gate-ease-iris), opacity 0.3s ease;
}

/* Der mittlere Balken sitzt auf der Mittellinie des Kastens, nicht auf der
   halben Kastenhoehe: die Balken haben eine eigene Dicke, sonst sind die beiden
   Abstaende ungleich (4 px und 2 px statt 3 und 3). Dieselbe Mitte fahren die
   aeusseren Balken im X-Zustand an — vorher blieben sie 2 px auseinander und
   das Kreuz wirkte verzogen. */
.mainmenu__bar:nth-child(1) { top: 0; }
.mainmenu__bar:nth-child(2) { top: var(--menu-bar-mid); }
.mainmenu__bar:nth-child(3) { bottom: 0; }

.mainmenu__button[aria-expanded="true"] .mainmenu__bar:nth-child(1) {
  transform: translateY(var(--menu-bar-mid)) rotate(45deg);
}

.mainmenu__button[aria-expanded="true"] .mainmenu__bar:nth-child(2) {
  opacity: 0;
}

.mainmenu__button[aria-expanded="true"] .mainmenu__bar:nth-child(3) {
  transform: translateY(calc(-1 * var(--menu-bar-mid))) rotate(-45deg);
}

/* --- Section 4: Menue als Kreis-Aufblende aus dem Button --- */

.mainmenu {
  position: static;
  z-index: var(--z-nav-panel);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--gate-menu-gap);
  padding: var(--gate-menu-pt) var(--gate-menu-px) var(--gate-menu-pb);
  background: var(--color-menu-veil);
}

/* Erst mit JS wird das Menue zur Kreis-Aufblende aus dem Button */
.js .mainmenu {
  position: fixed;
  inset: 0;
  overflow-y: auto;
  clip-path: circle(0% at calc(100% - var(--s-18)) var(--s-11));
  opacity: 0;
  visibility: hidden;
  transition: clip-path 0.7s var(--gate-ease-iris), opacity 0.45s ease,
    visibility 0s linear 0.7s;
}

@supports (backdrop-filter: blur(1px)) {
  .mainmenu {
    backdrop-filter: blur(var(--s-5)) saturate(120%);
  }
}

.js .mainmenu.is-open {
  clip-path: circle(155% at calc(100% - var(--s-18)) var(--s-11));
  opacity: 1;
  visibility: visible;
  transition: clip-path 0.9s var(--gate-ease-iris), opacity 0.4s ease,
    visibility 0s;
}

.mainmenu__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(290px, 1fr));
  gap: var(--gate-menu-col-gap);
  width: 100%;
  max-width: var(--container-wide);
  margin-inline: auto;
}

.mainmenu__col {
  min-width: 0;
}

.mainmenu__top {
  display: block;
  font-size: var(--gate-menu-top);
  font-weight: var(--weight-black);
  line-height: var(--leading-split);
  letter-spacing: -0.04em;
  text-transform: uppercase;
  color: var(--color-text);
  text-decoration: none;
  transition: color 0.3s ease;
}

/* Keine Laufweiten-Aenderung im Hover: sie verbreitert den Eintrag und schiebt
   die Spalte um (Kundenhinweis 03.08.2026). Die Farbe traegt die Rueckmeldung. */
.mainmenu__top:hover,
.mainmenu__top:focus-visible {
  color: var(--color-brand);
}

.mainmenu__rule {
  display: block;
  height: var(--border-hair);
  margin: var(--gate-menu-rule) 0;
  background: var(--color-line);
}

.mainmenu__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--gate-menu-list-gap);
}

.mainmenu__link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-tight);
  font-size: var(--gate-menu-sub);
  font-weight: var(--weight-semibold);
  color: var(--color-text-muted);
  text-decoration: none;
  transition: color 0.3s ease;
}

.mainmenu__link:hover,
.mainmenu__link:focus-visible {
  color: var(--color-text);
}

.mainmenu__dash {
  display: block;
  width: var(--s-4);
  height: var(--border-hair);
  background: var(--color-brand);
}

.mainmenu__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--gate-menu-bottom-gap);
  width: 100%;
  max-width: var(--container-wide);
  margin-inline: auto;
  padding-top: var(--gate-menu-bottom-pt);
  border-top: var(--border-hair) solid var(--color-line);
  font-size: var(--gate-menu-meta);
}

.mainmenu__meta a {
  color: var(--color-text-muted);
  text-decoration: none;
  transition: color 0.3s ease;
}

.mainmenu__meta a:hover,
.mainmenu__meta a:focus-visible {
  color: var(--color-text);
}

/* --------------------------------------------------------------------------
   Aktivzustand — `aria-current="page"` setzt `main.js` aus location.pathname
   (normalisiert: `.html` weg, Trailing Slash weg; Anker sind ausgenommen).

   Jeder Zustand traegt **zwei** Signale, Farbe allein genuegt nicht (1.4.1):
   der Ast seine Linienstaerke, die Unterseite Laenge und Staerke ihres Strichs,
   die Meta-Zeile den Schriftschnitt. Und `aria-current` selbst ist das
   programmatische Signal.

   Kontraste im hellen Menue: gold-800 5,57 : 1, ink-800 17,20 : 1.
   -------------------------------------------------------------------------- */

.mainmenu__top[aria-current="page"] {
  color: var(--color-brand);
}

/* Die Haarlinie unter dem Ast-Namen ist der Traeger: gold und doppelt stark,
   wenn die Ast-Seite selbst offen ist. Adjazenter Nachbar statt eines eigenen
   Elements — kein Umbruch im Markup und kein Layoutsprung. */
.mainmenu__top[aria-current="page"] + .mainmenu__rule {
  height: var(--border-thick);
  background: var(--color-brand);
}

/* Ist eine Unterseite des Astes offen, wird die Linie gold, bleibt aber
   Haarlinie — leiser als der Ast selbst. Ohne `:has()`-Unterstuetzung entfaellt
   nur dieser Hinweis; der aktive Eintrag darunter ist ohnehin markiert. */
.mainmenu__col:has(.mainmenu__link[aria-current="page"]) .mainmenu__rule {
  background: var(--color-brand);
}

.mainmenu__link[aria-current="page"] {
  color: var(--color-text);
  font-weight: var(--weight-bold);
}

.mainmenu__link[aria-current="page"] .mainmenu__dash {
  width: var(--s-8);
  height: var(--border-thick);
}

.mainmenu__meta a[aria-current="page"] {
  color: var(--color-brand);
  font-weight: var(--weight-bold);
}

/* Gestaffeltes Einblenden der Menue-Eintraege (JS setzt --i)

   Die Basis ist 0,55 s und nicht die 0,22 s der Vorlage (Kundenhinweis
   07.08.2026: „Hochzeiten" hatte keine Animation). Der Grund ist die
   Kreis-Aufblende: sie oeffnet aus dem Menue-Button, also aus der oberen
   rechten Ecke, und gibt die Eintraege nacheinander frei — der frueheste erst
   bei rund 0,42 s, „Hochzeiten" als am weitesten entfernter Eintrag erst bei
   rund 0,50 s. Mit 0,22 s Basis lief dessen Bewegung vollstaendig hinter dem
   geschlossenen Kreis ab: bei der Freilegung waren von den 24 px noch 1,5 px
   uebrig (Ease-out `--gate-ease-soft` ist stark frontlastig).

   0,55 s liegt ueber **jeder** Freilegungszeit, damit ist die Staffelung auf
   allen Eintraegen sichtbar. Die DOM-Reihenfolge bleibt; nur die Basis wandert.
   Gerechnet fuer 1440 x 900 gegen `circle(155% at calc(100% - var(--s-18))
   var(--s-11))` mit `--gate-ease-iris`.

   Der Hover-Wechsel der Schriftfarbe steht **mit in dieser Liste**, und die
   Staffelung laeuft ueber `--enter-delay` statt ueber `transition-delay`
   (Kundenhinweis 07.08.2026: die Navigation hatte keine Hover-Transition).

   Zwei Gruende, beide zwingend:

   1. `.mainmenu__top` und `.mainmenu__link` bringen ihr eigenes
      `transition: color 0.3s ease` mit — aber sie tragen **auch**
      `mainmenu__item`, und diese Regel hier ist mit (0,2,0) staerker als deren
      (0,1,0). Die Kurzschreibweise `transition` schreibt die ganze Liste, also
      war der Farbwechsel dort ueberschrieben und sprang hart um. Sichtbar wurde
      es nur im Menue: die Meta-Zeile ist selbst das `mainmenu__item`, ihre
      Links sind Kinder und behielten ihre Transition. Ohne JS greift diese
      Regel nicht, dort haben die beiden Basisregeln immer funktioniert.
   2. `transition-delay` als eigene Zeile gilt fuer **alle** Eigenschaften der
      Liste. Mit `color` darin haette der Hover die Staffelung geerbt — 0,55 bis
      1,03 s Verzoegerung, und zwar genau dann, wenn das Menue offen ist und
      gehovert wird. Die Verzoegerung haengt deshalb als Custom Property an den
      beiden Eintritts-Eigenschaften und nur an ihnen.

   Beim Schliessen faellt `--enter-delay` auf 0s zurueck — dieselbe Wirkung wie
   vorher, als `transition-delay` allein in der `.is-open`-Regel stand. */
.js .mainmenu__item {
  --enter-delay: 0s;
  opacity: 0;
  transform: translateY(var(--s-6));
  transition: opacity 0.5s ease var(--enter-delay),
    transform 0.6s var(--gate-ease-soft) var(--enter-delay),
    color 0.3s ease;
}

.js .mainmenu.is-open .mainmenu__item {
  --enter-delay: calc(0.55s + var(--i, 0) * 0.06s);
  opacity: 1;
  transform: translateY(0);
}

/* --- Section 5: Mitte — Eyebrow, Titel, Phrasen, zwei Wege --- */

.gate__body {
  position: relative;
  flex: none;
  text-align: center;
  padding-inline: var(--gate-inline);
  max-width: var(--container-wide);
}

.gate__eyebrow {
  margin: 0 0 var(--gate-lead-gap);
  font-size: var(--gate-eyebrow);
  font-weight: var(--weight-extrabold);
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
  color: var(--color-eyebrow);
  text-shadow: 0 var(--border-hair) var(--s-3) var(--color-scrim-80);
}

.gate__stack {
  display: inline-block;
  max-width: 100%;
}

.gate__title {
  display: flex;
  justify-content: center;
  margin: 0;
  font-size: var(--gate-title);
  font-weight: var(--weight-black);
  line-height: var(--leading-display);
  letter-spacing: var(--tracking-display);
  text-transform: uppercase;
}

.gate__char {
  display: inline-block;
}

.gate__phrases {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--gate-row-gap);
  margin: var(--gate-phrase-top) 0 0;
}

.gate__phrase {
  margin: 0;
  white-space: nowrap;
  color: var(--color-brand);
  transition: filter 0.45s ease;
}

.gate__phrase--script {
  font-family: var(--font-script);
  font-size: var(--gate-script);
  line-height: var(--leading-script);
}

.gate__phrase--sans {
  font-size: var(--gate-phrase);
  font-weight: var(--weight-semibold);
  line-height: var(--leading-duo);
  letter-spacing: -0.02em;
}

.gate.is-right .gate__phrase--script,
.gate.is-left .gate__phrase--sans {
  filter: opacity(0.3);
}

.gate__actions {
  display: flex;
  align-items: stretch;
  justify-content: space-between;
  gap: var(--gate-row-gap);
  margin: var(--gate-cta-top) 0 0;
}

.gate__cta {
  position: relative;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  gap: var(--gate-cta-gap);
  padding: var(--gate-cta-py) var(--gate-cta-px);
  border: var(--border-hair) solid var(--color-glass-34);
  border-radius: var(--radius-pill);
  font-size: var(--gate-cta-text);
  font-weight: var(--weight-extrabold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--color-text);
  text-decoration: none;
  transition: color 0.4s ease, border-color 0.4s ease,
    transform 0.5s var(--gate-ease-soft), box-shadow 0.5s ease, filter 0.45s ease;
}

.gate__cta-fill {
  position: absolute;
  inset: 0;
  background: var(--color-brand);
  transform: scaleX(0);
  transition: transform 0.55s var(--gate-ease-soft);
}

.gate__cta--left .gate__cta-fill { transform-origin: left center; }
.gate__cta--right .gate__cta-fill { transform-origin: right center; }

.gate__cta-label,
.gate__cta-arrow {
  position: relative;
}

.gate__cta-arrow {
  transition: transform 0.45s var(--gate-ease-soft);
}

.gate__cta:hover,
.gate__cta:focus-visible {
  border-color: transparent;
  color: var(--color-text-invert);
  transform: translateY(calc(var(--s-1) * -1));
  box-shadow: var(--glass-shadow-lift);
}

.gate__cta:hover .gate__cta-fill,
.gate__cta:focus-visible .gate__cta-fill {
  transform: scaleX(1);
}

.gate__cta:hover .gate__cta-arrow,
.gate__cta:focus-visible .gate__cta-arrow {
  transform: translateX(var(--s-2));
}

/* Zustand nach Zeigerzone: aktiver Weg akzentuiert, anderer zurueckgenommen */
.gate.is-left .gate__cta--left:not(:hover):not(:focus-visible),
.gate.is-right .gate__cta--right:not(:hover):not(:focus-visible) {
  border-color: var(--color-brand);
  color: var(--color-brand);
  transform: translateY(calc(var(--s-1) * -0.25));
}

.gate.is-left .gate__cta--right:not(:hover):not(:focus-visible),
.gate.is-right .gate__cta--left:not(:hover):not(:focus-visible) {
  filter: opacity(0.4);
}

/* --- Auftritt der Mitte: softIn/charIn wie im Entwurf --- */

@media (prefers-reduced-motion: no-preference) {
  .js-motion .gate__eyebrow {
    animation: gate-soft-in 0.8s ease both;
  }

  .js-motion .gate__char {
    animation: char-in 0.95s var(--gate-ease-char) both;
    animation-delay: calc(0.06s + var(--i, 0) * 0.055s);
  }

  .js-motion .gate__phrase--script {
    animation: gate-soft-in 0.9s ease 0.5s both;
  }

  .js-motion .gate__phrase--sans {
    animation: gate-soft-in 0.9s ease 0.62s both;
  }

  .js-motion .gate__cta--left {
    animation: gate-soft-in 0.9s ease 0.78s both;
  }

  .js-motion .gate__cta--right {
    animation: gate-soft-in 0.9s ease 0.88s both;
  }
}

@keyframes gate-soft-in {
  from { opacity: 0; transform: translateY(var(--s-5)); }
  to { opacity: 1; transform: none; }
}

@keyframes char-in {
  from { opacity: 0; transform: translateY(0.42em) rotate(3deg); }
  to { opacity: 1; transform: none; }
}

/* --------------------------------------------------------------------------
   gate — mobile Anordnung (<= 1024px)
   Hochzeiten oben ueber der oberen Bildhaelfte, Unternehmen unten ueber der
   unteren, MOMENTE mittig; "die bleiben" linksbuendig darueber, "die verkaufen"
   rechtsbuendig darunter — beide an der Breite von MOMENTE ausgerichtet.
   Die Ortszeile steht ganz unten am Viewport.
   Umgesetzt ohne zweites Markup: die Wrapper werden per display:contents
   aufgeloest, die Reihenfolge kommt aus order.
   -------------------------------------------------------------------------- */

@media (max-width: 1024px) {
  .gate {
    justify-content: space-evenly;
    padding-inline: var(--gate-inline);
  }

  .gate__body,
  .gate__actions,
  .gate__phrases {
    display: contents;
  }

  .gate__cta--left { order: 1; }
  .gate__stack { order: 2; }
  .gate__cta--right { order: 3; }

  /* Der Stapel schrumpft auf die Breite von MOMENTE — daran haengt die
     Links-/Rechtsbuendigkeit der beiden Phrasen. */
  .gate__stack {
    display: flex;
    flex-direction: column;
    width: max-content;
    max-width: 100%;
  }

  .gate__phrase--script {
    order: 1;
    align-self: flex-start;
    margin-bottom: var(--gate-phrase-top);
    padding-top: var(--gate-phrase-top);
  }

  .gate__title {
    order: 2;
  }

  .gate__phrase--sans {
    order: 3;
    align-self: flex-end;
    margin-top: var(--gate-phrase-top);
  }

  /* Die Ortszeile entfaellt mobil ganz (Kundenwunsch 06.08.2026). Sie stand
     hier zuletzt als letztes Element im Fluss ueber der Rechtslink-Zeile
     (`order: 4`), nachdem diese am 03.08.2026 den unteren Rand uebernommen
     hatte; `order`, `margin` und `text-align` sind mit ihr entfallen.

     `display: none` und nicht `visually-hidden`: Die Zeile ist Beiwerk, kein
     Inhalt, den Screenreader statt der Augen bekommen sollen. Betriebsname und
     Ort stehen weiter in `<title>`, Description und JSON-LD — auf dem
     mobilen Bildschirm der Startseite stehen sie nach dieser Aenderung
     nirgends mehr (die Seite hat weder Kopfbalken mit Logo noch Fuss). */
  .gate__eyebrow {
    display: none;
  }

  /* Platz fuer die angepinnte Zeile freihalten, damit der Fluss nicht darunter
     laeuft: ihr Abstand zur Kante (`--s-22`, siehe unten beim `gate__legal`)
     plus Raum fuer zwei umgebrochene Zeilen. Das Bewertungssiegel steckt in den
     88 px schon drin — vorher stand es als eigener Summand daneben. */
  .gate {
    padding-bottom: calc(var(--s-22) + var(--s-16));
  }

}

/* ==========================================================================
   Version 02 · Inhaltsseiten — 1:1-Schicht
   Greift nur auf Seiten mit <html data-layout="v02">. Die noch nicht
   umgestellten Seiten laufen unveraendert auf dem Token-Enum weiter.
   Werte aus dem Block „8 · Inhaltsseiten" in tokens.css.
   ========================================================================== */

/* --- Ast-Chrome: Logo links, Pills, Zurueck-Link rechts --- */

/* Der Kopfbalken: weiss, ueber die ganze Breite, ohne Rundung, sticky im Fluss.
   Er schafft damit selbst den Weissraum fuer Logo und Menue-Button und schiebt
   den Auftakt darunter — auf jedem Viewport gleich (Kundenwunsch 31.07.2026:
   der mobile Look gilt jetzt auch auf dem Desktop und auf der Startseite).
   Die Rundungen und Zwickel der frueheren Platte sind entfallen: sie liessen
   sich nicht geraeteuebergreifend saubere loesen. */
/* Die Flaeche laeuft ueber die ganze Breite, der **Inhalt** steht auf der
   Rasterbreite: Gutter aussen, `max-width` innen — dieselbe Konstruktion wie
   `.site-footer` + `.site-footer__grid` und wie `main.px-gutter` + `.prose`.
   Nur so liegt die Logokante genau auf der Inhaltskante (Kundenhinweis
   03.08.2026: sonst steht die Navigation haltlos im Raum). */
.branch-chrome {
  position: sticky;
  top: 0;
  z-index: var(--z-nav-panel);
  padding-inline: var(--space-gutter);
  background: var(--color-mat);
  transition: transform 0.35s ease, box-shadow 0.3s ease;
}

/* Erst wenn etwas darunter weggescrollt ist, setzt sich der Balken mit einer
   Kante ab — im Ruhezustand soll er nicht schweben. Die Klasse setzt main.js
   im selben Durchlauf, der schon den Lesefortschritt schreibt. */
.is-scrolled .branch-chrome {
  box-shadow: var(--shadow-edge);
}

.branch-chrome__bar {
  display: flex;
  align-items: center;
  gap: var(--v2-chrome-gap);
  max-width: var(--container-wide);
  margin-inline: auto;
  padding-block: var(--v2-chrome-py);
  pointer-events: auto;
}

/* Die helle Platte sitzt buendig in der Bildschirmecke: oben und links sind
   die Kanten des Viewports, gerundet ist allein die Ecke, die in den Inhalt
   hineinragt. Dadurch wirkt sie, als waechse sie aus der Ecke heraus.
   `data-theme="light"` dreht die Rollen — Flaeche, Text und Akzent kommen
   damit ohne einen einzigen Ad-hoc-Wert aus. */
/* Die Flaeche liegt jetzt am Balken, nicht an der Platte — die ist nur noch die
   Huelle fuer Logo und Links. `data-theme="light"` bleibt, damit beide dunkel
   auf dem weissen Balken stehen. */
.branch-chrome__plate {
  display: flex;
  align-items: center;
  gap: var(--v2-chrome-gap);
}

.branch-chrome__logo {
  flex: none;
  display: flex;
  align-items: center;
  text-decoration: none;
}

.branch-chrome__logo img {
  display: block;
  width: auto;
  height: var(--v2-chrome-logo);
}

.pill {
  position: relative;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  gap: var(--space-inline);
  min-height: var(--s-11);
  padding: var(--space-tight) var(--space-comp);
  border: var(--border-hair) solid var(--color-control-line);
  border-radius: var(--radius-pill);
  background: linear-gradient(180deg, var(--color-glass-22), var(--color-glass-weak));
  box-shadow: inset 0 var(--border-hair) 0 var(--color-control-sheen),
    var(--glass-shadow);
  color: var(--color-text);
  font-size: var(--v2-pill-text);
  font-weight: var(--weight-extrabold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  transition: transform 0.4s var(--gate-ease-soft), box-shadow 0.4s ease,
    background 0.4s ease, border-color 0.3s ease;
}

/* Die glasigen Controls verschwimmen, was hinter ihnen liegt.
   Ohne Unterstuetzung bleibt der Verlauf allein — lesbar, nur weniger glasig.
   Nicht dabei ist der Menue-Button: er liegt seit dem 31.07.2026 auf einer
   deckenden Platte, hinter der es nichts zu verschwimmen gibt. */
@supports (backdrop-filter: blur(1px)) {
  .gate__cta,
  .pill,
  .tag,
  [data-layout="v02"] .duo__cta {
    backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
  }
}

.pill:hover,
.pill:focus-visible {
  transform: translateY(calc(var(--s-1) * -0.5));
  border-color: var(--color-glass-34);
  background: linear-gradient(180deg, var(--color-glass-34), var(--color-glass-12));
  color: var(--color-text);
}

.pill__sheen {
  position: absolute;
  top: 0;
  left: -70%;
  width: 48%;
  height: 100%;
  background: linear-gradient(105deg, var(--color-scrim-00), var(--color-veil-50), var(--color-scrim-00));
  transform: skewX(-18deg);
  pointer-events: none;
  transition: left 0.75s var(--gate-ease-soft);
}

.pill:hover .pill__sheen,
.pill:focus-visible .pill__sheen {
  left: 130%;
}

.pill__label { position: relative; }

/* --- Auftakt --- */

[data-layout="v02"] .hero {
  gap: var(--v2-stage-gap);
  padding: var(--v2-stage-pt) 0 var(--v2-stage-pb);
}

[data-layout="v02"] .eyebrow {
  margin-bottom: var(--v2-eyebrow-mb);
  font-size: var(--v2-eyebrow);
}

[data-layout="v02"] .hero__body {
  padding-inline: var(--v2-inline);
}

/* Die Parole ist **ein Wort** und darf nie umbrechen (Kundenwunsch
   06.08.2026). Zwei Wege dorthin waren offen und beide sind noetig: JS zerlegt
   sie in `.char`-Kaesten, zwischen denen der Browser umbrechen darf — bei
   320px riss er das letzte E ab; und ohne JS greift `overflow-wrap:
   break-word` aus der Basis (Kap. 7) auf den Textknoten. `nowrap` schliesst
   beides aus. Damit sie dabei nicht ueber die Spalte laeuft, ist die
   Untergrenze von `--v2-h1` mitgezogen.

   Ausgenommen sind die Unterseiten (`--long`): dort steht ein ganzer Satz, der
   umbrechen muss. */
[data-layout="v02"] .hero__title {
  font-size: var(--v2-h1);
  line-height: var(--leading-display);
}

[data-layout="v02"] .hero__title:not(.hero__title--long) {
  white-space: nowrap;
}

/* Die Phrase darunter erbt das `nowrap` sonst mit — sie ist zweiwortig
   („die bleiben", „die verkaufen") und muss auf schmalen Geraeten umbrechen
   duerfen. Sie steht ohnehin in einer eigenen Zeile (`display: block`). */
[data-layout="v02"] .hero__title-sub {
  white-space: normal;
}

[data-layout="v02"] .hero__title-sub--script {
  font-size: var(--v2-phrase);
  line-height: var(--leading-duo);
  letter-spacing: -0.02em;
}

[data-layout="v02"] .hero__lead {
  margin-top: var(--v2-lead-mt);
  font-size: var(--v2-lead);
  line-height: var(--leading-copy);
}

[data-layout="v02"] .hero__hint {
  gap: var(--v2-hint-gap);
}

[data-layout="v02"] .hero__hint-line {
  height: var(--v2-hint-h);
}

/* --- Haltungsaussage --- */

[data-layout="v02"] .statement {
  padding: var(--v2-stmt-py) var(--v2-inline);
}

[data-layout="v02"] .statement > .container > .eyebrow {
  margin-bottom: var(--v2-stmt-eyebrow-mb);
}

[data-layout="v02"] .statement__claim {
  font-size: var(--v2-words);
  line-height: var(--leading-words);
  letter-spacing: -0.035em;
}

[data-layout="v02"] .statement__aside {
  margin-top: var(--v2-aside-mt);
  max-width: 56ch;
}

[data-layout="v02"] .script-line {
  font-size: var(--v2-script);
  line-height: var(--leading-script);
}

[data-layout="v02"] .statement__aside p:last-child {
  font-size: var(--v2-aside-text);
  line-height: var(--leading-copy);
}

/* Wort-Aufhellung: JS setzt --w je Wort, ohne JS bleibt alles deckend */
.js-motion .words__w {
  opacity: var(--w, 1);
  transition: opacity 0.18s linear;
}

/* --- Kreis-Aufblende --- */

[data-layout="v02"] .aperture__stage {
  padding: 0;
}

[data-layout="v02"] .aperture__body {
  padding-inline: var(--v2-ap-px);
  max-width: 1200px;
}

[data-layout="v02"] .aperture__title {
  font-size: var(--v2-ap-title);
  line-height: var(--leading-aperture);
  letter-spacing: -0.04em;
}

/* Schwarz statt der Themenfarbe, auf jedem Viewport (Kundenwunsch 06.08.2026:
   „wie auf /unternehmen/leistungen"). Vorher war die Ueberschrift erst nach dem
   Scrollen sichtbar.

   Der Grund: Die Section ist auf der v02-Schicht durchsichtig, es steht also das
   helle Passepartout dahinter — solange die Kreis-Aufblende das Bild noch nicht
   freigegeben hat. Auf einer dunkel gethemten Seite ist die Ueberschrift dort
   bone-050, also Weiss auf Weiss. `--color-on-mat` haelt beide Zustaende (die
   Rechnung steht in tokens.css).

   **Seit dem 07.08.2026 trifft `:not([data-theme])` keine Stelle mehr.** Alle
   drei `aperture`-Abschnitte tragen jetzt `data-theme="light"` — auf
   `/unternehmen` auf Kundenwunsch („heller Overlay, wie bei Produktionen"),
   und damit ist die Ueberschrift dort ohnehin ink-800.

   Die Regel bleibt als **Schutz** stehen: Ein `aperture` ohne eigenes Theme auf
   einer dunkel gethemten v02-Seite haette wieder Weiss auf Weiss, solange die
   Aufblende das Bild nicht freigegeben hat. Sie kostet nichts und faengt genau
   diesen Fall.

   Was der Themenwechsel gekostet hat, steht in CLAUDE.md Kap. 19: Auf dem
   Passepartout gewinnen Kapitelzeile und Fliesstext (1,48 auf 5,57 und 1,89 auf
   7,04 : 1), ueber dem freigelegten Bild verlieren sie (8,18 auf rund 1,5 und
   6,39 auf rund 1,9 : 1). Das Motiv ist mit einem gemessenen Mittelwert von
   0,237 dunkel, ein heller Schleier hellt es nicht weit genug auf. */
[data-layout="v02"] .aperture:not([data-theme]) .aperture__title {
  color: var(--color-on-mat);
}

[data-layout="v02"] .tag-list {
  gap: var(--v2-tag-gap);
  margin-top: var(--v2-tag-mt);
  max-width: 62ch;
}

[data-layout="v02"] .tag {
  padding: var(--space-inline) var(--s-5);
  font-size: var(--v2-tag-text);
  border-color: var(--color-glass-34);
}

[data-layout="v02"] .aperture__lead {
  margin-top: var(--v2-ap-lead-mt);
  font-size: var(--v2-ap-lead);
  line-height: var(--leading-copy);
}

/* --- Zwei Wege --- */

[data-layout="v02"] .duo {
  gap: var(--v2-duo-gap);
  padding: var(--v2-duo-pt) var(--v2-inline) var(--v2-duo-pb);
}

[data-layout="v02"] .duo__body > .eyebrow {
  margin-bottom: var(--v2-duo-eyebrow-mb);
}

/* Zeilenhoehe **nicht** `--leading-duo` (0.9) wie im Entwurf: bei 0.9 stehen die
   Zeilen enger als der Schriftkegel hoch ist, und die Umlautpunkte einer Zeile
   beruehren die Zeile darueber („koennen" in „Wofuer Sie Ihren Film einsetzen
   koennen"). Der Entwurf kam damit durch, weil seine Headline „Aus einer Hand"
   keine Oberzeichen hat. `--leading-tight` (1.12) laesst Punkten und
   Unterlaengen Luft; `--leading-duo` bleibt fuer die einzeiligen Phrasen
   (`.gate__phrase--sans`, `.hero__title-sub--script`), wo Zeilen nie
   aufeinandertreffen. */
[data-layout="v02"] .duo__title {
  font-size: var(--v2-duo-title);
  line-height: var(--leading-tight);
  letter-spacing: -0.04em;
}

[data-layout="v02"] .duo__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--v2-row-gap);
  margin-top: var(--v2-duo-row-mt);
}

[data-layout="v02"] .duo__cta {
  position: relative;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  gap: var(--v2-cta-gap);
  padding: var(--v2-cta-py) var(--v2-cta-px);
  border: var(--border-hair) solid var(--color-glass-34);
  border-radius: var(--radius-pill);
  font-size: var(--v2-cta-text);
  font-weight: var(--weight-extrabold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--color-text);
  text-decoration: none;
  background: none;
  transition: color 0.4s ease, border-color 0.4s ease,
    transform 0.5s var(--gate-ease-soft), box-shadow 0.5s ease,
    filter 0.45s ease;
}

[data-layout="v02"] .duo__cta-fill {
  position: absolute;
  inset: 0;
  background: var(--color-brand);
  transform: scaleX(0);
  transition: transform 0.55s var(--gate-ease-soft);
}

[data-layout="v02"] .duo__cta--left .duo__cta-fill { transform-origin: left center; }
[data-layout="v02"] .duo__cta--right .duo__cta-fill { transform-origin: right center; }

[data-layout="v02"] .duo__cta > span:not(.duo__cta-fill) { position: relative; }

[data-layout="v02"] .duo__cta:hover,
[data-layout="v02"] .duo__cta:focus-visible {
  border-color: transparent;
  color: var(--color-text-invert);
  transform: translateY(calc(var(--s-1) * -1));
  box-shadow: var(--glass-shadow-lift);
}

[data-layout="v02"] .duo__cta:hover .duo__cta-fill,
[data-layout="v02"] .duo__cta:focus-visible .duo__cta-fill { transform: scaleX(1); }

[data-layout="v02"] .duo__cta:hover .duo__arrow,
[data-layout="v02"] .duo__cta:focus-visible .duo__arrow { transform: translateX(var(--s-2)); }

[data-layout="v02"] .duo.is-left .duo__cta--left:not(:hover):not(:focus-visible),
[data-layout="v02"] .duo.is-right .duo__cta--right:not(:hover):not(:focus-visible) {
  border-color: var(--color-brand);
  color: var(--color-brand);
}

[data-layout="v02"] .duo.is-left .duo__cta--right:not(:hover):not(:focus-visible),
[data-layout="v02"] .duo.is-right .duo__cta--left:not(:hover):not(:focus-visible) {
  filter: opacity(0.4);
}

/* Drei Textvarianten uebereinander, nur eine sichtbar */
[data-layout="v02"] .duo__copy {
  position: relative;
  min-height: var(--v2-duo-copy-min);
  max-width: 60ch;
  margin: var(--v2-duo-copy-mt) auto 0;
}

[data-layout="v02"] .duo__copy p {
  position: absolute;
  inset: 0;
  margin: 0;
  font-size: var(--v2-ap-lead);
  line-height: var(--leading-copy);
  color: var(--color-text-muted);
  opacity: 0;
  transition: opacity 0.5s ease;
}

[data-layout="v02"] .duo__copy p[data-copy="both"] { opacity: 1; }
[data-layout="v02"] .duo.is-left .duo__copy p[data-copy="both"],
[data-layout="v02"] .duo.is-right .duo__copy p[data-copy="both"] { opacity: 0; }
[data-layout="v02"] .duo.is-left .duo__copy p[data-copy="left"] { opacity: 1; }
[data-layout="v02"] .duo.is-right .duo__copy p[data-copy="right"] { opacity: 1; }

/* --- Ablauf --- */

[data-layout="v02"] .steps__stage {
  gap: var(--v2-steps-colgap);
  padding: var(--v2-steps-pt) var(--v2-inline) var(--v2-steps-pb);
}

[data-layout="v02"] .steps__list {
  gap: var(--v2-steps-listgap);
  margin-top: var(--v2-steps-stack);
}

[data-layout="v02"] .steps__item {
  grid-template-columns: var(--v2-step-numcol) 1fr;
  gap: var(--v2-step-gap);
}

[data-layout="v02"] .steps__title {
  font-size: var(--v2-step-title);
  line-height: var(--leading-step);
}

[data-layout="v02"] .steps__text {
  font-size: var(--v2-step-text);
  max-width: 38ch;
}

[data-layout="v02"] .steps__frames {
  height: var(--v2-frames-h);
}

[data-layout="v02"] .steps__progress {
  margin-top: var(--v2-steps-stack);
}

/* --- Abschluss --- */

[data-layout="v02"] .split-cta__body {
  gap: var(--v2-split-gap);
  padding-inline: var(--v2-ap-px);
}

[data-layout="v02"] .split-cta__body > .script-line {
  font-size: var(--v2-split-script);
}

[data-layout="v02"] .split-cta__title {
  font-size: var(--v2-split-title);
  line-height: var(--leading-split);
}

[data-layout="v02"] .split-cta__lead {
  font-size: var(--v2-split-lead);
  line-height: var(--leading-copy);
  max-width: 46ch;
}

[data-layout="v02"] .split-cta__media {
  display: block;
}

[data-layout="v02"] .split-cta__half {
  position: absolute;
  top: 0;
  width: 50.5%;
  height: 100%;
}

[data-layout="v02"] .split-cta__half--left { left: 0; }
[data-layout="v02"] .split-cta__half--right { right: 0; }

/* --- Abschnittsschiene mit Timecode --- */

[data-layout="v02"] .chapter-rail {
  left: var(--v2-rail-left);
}

.chapter-rail__timecode {
  display: flex;
  align-items: center;
  gap: var(--space-inline);
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  letter-spacing: 0.22em;
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
}

.chapter-rail__rec {
  display: block;
  width: var(--s-2);
  height: var(--s-2);
  border-radius: var(--radius-pill);
  background: var(--color-danger);
}

/* Tilt des Statement-Asides — JS setzt --tilt-e (0..1), ohne JS bleibt es flach */
.js-motion [data-tilt] {
  transform: perspective(var(--v2-tilt-perspective))
    rotateX(calc(var(--v2-tilt-deg) * (1 - var(--tilt-e, 1))))
    translateY(calc(var(--v2-tilt-shift) * (1 - var(--tilt-e, 1))));
  transform-origin: 50% 100%;
  opacity: clamp(0, calc(var(--tilt-e, 1) * 1.4), 1);
}

/* --------------------------------------------------------------------------
   Auftakt der Reihe „Version 02": Headline loest sich beim Scrollen auf
   JS setzt --char-p (0..1) an der Headline und --stag je Zeichen. Ohne JS
   bleibt die Headline unveraendert stehen — der Text ist ein Textknoten,
   die Zeichen-Spans kommen erst per Skript.
   -------------------------------------------------------------------------- */

.char {
  display: inline-block;
}

.js-motion [data-chars] {
  letter-spacing: calc(
    var(--char-track-base, var(--tracking-display)) +
      var(--char-p, 0) * var(--v2-char-track)
  );
}

.js-motion .char {
  transform: translateY(calc(
      -1 * var(--char-p, 0) * var(--v2-char-lift) * var(--stag, 1) * var(--v2-fx)))
    scale(calc(1 - var(--char-p, 0) * var(--v2-char-scale) * var(--v2-fx)));
  opacity: clamp(0, calc(1 - var(--char-p, 0) * 1.25), 1);
  will-change: transform, opacity;
}

@media (prefers-reduced-motion: no-preference) {
  .js-motion .char__in {
    display: inline-block;
    animation: char-in 0.95s var(--gate-ease-char) both;
    animation-delay: calc(0.06s + var(--i, 0) * 0.055s);
  }
}

/* Schreibschrift-Phrase im Auftakt, wie im Entwurf */
[data-layout="v02"] .hero__title-sub--script {
  margin-top: var(--v2-phrase-top);
  font-family: var(--font-script);
  font-size: var(--v2-hero-script);
  font-weight: var(--weight-regular);
  line-height: var(--leading-script);
  letter-spacing: var(--tracking-normal);
}

/* --------------------------------------------------------------------------
   Passepartout: Panels auf heller Flaeche
   Kundenwunsch vom 31.07.2026. Bis dahin liefen alle Sections randlos bis an
   die Viewport-Kanten. Jetzt liegt zwischen ihnen und um sie herum die Flaeche
   `--color-mat`, und jede Section ist ein Panel mit runden Ecken.

   Der Abstand kommt aus der Polsterung von `main` plus einem `margin-top`
   zwischen Geschwistern — nicht aus `margin` an jeder Section, weil
   angrenzende Blockraender kollabieren und der Abstand dann halb so gross
   waere.

   Geklippt wird an der Section. Bei den drei angepinnten Sections
   (`aperture`, `steps`, `split-cta`) **muss** das `clip` sein und nicht
   `hidden`: `hidden` macht die Section zum Scrollport ihrer Buehne und
   `position: sticky` waere wieder wirkungslos. `clip` klippt, ohne ein
   Scrollport zu sein.
   -------------------------------------------------------------------------- */

/* **Oben ohne Passepartout** (Kundenwunsch 06.08.2026: der Abstand ueber dem
   Auftakt war hoeher als der Kopfbalken). Balken und Passepartout sind
   dieselbe Farbe — der Streifen ueber dem ersten Panel las sich deshalb nicht
   als Rahmen, sondern als zu hohe Navigation. Ohne die obere Polsterung ist
   der Weissraum ueber dem Auftakt **genau `--v2-chrome-h`**, die Hoehe des
   Balkens. Links, rechts und unten bleibt der Rahmen; die Rundung der oberen
   Panel-Ecken bleibt sichtbar, weil in den Ecken die Mattflaeche steht. */
[data-layout="v02"] main#inhalt {
  padding: 0 var(--v2-mat-gap) var(--v2-mat-gap);
  background: var(--color-mat);
}

[data-layout="v02"] main#inhalt > section {
  border-radius: var(--v2-panel-radius);
}

[data-layout="v02"] main#inhalt > section + section {
  margin-top: var(--v2-mat-gap);
}

/* ohne angepinnte Buehne — hier ist `hidden` unbedenklich */
[data-layout="v02"] .hero,
[data-layout="v02"] .statement,
[data-layout="v02"] .duo {
  overflow: hidden;
}

/* mit angepinnter Buehne — nur `clip`, sonst bricht sticky */
[data-layout="v02"] .aperture,
[data-layout="v02"] .steps {
  overflow: visible;
  overflow: clip;
}

/* Ein Panel darf nur so hoch werden, dass sein unteres Ende im Bild bleibt. Was
   abgeht, haengt davon ab, ob der Kopfbalken dabei steht — der Auftakt sieht ihn,
   alles darunter nicht mehr. */
[data-layout="v02"] .hero {
  min-height: calc(100vh - var(--v2-hero-inset));
  min-height: calc(100svh - var(--v2-hero-inset));
}

/* Mobil zusaetzlich eine feste Untergrenze (Kundenwunsch 06.08.2026):
   `--v2-hero-min` 600px. Auf kurzen Geraeten blieb vom Bildschirm nach Abzug
   des Kopfbalkens zu wenig fuer Parole, Vorspann und Scroll-Hinweis.

   `max()` und nicht der blosse Wert: Sonst waere der Auftakt auf einem hohen
   Telefon *kuerzer* als der Bildschirm und nicht mehr bildschirmfuellend —
   das war nicht verlangt. Wer ihn dort tatsaechlich auf 600px begrenzen will,
   ersetzt die beiden Zeilen durch `min-height: var(--v2-hero-min)`.

   Die Regel muss **nach** der Basisregel stehen: gleiche Spezifitaet (0,2,0),
   und eine Media Query aendert daran nichts. */
@media (max-width: 1024px) {
  [data-layout="v02"] .hero {
    min-height: max(var(--v2-hero-min), calc(100vh - var(--v2-hero-inset)));
    min-height: max(var(--v2-hero-min), calc(100svh - var(--v2-hero-inset)));
  }
}

/* Alles darunter erreicht man durch Herunterscrollen — dabei ist der Balken
   weggefahren, es geht nur das Passepartout ab. */
[data-layout="v02"] .duo,
[data-layout="v02"] .split-cta {
  min-height: calc(100vh - var(--v2-panel-inset));
  min-height: calc(100svh - var(--v2-panel-inset));
}

/* Abschnitt 03 · das Panel ist die Buehne, nicht die Section.
   Die Section ist 240vh hoch; ihre runden Ecken liegen damit weit ausserhalb
   des Viewports, solange die Buehne angepinnt steht — das Bild haette dort
   eckige Kanten. Also traegt die Buehne die Rundung. Moeglich ist das hier,
   weil `.aperture__stage` randlos ist und `overflow: hidden` schon traegt —
   anders als `.steps__stage` (zentrierter Container).

   Section und Buehne bleiben beide durchsichtig: In Abschnitt 03 steht ueberall
   das Passepartout durch — auch dort, wo die Kreis-Aufblende das Bild noch
   nicht freigegeben hat, und im Rahmen, den das zurueckfahrende Bild am Ende
   laesst (Kundenwunsch vom 31.07.2026; der Entwurf hat dort ink-900).

   Deshalb traegt die Section `data-theme="light"`: heller Text auf hellem Grund
   waere unlesbar. Der Wechsel behebt zugleich einen Kontrastfehler — das Bild
   ist mit einem Mittelwert von 0,73 hell, und heller Text lag an der
   schwaechsten Scrim-Stelle bei 3,37 : 1. Jetzt sind es mindestens 11,51 : 1. */
[data-layout="v02"] .aperture {
  background: transparent;
}

[data-layout="v02"] .aperture__stage {
  border-radius: var(--v2-panel-radius);
}

/* Das Bild traegt die Rundung selbst. Es faehrt von Skala 1.34 auf 0.86, ist
   also erst groesser und dann kleiner als die Buehne — und sobald es kleiner
   ist (ab pin 0.71), fasst die Rundung der Buehne es nicht mehr, die Ecken
   waeren wieder eckig. Beide Rundungen sind also noetig: die der Buehne fuer
   die Phase, in der das Bild ueber den Kasten hinausragt, die des Bildes fuer
   den Rahmen am Ende. Mitskaliert wird sie vom `transform` — die Rundung waechst
   und schrumpft mit dem Bild, was dem Zoom entspricht. */
[data-layout="v02"] .aperture__media picture {
  display: block;
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: var(--v2-panel-radius);
}

/* Die Unschaerfe sitzt am Bild, Rundung und Zoom am `picture`. Waere der Blur am
   selben Kasten wie die Rundung, wuerde er die Ecken mitverwischen; so klippt
   das `picture` das unschaerfe Ergebnis mit scharfer Kante. Der Ueberscan haelt
   den ausblendenden Rand des Blurs (er mischt sonst mit Transparenz und legt
   einen hellen Saum ins Bild) ausserhalb des Klipp-Kastens. */
[data-layout="v02"] .aperture__media img {
  transform: scale(var(--v2-ap-overscan));
  filter: blur(
    calc(var(--v2-ap-blur) + var(--aperture-p, 0) * var(--v2-ap-blur-scroll))
  );
}

/* Abschnitt 05 · die Rundung sitzt am Bildstapel, nicht an den Einzelbildern.
   Die vier Frames liegen `inset: 0` und werden nie kleiner als ihr Kasten — die
   Kreuzblende setzt nur `opacity` (der Entwurf skaliert zwischen 1.1 und 1.0,
   also auch nie unter 1). Damit fasst die Rundung des Kastens sie immer, anders
   als in Abschnitt 03, wo das Bild auf 0.86 zurueckfaehrt und deshalb eine
   eigene braucht. `overflow: hidden` traegt `.steps__frames` schon. */
[data-layout="v02"] .steps__frames {
  border-radius: var(--v2-panel-radius);
}

/* --------------------------------------------------------------------------
   Der Footer ist seit dem 31.07.2026 schwarz auf weiss (Kundenwunsch): kein
   dunkler Grund, kein abgesetzter Rahmen, keine Rundung, keine Schlusszeile.
   Er traegt dafuer `data-theme="light"` im Wiederholblock — damit kommen Text,
   Spaltentitel und Links ueber die Rollen (17,20 : 1 / 5,57 : 1 / 7,04 : 1).

   Die Flaechen-Deklaration bleibt **bewusst stehen** und loest im hellen Theme
   zu bone-050 auf. Ein wirklich durchsichtiger Footer waere auf den vier dunkel
   gethemten Seiten (`/unternehmen/*`) dunkler Text auf dunklem Grund.
   -------------------------------------------------------------------------- */

[data-layout="v02"] body {
  background: var(--color-mat);
}

/* Die Laufweite der Zeichen-Ueberschriften waechst beim Zerfall. Die Regel
   braucht den Ausgangswert als Rolle, weil `calc()` nicht additiv auf eine
   bestehende Deklaration aufsetzen kann. */
[data-layout="v02"] .hero__title { --char-track-base: var(--tracking-display); }
[data-layout="v02"] .duo__title { --char-track-base: -0.04em; }

/* --------------------------------------------------------------------------
   Chrome mobil: Burger und Drill-Down
   Es gibt weiterhin nur EIN Navigations-Markup (Kap. 5): der Baustein
   `nav.html` steckt im geteilten Kopf und in der Ast-Chrome. Der Burger sitzt
   im Tab — dort, wo auf dem Desktop der Zurueck-Link steht.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   Hauptmenue: ein Button, ein Panel, alle Seiten
   Seit dem 31.07.2026 gibt es nur noch die Navigation der Vorschaltseite —
   Kreis-Aufblende aus dem Button, zwei Spalten, Rechtslinks. Der Drill-Down des
   geteilten Kopfes ist ersatzlos entfallen (Kundenwunsch).

   Der Button sitzt in einem fixierten weissen Tab in der rechten obereren Ecke,
   in derselben Machart wie auf `/hochzeiten`: aussen buendig, innen gerundet.
   -------------------------------------------------------------------------- */

/* Der Tab traegt keine eigene Flaeche — der weisse Kopfbalken darunter liefert
   sie. Er ist damit nur noch Position und Polsterung; sichtbar ist allein der
   Burger. `data-theme="light"` am Tab gibt die dunklen Balken (ink-800 auf
   bone-050, 17,20 : 1). */
/* Der Burger sitzt auf der **rechten Rasterkante**, nicht am Viewportrand. Der
   Tab spannt dafuer ueber die ganze Breite und begrenzt sich auf Rasterbreite
   plus die beiden Gutter — innen bleibt damit genau die Inhaltsbreite stehen,
   und `justify-content: flex-end` legt den Knopf auf deren rechte Kante.

   Ein Wrapper waere die Alternative; der Tab hat aber nur dieses eine Kind, und
   der Baustein liegt in jeder Seite — ein Element weniger ist hier mehr wert.

   `pointer-events` ist Pflicht, seit der Kasten breit ist: sonst faengt er die
   Klicks auf das Logo und auf alles darunter ab. */
.mainmenu__tab {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: var(--z-nav-button);
  display: flex;
  align-items: center;
  justify-content: flex-end;
  max-width: calc(var(--container-wide) + 2 * var(--space-gutter));
  margin-inline: auto;
  padding: var(--v2-chrome-py) var(--space-gutter);
  background: transparent;
  pointer-events: none;
}

/* Der Button traegt keine eigene Flaeche mehr — die Platte darunter liefert den
   Kontrast, `data-theme="light"` am Tab die Rollen (ink-800, 17,20 : 1). */

/* Solange das Off-Canvas-Menue offen ist, scrollt die Seite darunter nicht mit.
   `overflow: hidden` am Wurzelelement, gesetzt von setUpMainMenu(). Das Panel
   selbst bleibt scrollbar (`.js .mainmenu { overflow-y: auto }`), damit lange
   Menues auf kleinen Viewports erreichbar bleiben.

   **Nur am Wurzelelement, nie zusaetzlich am `body`** (Kundenhinweis
   06.08.2026: der Inhalt sprang beim Oeffnen). Der Wert des Wurzelelements
   wird auf den Viewport uebertragen und sperrt damit das Scrollen; `html`
   selbst bleibt dabei `visible`. Ein zweites `overflow: hidden` am `body`
   macht den `body` dagegen zu einem echten Scroll-Container — und `position:
   sticky` bezieht sich immer auf den naechsten scrollenden Vorfahren. Alle
   fuenf sticky-Elemente (Kopfbalken, `aperture__stage`, `steps__stage`,
   `gallery-x__stage`, `split-cta__stage`) verloren deshalb beim Oeffnen ihren
   Halt und sprangen an ihre Position im Fluss, beim Schliessen zurueck. */
.is-menu-open {
  overflow: hidden;
}

/* --------------------------------------------------------------------------
   Angepinnte Szenen mobil (Kundenwunsch 31.07.2026)
   Bis 1024 px steht der Chrome-Balken sticky im Fluss und belegt die oberen
   `--v2-chrome-h`. Eine Buehne, die bei `top: 0` klebt, laege dahinter — sie
   setzt deshalb darunter an und wird um denselben Betrag kuerzer.
   -------------------------------------------------------------------------- */

@media (max-width: 1024px) {
  /* Die Unschaerfe ist der teuerste Teil der Aufblende. Mobil laeuft sie auf
     halbem Weg — sichtbar, aber ohne die Bildrate zu kosten. */
  .js-motion .aperture__media img {
    filter: blur(
      calc(var(--v2-ap-blur) + var(--aperture-p, 0) * var(--v2-ap-blur-scroll) / 2)
    );
  }
}

/* Die Vorschaltseite hat seit dem 31.07.2026 **keine Navigation** — kein Balken,
   kein Logo, kein Menue-Button, kein Panel, kein Lesefortschritt (sie scrollt
   ohnehin nicht). Der Weg fuehrt allein ueber die beiden Wege-Pills.

   Weil damit alle Rechtslinks weggefallen waeren, die zuvor in der Menue-Spalte
   standen, traegt die Seite sie jetzt selbst: `gate__legal` unter den Pills.
   Kap. 12 verlangt den CMP-Link auf **jeder** Seite, und Impressum wie
   Datenschutz muessen von jeder Seite erreichbar bleiben. */
/* Die Zeile steht **am unteren Bildschirmrand**, nicht unter den Pills
   (Kundenwunsch 03.08.2026) — mit `--gate-pad-bottom` Abstand zur Kante, dem
   gleichen Wert, den auch die Ortszeile mobil nutzt.

   Sie ist dafuer direktes Kind von `.gate` und nicht mehr von `.gate__body`:
   `position: absolute` bezieht sich auf den naechsten positionierten Vorfahren,
   und `.gate__body` traegt `position: relative` (es muss ueber dem absolut
   liegenden Bildstapel stehen). In `.gate__body` haette die Zeile also an dessen
   Unterkante geklebt, nicht an der des Bildschirms. */
.gate__legal {
  position: absolute;
  left: var(--gate-inline);
  right: var(--gate-inline);
  bottom: var(--gate-pad-bottom);
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  /* Zeilen halb so weit auseinander wie die Links nebeneinander
     (Kundenwunsch 06.08.2026) — erst `row-gap`, dann `column-gap`. */
  gap: var(--gate-menu-bottom-row) var(--gate-menu-bottom-gap);
  margin: 0;
  font-size: var(--gate-menu-meta);
}

.gate__legal a {
  color: var(--color-text-muted);
  text-decoration: none;
  transition: color 0.3s ease;
}

.gate__legal a:hover,
.gate__legal a:focus-visible {
  color: var(--color-text);
}

/* Mobil rueckt die Zeile um die Siegelhoehe nach oben (Kundenwunsch 03.08.2026):
   das Bewertungs-Widget liegt fix am unteren Rand und verdeckte sie sonst.
   Scrollen ist auf dieser Seite kein Ausweg — `.gate--js` sperrt sie per
   `overflow: hidden` bewusst auf einen Bildschirm.

   **Diese Regel muss hinter der Basisregel stehen.** Sie hat dieselbe
   Spezifitaet (0,1,0), und eine Media Query aendert daran nichts — allein die
   Reihenfolge entscheidet. Im mobilen Sammelblock weiter oben (dort stehen die
   uebrigen `gate`-Ruecknahmen) war sie wirkungslos, weil `.gate__legal` selbst
   erst hier definiert wird. */
@media (max-width: 1024px) {
  /* **88 px** zur Kante (Kundenwunsch 06.08.2026: „ca. 90 px"). Vorher war es
     `--gate-pad-bottom` + `--s-25`, also 124 bis 154 px — die Zeile stand zu
     hoch im Bild. Der feste Wert ist ein 4px-Vielfaches (`--s-22`) und haelt
     das 70 px hohe Bewertungssiegel weiter frei, jetzt mit 18 px Luft statt
     mit 54. */
  .gate__legal {
    bottom: var(--s-22);
  }
}

/* Derselbe Scroll-Zustand am geteilten Kopf der 13 uebrigen Seiten — er ist
   ohnehin glasig, es fehlte nur die Kante. */
.site-header {
  transition: transform 0.35s ease, box-shadow 0.3s ease;
}

.is-scrolled .site-header {
  box-shadow: var(--shadow-edge);
}

/* --------------------------------------------------------------------------
   Kopf aus- und einblenden (Kundenwunsch 31.07.2026)
   Beim Herunterscrollen faehrt der Kopf nach oben aus dem Bild, beim
   Hochscrollen wieder herein — wie auf Mobilgeraeten gewohnt. Der Menue-Button
   faehrt mit, sonst bliebe er allein stehen.

   `main.js` setzt `is-nav-hidden` im selben Durchlauf, der schon den
   Lesefortschritt schreibt, und laesst den Kopf in vier Faellen stehen: in den
   oberen 160 px, bei offenem Menue, wenn der Fokus im Kopf steht, und bei
   `prefers-reduced-motion: reduce`. Der Lesefortschritt bleibt sichtbar — er ist
   eine Kante am Bildschirmrand, kein Bedienelement.
   -------------------------------------------------------------------------- */

.mainmenu__tab {
  transition: transform 0.35s ease;
}

.is-nav-hidden .branch-chrome,
.is-nav-hidden .site-header,
.is-nav-hidden .mainmenu__tab {
  transform: translateY(-100%);
}

/* --------------------------------------------------------------------------
   Panel-Besitz beim Ablauf und beim Abschluss
   Beide Sections sind im inszenierten Zustand mehrere Bildschirme hoch (320vh
   bzw. 240vh). Solange **sie** die Flaeche tragen, faellt ihr Grund im Viewport
   oben und unten bis an die Kante — der weisse Rand ist dann nicht umlaufend,
   und beim Abschluss standen die Bildhaelften seit der Hoehenkorrektur in einem
   dunklen Feld statt bildschirmfuellend.

   Also traegt hier wie bei `aperture` die **Buehne** Flaeche und Rundung, und die
   Section wird durchsichtig. Beim Ablauf muss die Buehne dafuer randlos werden:
   Ihre Begrenzung auf `--container-wide` wanderte in die Polsterung, die die
   ueberschuessige Breite aussen aufteilt — der Inhalt bleibt zentriert, der
   Kasten wird voll breit.
   -------------------------------------------------------------------------- */

@media (min-height: 600px) {
  .js-motion[data-layout="v02"] .steps,
  .js-motion[data-layout="v02"] .split-cta {
    background: transparent;
  }

  .js-motion[data-layout="v02"] .steps__stage,
  .js-motion[data-layout="v02"] .split-cta__stage {
    border-radius: var(--v2-panel-radius);
    background: var(--color-surface);
    overflow: hidden;
  }

  .js-motion[data-layout="v02"] .steps__stage {
    max-width: none;
    margin-inline: 0;
    padding-inline: var(--v2-steps-px);
  }
}

/* --------------------------------------------------------------------------
   Ablauf mobil: nur die vier Punkte untereinander (Kundenwunsch 07.08.2026)

   Bis 1024 px faellt der Bildstapel weg und mit ihm die ganze Szene — die
   Punkte stehen einfach untereinander, alle gleich deckend.

   Der Ablauf war ohnehin die einzige Szene, die mobil **nicht** angepinnt wird
   (die Bedingung darueber ist `min-width: 1025px` und `min-height: 720px`).
   Gemalt wurde sie trotzdem: `main.js` haengt nur an `min-height: 600px` und
   schrieb deshalb auch mobil `--steps-p`, die Kreuzblende der Bilder und die
   Deckkraft der Punkte (0,35 fuer die inaktiven). Ohne Pinning lief das am
   normalen Seitenscroll mit — Punkte, die beim Scrollen auf- und abdunkeln,
   ohne dass etwas daran haengt. Die JS-Seite ist mitgezogen: `stepsScene`
   spiegelt jetzt genau die Media Query der Buehne. **Beide muessen zusammen
   geaendert werden.**

   Die Fortschrittszeile geht mit. Sie ist die Anzeige dieser Szene: der Balken
   liest `--steps-p`, der Zaehler wird von `main.js` gestellt. Ohne die Szene
   stuende dort dauerhaft „01 / 04" und ein Balken auf einem Viertel — eine
   Fortschrittsanzeige, die keinen Fortschritt mehr anzeigt.

   Abstaende: oben und unten standen 101 px und 59 px gegen 19,5 px seitlich
   (bei 390 x 844). Jetzt ist die Polsterung senkrecht symmetrisch und genau
   **doppelt so gross wie die seitliche** — 39 px gegen 19,5 px bei 390 px,
   und die Relation bleibt ueber den ganzen mobilen Bereich stehen, weil beide
   Werte an `--v2-inline` haengen.

   Die seitliche Polsterung bleibt bewusst `--v2-inline`: sie ist auf allen
   Panels dieselbe, und eine eigene Kante nur fuer den Ablauf haette den
   Textanfang gegen die Nachbarabschnitte verschoben. Die senkrechte ist damit
   die einzige Stellschraube.
   -------------------------------------------------------------------------- */

@media (max-width: 1024px) {
  .steps__frames,
  .steps__progress {
    display: none;
  }

  [data-layout="v02"] .steps__stage {
    padding-block: calc(var(--v2-inline) * 2);
  }
}

/* --------------------------------------------------------------------------
   Abschluss mobil: geklippt wird der Bildstapel, nicht die Textsaeule

   Gemeldet am 03.08.2026: der untere Knopf war auf `/unternehmen/imagevideos`
   angeschnitten. Der erste Versuch nahm mobil das Pinning heraus — damit war
   der Effekt weg (gemeldet 05.08.2026). **Die Szene bleibt also mobil an**, und
   der Anschnitt wird an seiner Ursache behoben.

   Die Ursache ist das `overflow: hidden` der Buehne. Es ist noetig, damit die
   beiden Bildhaelften beim Ausfahren nicht ueber das Panel hinauslaufen und die
   runden Ecken halten — aber es klippt eben **alles**, auch die Textsaeule. Und
   die Saeule steht im Ruhezustand nicht mittig, sondern ist um
   `46px x --v2-fx` = 74 px nach unten versetzt (ihre Kurve laeuft von dort nach
   oben). Genau diese 74 px druecken den letzten Knopf an die Klippkante.

   `.split-cta__media` liegt mit `inset: 0` deckungsgleich auf der Buehne. Es
   kann das Klippen also uebernehmen, ohne dass sich etwas veraendert: die
   Haelften bleiben gefasst, die Ecken bleiben rund — die Textsaeule aber liegt
   als Geschwister daneben und wird nicht mehr beschnitten. Ist der Text auf
   einem sehr schmalen Geraet laenger als der Bildschirm, ragt er sichtbar
   heraus statt unsichtbar abgeschnitten zu werden.

   **Dieser Block steht bewusst nach dem Panel-Besitz.** Dort setzt
   `.js-motion[data-layout="v02"] .split-cta__stage` das `overflow: hidden` mit
   gleicher Spezifitaet (0,3,0); im mobilen Sammelblock weiter oben waere die
   Ruecknahme wirkungslos. Beide Selektoren stehen mit, damit die Grundfassung
   und die v02-Fassung getroffen sind.
   -------------------------------------------------------------------------- */

@media (max-width: 1024px) {
  .js-motion .split-cta__stage,
  .js-motion[data-layout="v02"] .split-cta__stage {
    overflow: visible;
  }

  .js-motion .split-cta__media,
  .js-motion[data-layout="v02"] .split-cta__media {
    border-radius: var(--v2-panel-radius);
    overflow: hidden;
  }

  /* Der Verlauf liegt als Geschwister mit `inset: 0` auf der Buehne und hatte
     nie eine eigene Rundung — die Buehne klippte ihn. Ohne sie stuenden seine
     eckigen Ecken ueber den runden Panel-Ecken. Dieselbe Loesung wie beim
     Verlauf der Aufblende, der aus demselben Grund `--v2-panel-radius` traegt. */
  .js-motion .split-cta__scrim,
  .js-motion[data-layout="v02"] .split-cta__scrim {
    border-radius: var(--v2-panel-radius);
  }
}

/* --------------------------------------------------------------------------
   gallery-x — waagerechte Bildstrecke, angepinnt (Hochzeitsfotos, Abschnitt 02)
   Die Buehne klebt, der Streifen faehrt seitwaerts. Der Weg ist die eigene
   Ueberbreite: `translateX(calc(-1 * p * (100% - 100vw)))` — `100 %` ist die
   Streifenbreite, `100vw` der Bildschirm, die Differenz also genau das, was
   ueber den Rand hinausragt. Damit muss JS nichts messen, es schreibt allein den
   Fortschritt.

   Ohne JS und ohne Bewegung bleibt der Streifen ein normal scrollbarer Kasten
   (`overflow-x: auto`) — die Bilder sind dann per Wischen oder Tastatur
   erreichbar.
   -------------------------------------------------------------------------- */

/* Beide Baender liegen auf dem Seitengrund (Kundenwunsch 31.07.2026): keine
   eigene Flaeche. Sie tragen dafuer `data-theme="light"` im Markup, sonst
   stuende heller Text auf dem weissen Passepartout. */
.gallery-x {
  position: relative;
}

/* Die Buehne hat **keine** eigene Hoehe (Kundenwunsch 31.07.2026): Auf weissem
   Grund wirkte der leere Kasten wie ein Fehler. Sie ist so hoch wie ihr Inhalt
   und traegt stattdessen einen senkrechten Innenabstand.

   Der Wert haengt mit dem Facts-Band zusammen: Der Abstand unter dessen Text ist
   Facts-Polsterung + Passepartout + dieser Innenabstand. Damit das Band mittig
   zwischen Auftakt und Galerie bleibt, tragen beide `--s-11` und Facts unten
   nichts — 22 + 44 oben, 0 + 22 + 44 unten. Wer hier aendert, zieht die untere
   Facts-Polsterung nach. */
.gallery-x__stage {
  display: flex;
  flex-direction: column;
  gap: var(--space-comp);
  padding-block: var(--s-11);
  overflow: hidden;
}

.gallery-x__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-tight);
  padding-inline: var(--v2-inline);
}

.gallery-x__hint {
  margin: 0;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

.gallery-x__strip {
  display: flex;
  align-items: center;
  gap: var(--v2-shot-gap);
  margin: 0;
  padding: 0 var(--v2-inline);
  overflow-x: auto;
  /* Dieselbe Pflicht wie beim `film-grid`: eine Achse auf `auto` rechnet die
     andere von `visible` auf `auto` hoch, und ein senkrechter Balken neben dem
     waagerechten ist immer ein Fehler. */
  overflow-y: hidden;
  list-style: none;
}

.gallery-x__item {
  flex: none;
  margin: 0;
}

.gallery-x__item--tall { width: var(--v2-shot-tall); aspect-ratio: 4 / 5; }
.gallery-x__item--wide { width: var(--v2-shot-wide); aspect-ratio: 3 / 2; }
.gallery-x__item--portrait { width: var(--v2-shot-portrait); aspect-ratio: 2 / 3; }
.gallery-x__item--square { width: var(--v2-shot-square); aspect-ratio: 1 / 1; }

.gallery-x__item--top { align-self: flex-start; }
.gallery-x__item--mid { align-self: center; }
.gallery-x__item--low { align-self: flex-end; }

.gallery-x__figure {
  position: relative;
  height: 100%;
  margin: 0;
  overflow: hidden;
  background: var(--color-surface-alt);
}

.gallery-x__figure img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Abschnitt 05 der Fotoseite steht auf dem zweiten hellen Ton des Entwurfs. */
.steps--mat[data-theme="light"] {
  background: var(--color-surface-alt);
}

.benefits__note {
  max-width: var(--measure-wide);
  margin-top: var(--space-group);
}

/* Auftakt-Ueberschrift der Unterseiten: ein Satz, keine Parole. Steht nach der
   allgemeinen v02-Regel, weil beide dieselbe Spezifitaet haben. `text-transform`
   muss ausdruecklich zurueckgenommen werden — die Basisregel setzt Versalien,
   und ein Satz in Versalien liest sich schlecht und laenger. */
[data-layout="v02"] .hero__title--long {
  font-size: var(--v2-h1-sub);
  line-height: var(--leading-step);
  letter-spacing: var(--tracking-tight);
  text-transform: none;
  text-wrap: balance;
}

/* „Auf den Punkt" unter dem Auftakt: der Entwurf setzt die vier Punkte in ein
   Raster, das je Spalte mindestens 230 px braucht — auf breiten Bildschirmen
   vier Spalten, dann zwei, dann eine. Die Punkte selbst bleiben `check-list`:
   Gedankenstrich in Gold, gedaempfter Text, dieselbe Zeilenfuehrung. */
.check-list--grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: var(--space-comp) var(--space-group);
  /* Keine Breitenbegrenzung (Kundenwunsch 31.07.2026): Das Band laeuft ueber die
     volle Spaltenbreite. `none` muss ausdruecklich stehen — `.check-list` bringt
     als Fliesstext-Liste 65ch mit. */
  max-width: none;
  margin-block: 0;
}

/* Die Haarlinien der Vorlage oben und unten am Band. */
/* Weder Flaeche noch Linien (Kundenwunsch 31.07.2026) — das Band liegt frei auf
   dem Seitengrund. Der Abstand zur Galerie ist halbiert: Er setzte sich aus der
   unteren Polsterung des Bandes, dem Passepartout und der oberen Polsterung der
   Galeriebuehne zusammen (Desktop 56 + 22 + 32 = 110 px). Gekuerzt sind die
   beiden Polsterungen auf `--space-tight`, das Passepartout bleibt: 16 + 22 + 16
   = 54 px. */
/* Weder Flaeche noch Linien (Kundenwunsch 31.07.2026) — das Band liegt frei auf
   dem Seitengrund und steht **mittig** zwischen Auftakt und Galerie.

   Der Abstand darueber ist Passepartout plus obere Polsterung, der darunter
   untere Polsterung plus Passepartout. Gleich sind sie nur, wenn die Polsterung
   symmetrisch ist **und** die Galerie oben keine eigene mehr aufschlaegt — sonst
   waere die untere Seite um deren Betrag groesser. Mit 44 px je Seite bleibt der
   Gesamtabstand der halbierten Fassung erhalten: 22 + 44 = 66 px oben,
   44 + 22 = 66 px unten. */
[data-layout="v02"] .facts {
  padding-block: var(--s-11) 0;
  background: none;
}

/* Auf `/hochzeiten/hochzeitsvideos` folgt unter dem Band ein **deckendes** Panel
   (die dunkle Filmgalerie), keine flaechenlose Buehne. Dessen Polsterung liegt
   innerhalb des Panels und liest damit nicht als Abstand — das Band braucht seine
   untere Polsterung deshalb selbst. Ohne den Modifier stehen 66 px ueber, aber
   nur 22 px unter dem Band. Muss **nach** der Basisregel stehen: gleiche
   Spezifitaet. */
[data-layout="v02"] .facts--even {
  padding-block: var(--s-11);
}

/* --------------------------------------------------------------------------
   flow — Ablauf als vier Karten nebeneinander (Kundenwunsch 31.07.2026)
   Statt der senkrechten `steps`-Liste: ein Raster, das ab vier Spalten auf zwei
   und eine faellt. Jede Karte traegt ihre Nummer als grosse, leise Ziffer.

   Auftritt: Die Karten erscheinen **nacheinander**, scrollgebunden. Der
   Fortschritt der Gruppe (`--flow-p`, von JS) wird mit vier multipliziert und je
   Karte um ihren Index versetzt — Karte 1 ist bei 25 % fertig, Karte 4 bei
   100 %. Beim Zurueckscrollen laeuft es rueckwaerts, weil der Wert am Scroll
   haengt und nicht an einem einmaligen Ereignis.

   **Bewusste Abweichung von Kap. 9** („ein Entrance-Muster projektweit"): Dies
   ist neben dem Tilt am Statement-Aside und dem Zeichen-Zerfall die dritte
   benannte Ausnahme. Die Karten tragen **kein** `data-reveal` — kein Element
   bekommt also zwei Auftritts-Mechanismen.
   -------------------------------------------------------------------------- */

.flow {
  padding-block: var(--space-hero);
}

.flow__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: var(--space-comp);
  margin: var(--space-group) 0 0;
  padding: 0;
  list-style: none;
  counter-reset: none;
}

.flow__card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-inline);
  padding: var(--space-comp);
  overflow: hidden;
  border: var(--border-hair) solid var(--color-line);
  border-radius: var(--v2-panel-radius);
  background: var(--color-surface);
}

.flow__num {
  position: absolute;
  top: calc(-1 * var(--space-tight));
  right: var(--space-tight);
  color: var(--color-line);
  font-size: var(--v2-flow-num);
  font-weight: var(--weight-black);
  line-height: var(--leading-flat);
  letter-spacing: var(--tracking-display);
  pointer-events: none;
}

.flow__title {
  position: relative;
  margin: 0;
  font-size: var(--text-lg);
  font-weight: var(--weight-extrabold);
  line-height: var(--leading-snug);
  color: var(--color-text);
}

.flow__text {
  position: relative;
  margin: 0;
  color: var(--color-text-muted);
  line-height: var(--leading-relaxed);
}

/* Auftritt nur mit JS und nur bei erlaubter Bewegung — ohne das stehen die
   Karten fertig da (kein `opacity: 0` als CSS-Vorgabe, Kap. 9). */
@media (prefers-reduced-motion: no-preference) {
  .js-motion .flow__card {
    --e: clamp(0, calc(var(--flow-p, 1) * 4 - var(--i, 0)), 1);
    opacity: var(--e);
    transform: translateY(calc((1 - var(--e)) * var(--v2-flow-lift)))
      scale(calc(0.97 + var(--e) * 0.03));
    will-change: transform, opacity;
  }

  .js-motion .flow__card:nth-child(1) { --i: 0; }
  .js-motion .flow__card:nth-child(2) { --i: 1; }
  .js-motion .flow__card:nth-child(3) { --i: 2; }
  .js-motion .flow__card:nth-child(4) { --i: 3; }
}

/* Die Kacheln der Bildstrecke stehen fuer sich — kein Verlauf, kein Titel
   (Kundenwunsch 31.07.2026). Ihr Auftritt ist ein Aufdecken von oben nach unten:
   `clip-path` gibt das Bild frei, dazu setzt es sich aus 1.04 auf 1. Gestaffelt
   ueber den Eintritt der Gruppe (`--shots-p`), Faktor 6 — die ersten sechs
   Kacheln laufen sichtbar nacheinander auf, die uebrigen sind fertig, bevor sie
   von rechts hereinfahren.

   Vierte benannte Ausnahme zu Kap. 9. Auch hier tragen die Kacheln **kein**
   `data-reveal`, und ohne JS oder bei reduzierter Bewegung stehen sie fertig da. */
@media (prefers-reduced-motion: no-preference) {
  .js-motion .gallery-x__item {
    --e: clamp(0, calc(var(--shots-p, 1) * 6 - var(--i, 0)), 1);
    clip-path: inset(0 0 calc((1 - var(--e)) * 100%) 0);
    transform: scale(calc(1.04 - var(--e) * 0.04));
    will-change: clip-path, transform;
  }

  .js-motion .gallery-x__item:nth-child(1) { --i: 0; }
  .js-motion .gallery-x__item:nth-child(2) { --i: 0.5; }
  .js-motion .gallery-x__item:nth-child(3) { --i: 1; }
  .js-motion .gallery-x__item:nth-child(4) { --i: 1.5; }
  .js-motion .gallery-x__item:nth-child(5) { --i: 2; }
  .js-motion .gallery-x__item:nth-child(6) { --i: 2.5; }
  .js-motion .gallery-x__item:nth-child(7) { --i: 3; }
  .js-motion .gallery-x__item:nth-child(8) { --i: 3.5; }
  .js-motion .gallery-x__item:nth-child(n + 9) { --i: 4; }
}

/* Das FAQ steht buendig unter seiner Ueberschrift. `.faq__list` ist von Haus aus
   mit `margin-inline: auto` zentriert, die Ueberschrift nicht — daher der
   Versatz. Auf `[data-layout="v02"]` begrenzt; die drei uebrigen Seiten mit FAQ
   haben denselben Versatz, sind aber nicht Teil dieses Auftrags. */
[data-layout="v02"] .faq__list {
  max-width: none;
  margin-inline: 0;
}

/* Die Auftakt-Ueberschrift der Unterseiten blendet beim Scrollen aus und laeuft
   dem Scroll etwas nach (Kundenwunsch 31.07.2026). Der Zeichen-Zerfall der
   Ueberseiten war hier zu viel: Er zerlegt achtzig Zeichen einzeln, was bei einem
   ganzen Satz unruhig wirkt statt praegnant.

   Der Fortschritt ist `--hero-p`, den `.hero` ohnehin traegt — kein zusaetzlicher
   Wert, kein zusaetzliches JS. Der Nachlauf ist eine Verschiebung **nach unten**:
   Waehrend die Seite nach oben laeuft, wandert die Ueberschrift gegen die
   Laufrichtung und bleibt damit zurueck. Ohne JS ist `--hero-p` nicht gesetzt,
   der Vorschaltwert 0 laesst die Ueberschrift ruhig und voll deckend stehen. */
.js-motion[data-layout="v02"] .hero__title--long {
  opacity: clamp(0, calc(1 - var(--hero-p, 0) * 1.25), 1);
  transform: translateY(calc(var(--hero-p, 0) * var(--v2-hero-lag)));
  will-change: transform, opacity;
}

/* --------------------------------------------------------------------------
   Bildstrecke mobil: echtes Wischen statt Inszenierung (Kundenwunsch 31.07.2026)
   Angepinnt gibt es keinen waagerechten Scrollbereich — der Streifen wird ja
   verschoben, nicht gescrollt. Ein Wisch nach links traf damit ins Leere und
   wurde als Vertikalscroll gedeutet: Die Seite sprang weiter, statt die Bilder zu
   zeigen.

   Bis 1024 px ist die Inszenierung deshalb aus. Der Streifen ist ein normaler
   Scrollbereich: waagerecht wischen bewegt die Bilder, senkrecht wischen scrollt
   die Seite weiter — der Browser trennt die Achsen selbst.
   `overscroll-behavior-x: contain` verhindert, dass der Wisch am Ende des
   Streifens auf die Seite ueberspringt.
   -------------------------------------------------------------------------- */

@media (max-width: 1024px) {
  .js-motion .gallery-x {
    height: auto;
  }

  /* Bildschirmhoehe und Zentrierung gehoeren zum angepinnten Zustand und
     muessen hier zurueck — mobil ist die Buehne nicht angepinnt, ein
     bildschirmhoher Kasten waere dort der leere Kasten von oben. */
  .js-motion .gallery-x__stage {
    position: static;
    container-type: normal;
    min-height: 0;
    justify-content: flex-start;
  }

  .js-motion .gallery-x__strip {
    width: auto;
    overflow-x: auto;
    overflow-y: hidden;
    transform: none;
    will-change: auto;
    scroll-snap-type: x proximity;
    scroll-padding-inline: var(--v2-inline);
    overscroll-behavior-x: contain;
  }

  .js-motion .gallery-x__item {
    scroll-snap-align: center;
  }
}

/* --------------------------------------------------------------------------
   pricing · price-table · package-card — Pakete und Preise (21.08.2026)

   Der Abschnitt zeigt denselben Stoff **zweimal**, und das ist Absicht: eine
   Tabelle fuer den Vergleich (Paket, Begleitzeit und Preis nebeneinander) und
   Karten fuer den Inhalt (was jeweils dazugehoert). Die Tabelle beantwortet
   „was kostet was", die Karten „was bekomme ich dafuer". Beides in einer
   Darstellung waere entweder eine unlesbar breite Tabelle mit siebzehn
   Leistungspunkten je Zeile oder ein Kartenstapel, in dem sich Preise nicht
   vergleichen lassen.

   Die Tabelle ist eine **echte** `<table>`: Spaltenkoepfe, Zeilenkoepfe und der
   Gruppenkopf (`scope="rowgroup"`) sind Struktur, nicht Optik. Der uebliche
   „responsive table"-Umbau auf `display: block` faellt deshalb aus — er nimmt
   der Tabelle in den meisten Browsern genau diese Semantik. Auf schmalen
   Geraeten scrollt sie stattdessen seitwaerts in ihrem eigenen Kasten, und der
   Kasten ist fokussierbar, damit er auch per Tastatur bedienbar bleibt.
   -------------------------------------------------------------------------- */

.pricing {
  padding: var(--space-section) var(--space-gutter);
  background: var(--color-surface);
}

/* --- Preisuebersicht --- */

.price-table__scroll {
  margin-top: var(--space-group);
  overflow-x: auto;
  overscroll-behavior-x: contain;
}

.price-table {
  width: 100%;
  min-width: var(--table-min);
  border-collapse: collapse;
  text-align: left;
}

.price-table__label {
  padding: 0 var(--space-comp) var(--space-tight) 0;
  border-bottom: var(--border-hair) solid var(--color-line-strong);
  font-size: var(--text-sm);
  font-weight: var(--weight-extrabold);
  letter-spacing: var(--tracking-wide);
  line-height: var(--leading-normal);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

/* Der Gruppenkopf traegt keine Linie: Die Haarlinie der ersten Datenzeile
   darunter liest sich als seine Unterstreichung, eine zweite waere doppelt. */
.price-table__group {
  padding: var(--space-group) var(--space-comp) var(--space-tight) 0;
  font-size: var(--text-sm);
  font-weight: var(--weight-extrabold);
  letter-spacing: var(--tracking-wide);
  line-height: var(--leading-normal);
  text-transform: uppercase;
  color: var(--color-brand);
}

.price-table__name,
.price-table__time,
.price-table__price {
  padding: var(--space-comp) var(--space-comp) var(--space-comp) 0;
  border-top: var(--border-hair) solid var(--color-line);
  line-height: var(--leading-relaxed);
  vertical-align: baseline;
}

.price-table__name {
  font-weight: var(--weight-bold);
  color: var(--color-text);
}

.price-table__sub {
  display: block;
  font-size: var(--text-sm);
  font-weight: var(--weight-regular);
  line-height: var(--leading-normal);
  color: var(--color-text-muted);
}

.price-table__time {
  color: var(--color-text-muted);
}

.price-table__label--price,
.price-table__price {
  padding-right: 0;
  text-align: right;
}

/* Nur Datenzeilen heben sich unter dem Zeiger — der Gruppenkopf ist ebenfalls
   ein `tr` und soll ruhig bleiben. Ohne `:has()`-Unterstuetzung entfaellt die
   ganze Regel; sie ist Lesehilfe, kein Bedienelement. */
.price-table tbody tr:has(td):hover {
  background: var(--color-surface-alt);
}

.price-table__note {
  margin: var(--space-comp) 0 0;
  max-width: var(--measure-wide);
  font-size: var(--text-sm);
  line-height: var(--leading-relaxed);
  color: var(--color-text-muted);
}

/* --- Preisangabe: „ab" leise, Betrag in der Markenfarbe --- */

.price {
  margin: var(--space-comp) 0 0;
  font-size: var(--text-2xl);
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-tight);
}

.price__from {
  font-weight: var(--weight-regular);
  color: var(--color-text-muted);
}

.price .price__from {
  font-size: var(--text-md);
}

.price__amount {
  font-weight: var(--weight-extrabold);
  white-space: nowrap;
  color: var(--color-brand);
}

/* --- Paketkarten --- */

.package-grid {
  list-style: none;
  margin: var(--space-group) 0 0;
  padding: 0;
  display: grid;
  gap: var(--space-block);
}

@media (min-width: 640px) {
  .package-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 1025px) {
  .package-grid { grid-template-columns: repeat(3, 1fr); }
}

.package-card {
  display: flex;
  flex-direction: column;
  padding: var(--space-block);
  border: var(--border-hair) solid var(--color-line);
  border-radius: var(--v2-panel-radius);
  background: var(--color-surface-alt);
}

.package-card__num {
  margin: 0;
  font-size: var(--text-sm);
  font-weight: var(--weight-extrabold);
  letter-spacing: var(--tracking-wide);
  line-height: var(--leading-normal);
  text-transform: uppercase;
  color: var(--color-eyebrow);
}

.package-card__title {
  margin: var(--space-inline) 0 0;
  font-size: var(--text-xl);
  font-weight: var(--weight-extrabold);
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-tight);
}

.package-card__sub {
  margin: var(--space-inline) 0 0;
  line-height: var(--leading-relaxed);
  color: var(--color-text-muted);
}

.package-card__text {
  margin: var(--space-comp) 0 0;
  line-height: var(--leading-relaxed);
  color: var(--color-text-muted);
}

/* Die Karten eines Rasters sind gleich hoch, ihre Leistungslisten aber nicht —
   das Premium-Paket bringt siebzehn Punkte mit, das kleinste neun. `margin-top:
   auto` legt die Begleitzeit deshalb auf den Kartenboden, damit sie in allen
   drei Karten auf einer Linie steht. Zwei Deklarationen statt der
   Kurzschreibweise `margin: auto 0 0`: die faellt sonst durch den Spacing-Scan
   (QA-Check 5), der Rohwerte ausser `0` und `auto` allein nicht kennt. */
.package-card__meta {
  margin: 0;
  margin-top: auto;
  padding-top: var(--space-comp);
  border-top: var(--border-hair) solid var(--color-line);
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
  color: var(--color-text-muted);
}

/* --------------------------------------------------------------------------
   cta-row — ein Knopf mitten im Abschnitt (21.08.2026)

   Die Wegweiser auf `/hochzeiten/pakete-preise` stehen in Abschnitten, die
   bisher keinen Knopf trugen (`aperture`, `text-section`, `benefits`). Sie
   brauchen dafuer eine eigene Zeile: Ein blanker `<a class="btn">` in einem
   `<p>` bekaeme den Browser-Standardrand von 1em, und eine `mt-*`-Utility
   waere hier wirkungslos, sobald die Komponente daneben die Kurzschreibweise
   `margin` setzt (dieselbe Falle wie in Kap. 22).

   Die Ausrichtung kommt **nicht** aus dem `text-align` des Vorfahren, weil ein
   Flex-Container das nicht auf seine Kinder als Hauptachse uebersetzt — der
   zentrierte `aperture__body` braucht deshalb den Modifier.
   -------------------------------------------------------------------------- */

.cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-tight);
  margin: var(--space-group) 0 0;
}

.cta-row--center {
  justify-content: center;
}
