/* ================= PATRICKS WELT — LebensFEELfalt design system =================
   Calm, warm, premium wellness. His real brand colours: sage-green + slate-blue + cream.
   A distinct visual language from the Kovač site: soft glass, organic depth, warm serif. */
:root {
  --sage: #6bb29b;
  /* --sage-deep darkened #4e9079 -> #3c7059 so it clears WCAG AA 4.5:1 on cream
     (was 3.33:1, now ~5.2:1). It is the colour of every eyebrow, tag, hint and the
     primary PDF CTA. --sage stays bright for decorative fills (progress bar, bullets). */
  --sage-deep: #3c7059;
  --sage-soft: #a7d0c1;
  --slate: #3f5064;
  --slate-deep: #2a3746;
  --slate-soft: #566880;  /* darkened from #6d7f92 (3.65:1) to clear AA on cream (~4.7:1);
                             only used for the nav sub-label and the world labels, both text */
  --cream: #f5f1e7;
  --cream-warm: #ece3d3;
  --ink: #1f2c27;         /* deep slate-green for dark sections / text */
  --paper: var(--cream);
  --glass: rgba(255, 255, 255, 0.16);
  --glass-line: rgba(255, 255, 255, 0.4);
  --shadow-soft: 0 24px 60px rgba(31, 44, 39, 0.14);
  --shadow-lift: 0 40px 90px rgba(31, 44, 39, 0.22);

  --sans: 'Archivo', system-ui, sans-serif;
  --display: 'Fraunces', Georgia, serif;
  --serif: 'Fraunces Text', Georgia, serif;

  --r-sm: 4px; --r-md: 16px; --r-lg: 22px;
  --fs-body: 1.0625rem; --lh-body: 1.72; --fs-lead: 1.2rem;
}
/* shared running-text scale */
.world__lead, .offer__desc, .warum__text p, .angebote__sub, .pdf__card p, .footer__lead {
  font-size: var(--fs-body); line-height: var(--lh-body);
}

/* visually hidden but exposed to assistive tech — carries the page's only h1 */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: auto; }
body {
  font-family: var(--sans);
  background: var(--paper);
  color: var(--ink);
  overflow-x: hidden;
  cursor: none;
  -webkit-font-smoothing: antialiased;
}
img, canvas, video { display: block; max-width: 100%; }
/* Der Deko-Punkt ersetzt den Zeiger ueberall, nur in Eingabefeldern nicht. Wer
   tippt, muss sehen, wo er hinklickt und wo der Text anfaengt — ein Kreis, der
   dem Zeiger hinterherlaeuft, kann das nicht. Betrifft seit dem 06.08.2026 auch
   das Formular in der PDF-Sektion, das mitten auf der Seite steht. */
input, textarea, select { cursor: auto; }
input[type="email"], input[type="text"], input[type="number"],
input[type="tel"], textarea { cursor: text; }
a { color: inherit; text-decoration: none; }

/* Focus. The site had NO focus rule anywhere — nothing set outline:none, so the
   UA ring survived, but it was never tuned for these backgrounds and the nav's
   hover underline had no keyboard equivalent. :focus-visible keeps it off mouse
   clicks. */
:focus-visible {
  outline: 3px solid var(--sage-deep);
  outline-offset: 3px;
  border-radius: 2px;
}
.nav__links a:focus-visible span::after { transform: scaleX(1); transform-origin: left; }

/* body{cursor:none} hides the pointer for the custom dot, and the native cursor
   is only restored under (hover:none). Links WITHOUT a [data-cursor] — the
   Impressum/Datenschutz links above all — therefore had no hover affordance at
   all for mouse users. The custom dot still draws on top of this. */
a, button, summary, label { cursor: pointer; }

/* Skip link: off-screen until focused, then pinned top-left above everything. */
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 300;
  background: var(--slate-deep); color: var(--cream);
  padding: 0.9rem 1.3rem; border-radius: 0 0 12px 0;
  font-size: 0.8rem; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase;
}
.skip-link:focus { left: 0; }
::selection { background: var(--sage); color: #fff; }

/* ---------- reusable glass panel ---------- */
.glass {
  background: var(--glass);
  border: 1px solid var(--glass-line);
  backdrop-filter: blur(16px) saturate(1.2);
  -webkit-backdrop-filter: blur(16px) saturate(1.2);
  box-shadow: var(--shadow-soft), inset 0 1px 0 rgba(255,255,255,0.35);
}

/* ---------- fixed UI ---------- */
.grain {
  position: fixed; inset: 0; z-index: 60; pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
  background-size: 240px 240px; opacity: 0.035;
}
/* 95, not 90: .nav__mobile is also 90 and comes later in the DOM, so it won every
   tie and buried the custom dot. With body{cursor:none} and the native-cursor
   restore gated behind (hover:none), a narrow DESKTOP window with the menu open
   had no visible pointer at all. Still below .nav (100). */
.cursor { position: fixed; top: 0; left: 0; z-index: 95; pointer-events: none; }
.cursor__dot {
  display: block; width: 10px; height: 10px; border-radius: 50%;
  background: var(--sage-deep); transform: translate(-50%, -50%);
  transition: width .3s cubic-bezier(.22,1,.36,1), height .3s cubic-bezier(.22,1,.36,1), background .3s;
}
.cursor.is-hover .cursor__dot { width: 68px; height: 68px; background: rgba(107,178,155,0.22); border: 1px solid var(--sage-deep); }
.cursor__label {
  position: absolute; top: 0; left: 0; transform: translate(-50%,-50%);
  font-size: 0.58rem; font-weight: 600; letter-spacing: 0.12em; color: var(--slate-deep);
  opacity: 0; white-space: nowrap; text-transform: uppercase;
}
.cursor.is-hover .cursor__label { opacity: 1; }
@media (hover: none) { .cursor { display: none; } body { cursor: auto; } }

.progress {
  position: fixed; top: 0; left: 0; height: 2px; width: 100%;
  background: var(--sage); z-index: 80; transform-origin: 0 0; transform: scaleX(0);
}

/* ================= LOADER =================
   Der Vorhang wird NICHT mehr von GSAP bewegt, sondern von einer CSS-Transition,
   die ein Inline-Skript per .is-out ausloest. Grund steht im Markup. Alles hier
   muss deshalb ohne JS einen sinnvollen Zustand haben. */
.loader {
  position: fixed; inset: 0; z-index: 200;
  background: var(--cream);
  display: grid; grid-template-rows: 1fr auto 1fr; justify-items: center; align-items: center;
  padding: 8vh 6vw;
  transform: translateY(0);
  transition: transform .72s cubic-bezier(.76, 0, .24, 1);
  will-change: transform;
}
.loader.is-out { transform: translateY(-100%); }

.loader__brand {
  align-self: start;
  font-family: var(--serif); font-size: clamp(1rem, 2.2vw, 1.35rem);
  color: var(--slate-deep); letter-spacing: 0.02em; opacity: .75;
}
.loader__brand em { font-style: normal; color: var(--sage-deep); }

/* Die Zahl ist der Hauptdarsteller. Tabellenziffern, sonst zappelt die Breite
   bei jedem Zaehlerschritt und das ganze Element wackelt. */
.loader__num {
  display: flex; align-items: baseline; gap: .1em;
  font-family: var(--display); font-weight: 400;
  font-size: clamp(4.5rem, 22vw, 11rem); line-height: .9;
  color: var(--slate-deep);
  font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1;
}
.loader__num i { font-style: normal; font-size: .28em; color: var(--sage-deep); }

.loader__bar {
  width: min(300px, 62vw); height: 2px; margin-top: 2.2rem;
  background: rgba(63, 80, 100, 0.14); border-radius: 2px; overflow: hidden;
}
/* scaleX statt width: width laesst den Browser bei jedem Frame neu layouten,
   transform laeuft im Compositor. Bei 60 fps ist das der Unterschied zwischen
   ruckeln und nicht ruckeln, gerade auf dem Handy. */
.loader__fill {
  height: 100%; width: 100%; background: var(--sage);
  transform: scaleX(0); transform-origin: 0 50%;
}
.loader__word {
  align-self: end;
  font-family: var(--sans); font-size: .68rem; letter-spacing: .28em;
  text-transform: uppercase; color: var(--slate); opacity: .55;
}

@media (prefers-reduced-motion: reduce) {
  .loader { transition: opacity .25s linear; }
  .loader.is-out { transform: none; opacity: 0; }
}

/* ================= NAV ================= */
.nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 100;
  padding: 1.1rem 1.8rem;
}
.nav__bar {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 2rem;
  padding: 0.7rem 0.7rem 0.7rem 1.5rem;
  border-radius: 999px;
  background: rgba(245, 241, 231, 0.86);
  border: 1px solid rgba(63, 80, 100, 0.09);
  box-shadow: 0 10px 34px rgba(31, 44, 39, 0.07);
  /* Ergaenzt am 19.08.2026: die Pille hatte nur Deckkraft, keine Weichzeichnung.
     Beim Scrollen lief Fliesstext ungebremst darunter durch und blieb an der
     Kante teils lesbar — Wort "sollst" im About-Absatz war betroffen (Beleg:
     idx-mob-sollst2.png). .nav__sub-panel und .glass loesen dieselbe
     Ueberlagerung schon per backdrop-filter, hier fehlte er nur an der Pille.
     Zwilling zu base.css .nav__bar — dort ebenfalls ergaenzt. */
  backdrop-filter: blur(14px) saturate(1.2);
  -webkit-backdrop-filter: blur(14px) saturate(1.2);
  transition: background .45s ease, border-color .45s ease, box-shadow .45s ease;
}
.nav__brand { display: flex; flex-direction: column; line-height: 1.02; }
.nav__brand-text { font-family: var(--serif); font-size: 1.16rem; font-weight: 600; letter-spacing: 0.005em; }
.nav__brand-text em { font-style: normal; color: var(--sage-deep); }
.nav__brand-sub {
  font-size: 0.52rem; letter-spacing: 0.24em; text-transform: uppercase;
  color: var(--slate-soft); margin-top: 3px;
}
.nav__links {
  justify-self: center; display: flex; gap: 1.9rem;
  font-size: 0.71rem; letter-spacing: 0.1em; text-transform: uppercase; font-weight: 500;
}
/* 0.71rem of text gave these a 16px-tall hit area — under the 24px minimum target
   size. The padding grows the target without moving the underline, which hangs off
   the inner span. The row still sits below the 38px CTA, so the bar keeps its height. */
.nav__links a { padding: 6px 0; }
/* OHNE DIESE ZEILE STEHT "ANGEBOTE" 6px HOEHER ALS DER REST.
   "Angebote" ist der einzige Punkt mit Aufklappmenue und steckt deshalb in einem
   <div class="nav__item">. Damit ist sein <a> ein normales Inline-Element. Die
   drei anderen sind direkte Flex-Kinder von .nav__links und werden dadurch zu
   Blockelementen. Senkrechtes Padding wirkt bei Inline-Elementen NICHT auf die
   Hoehe — die 6px oben verpuffen also genau dort und nirgends sonst.
   Gemessen am 06.08.2026: "Angebote" bei top 29px, die anderen drei bei 35px,
   Hoehe 24.5px gegen 27.5px. display:block macht das <a> blockartig wie seine
   Geschwister, damit zaehlt das Padding und alle vier stehen auf einer Linie. */
.nav__item > a { display: block; }
.nav__links a span { position: relative; display: inline-block; padding-bottom: 3px; }
.nav__links a span::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
  background: currentColor; transform: scaleX(0); transform-origin: right;
  transition: transform .5s cubic-bezier(.22,1,.36,1);
}
.nav__links a:hover span::after { transform: scaleX(1); transform-origin: left; }

/* ---- Aufklapp-Menue unter "Angebote" ----
   Kein Toggle-Script: das Panel oeffnet auf :hover UND :focus-within, damit es mit
   der Maus und mit der Tabulatortaste funktioniert und auch ohne JavaScript steht.
   .nav__sub ist nur die Positionsschale und traegt oben transparente Polsterung,
   damit der Weg von der Beschriftung ins Panel nicht durch eine Luecke fuehrt, in
   der :hover abreisst. Der sichtbare Kasten ist .nav__sub-panel. */
.nav__item { position: relative; }
.nav__sub {
  position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
  padding-top: 14px; min-width: 236px;
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity .28s ease, visibility .28s;
}
.nav__item:hover .nav__sub,
.nav__item:focus-within .nav__sub {
  opacity: 1; visibility: visible; pointer-events: auto;
}
.nav__sub-panel {
  display: flex; flex-direction: column;
  padding: 0.5rem 0.4rem;
  border-radius: 18px;
  background: rgba(245, 241, 231, 0.94);
  border: 1px solid rgba(63, 80, 100, 0.09);
  box-shadow: 0 16px 40px rgba(31, 44, 39, 0.14);
  backdrop-filter: blur(14px) saturate(1.2);
  -webkit-backdrop-filter: blur(14px) saturate(1.2);
  transform: translateY(-6px);
  transition: transform .32s cubic-bezier(.22,1,.36,1), background .45s ease, border-color .45s ease;
}
.nav__item:hover .nav__sub-panel,
.nav__item:focus-within .nav__sub-panel { transform: translateY(0); }
/* eigene Regel, sonst erbt der Untereintrag die 6px-Polsterung der Hauptzeile und
   bleibt unter der Mindestgroesse fuer Klickflaechen */
.nav__sub-panel a {
  display: block; padding: 0.62rem 0.9rem; border-radius: 12px;
  font-size: 0.7rem; letter-spacing: 0.06em; text-transform: none;
  font-weight: 500; white-space: nowrap;
  transition: background .25s ease;
}
.nav__sub-panel a:hover,
.nav__sub-panel a:focus-visible { background: rgba(107, 178, 155, 0.16); }
/* ueber den dunklen Sektionen faerbt sich die Pille um, das Panel muss mit */
.nav--light .nav__sub-panel {
  background: rgba(24, 34, 30, 0.9); border-color: rgba(245,241,231,0.14);
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.3);
}
.nav--light .nav__sub-panel a:hover,
.nav--light .nav__sub-panel a:focus-visible { background: rgba(245, 241, 231, 0.12); }

.nav__cta {
  justify-self: end; display: inline-flex; align-items: center; gap: 0.55rem;
  background: var(--slate-deep); color: var(--cream);
  font-size: 0.67rem; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase;
  padding: 0.78rem 1.3rem; border-radius: 999px;
  transition: background .35s ease, opacity .3s ease;
}
/* Ausgeblendet, solange der Hero mit seiner eigenen, groesseren "Termin
   sichern"-CTA im Bild steht (siehe .hero__cta) — zwei identische Knoepfe
   gleichzeitig im ersten Bildschirm waren genau die Konkurrenz, die am
   19.08.2026 bemaengelt wurde. navCta() in main.js schaltet is-visible, sobald
   der Hero komplett durchgescrollt ist; ab dann uebernimmt diese Pille wieder
   ihre alte Rolle als durchgehend erreichbare Buchungs-CTA in der Nav. */
.nav__cta { opacity: 0; pointer-events: none; }
.nav__cta.is-visible { opacity: 1; pointer-events: auto; }
.nav__cta i { font-style: normal; transition: transform .35s cubic-bezier(.22,1,.36,1); }
.nav__cta:hover { background: var(--sage-deep); }
.nav__cta:hover i { transform: translateX(4px); }

/* ---- mobile menu ---- */
.nav__toggle {
  display: none; justify-self: end; width: 44px; height: 44px; border: 0; background: none;
  cursor: pointer; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
}
.nav__toggle span {
  display: block; width: 22px; height: 2px; background: var(--ink); border-radius: 2px;
  transition: transform .3s ease, opacity .3s ease;
}
.nav.is-open .nav__toggle span:first-child { transform: translateY(4px) rotate(45deg); }
.nav.is-open .nav__toggle span:last-child { transform: translateY(-4px) rotate(-45deg); }
.nav__mobile {
  position: fixed; inset: 0; z-index: 90; background: var(--cream);
  display: flex; flex-direction: column; gap: 1.4rem; padding: 0 8vw;
  /* Im Querformat (844x390) passten die fuenf Eintraege nicht mehr in die Hoehe.
     Zentriertes Flex laeuft in BEIDE Richtungen ueber, der erste Link stand also bei
     -4px oberhalb des Overlays und war ohne overflow gar nicht erreichbar.
     `safe center` zentriert nur solange Platz ist und faellt sonst auf start zurueck. */
  justify-content: safe center;
  overflow-y: auto; -webkit-overflow-scrolling: touch;
  padding-block: 6vh;
  opacity: 0; visibility: hidden; transform: translateY(-8px);
  transition: opacity .3s ease, transform .3s ease, visibility .3s;
}
.nav__mobile.is-open { opacity: 1; visibility: visible; transform: none; }
.nav__mobile a {
  font-family: var(--display); font-size: clamp(1.6rem, 8vw, 2.4rem); color: var(--ink);
  padding: 0.3rem 0; letter-spacing: -0.01em;
}
/* Die fuenf Detailseiten stehen offen unter "Angebote", nicht hinter einem
   zweiten Tipp. Eigene Groesse, sonst erben sie die clamp(1.6rem, 8vw, 2.4rem)
   der Hauptzeilen und neun Eintraege dieser Groesse passen auf kein Telefon.
   Die Kante links ordnet sie sichtbar unter. */
.nav__mobile-sub {
  display: flex; flex-direction: column;
  margin: -0.5rem 0 0.2rem; padding-left: 1.05rem;
  border-left: 1px solid rgba(63, 80, 100, 0.16);
}
.nav__mobile-sub a {
  font-family: var(--sans); font-size: 0.92rem; font-weight: 500;
  letter-spacing: 0.01em; color: var(--slate);
  min-height: 44px; display: flex; align-items: center; padding: 0;
}
.nav__mobile-cta {
  margin-top: 1rem; font-family: var(--sans) !important; font-size: 0.8rem !important;
  font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: var(--sage-deep) !important;
}
@media (max-width: 900px) {
  .nav__bar { grid-template-columns: 1fr auto; }   /* brand | toggle */
  .nav__toggle { display: inline-flex; }
  .nav__cta { display: none; }   /* the CTA lives inside the overlay on phones */
}
/* ---- mobile booking dock ----
   Desktop already carries .nav__cta in the bar, so this is phones/tablets only.
   z-index 85 sits under .nav__mobile (90) so the open menu covers it, and under
   .nav (100). */
.cta-dock { display: none; }
/* Ergaenzt am 20.08.2026: war vorher eine kleine, mittig schwebende Pille mit
   Rand auf allen Seiten — sah aus wie ein zufaellig auf das Foto geklebter
   Knopf, ohne Bezug zum Rest der Flaeche. Jetzt eine randlose Vollbreite-Leiste,
   die am Bildschirmrand sitzt: dieselbe Konstruktion wie eine Tab- oder
   App-Leiste, die man aus jeder mobilen App kennt, und genau deshalb liest sie
   sich als gewollter Teil der Oberflaeche statt als loses Element. Groesser
   dazu: 0.74rem/48px Mindesthoehe -> 0.92rem/60px. */
@media (max-width: 900px) {
  .cta-dock {
    display: flex; align-items: center; justify-content: center; gap: 0.6rem;
    position: fixed; z-index: 85;
    left: 0; right: 0; bottom: 0;
    min-height: 60px;
    padding: 1.05rem 1.5rem calc(1.05rem + env(safe-area-inset-bottom, 0px));
    border-radius: 0;
    /* sage-deep statt slate-deep (20.08.2026): das ist die Marken-Aktionsfarbe,
       schon eigens auf 4.5:1 Kontrast zu --cream abgedunkelt (siehe Kommentar
       oben bei --sage-deep) — kraeftiger und "lebendiger" als das neutrale
       Navy-Grau, und macht den Knopf als DIE Handlung sofort erkennbar. */
    background: var(--sage-deep); color: var(--cream);
    font-size: 0.92rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
    white-space: nowrap;   /* uppercase + tracking wrapped "Termin sichern" onto two lines */
    box-shadow: 0 -10px 28px rgba(20, 44, 34, 0.35);
    opacity: 0; pointer-events: none; transform: translateY(100%);
    transition: transform .5s cubic-bezier(.22, 1, .36, 1), opacity .4s ease;
  }
  .cta-dock i { font-style: normal; }
  .cta-dock.is-shown {
    transform: translateY(0); opacity: 1; pointer-events: auto;
  }
}

/* over dark sections. War 0.72 — an der Trigger-Kante (site.js, 'top 4rem') liegt
   die Ueberschrift des .sec--dark-Abschnitts direkt hinter der Pille, und ohne
   backdrop-filter schimmerte der Text durch (19.08.2026). 0.92 haelt die Pille
   deckend genug, dass darunterliegende Ueberschriften nicht mehr durchscheinen. */
.nav--light .nav__bar { background: rgba(24, 34, 30, 0.92); border-color: rgba(245,241,231,0.14); box-shadow: none; }
.nav--light { color: var(--cream); }
.nav--light .nav__brand-text em { color: var(--sage-soft); }
.nav--light .nav__brand-sub { color: rgba(245,241,231,0.55); }
.nav--light .nav__cta { background: var(--cream); color: var(--ink); }

/* ---------- dynamic hero background (soft moving light, no particles) ---------- */
.hero__bg { position: absolute; inset: 0; overflow: hidden; z-index: 0; pointer-events: none; }
/* drifting contour field — sits under the glows, never stops moving */
.hero__lines { position: absolute; inset: 0; width: 100%; height: 100%; }
.hero__glow { position: absolute; border-radius: 50%; will-change: transform; }
.hero__glow--a {
  width: 72vw; height: 72vw; left: -22vw; top: -26vw;
  background: radial-gradient(circle at center, rgba(107,178,155,0.34), rgba(107,178,155,0) 66%);
}
.hero__glow--b {
  width: 62vw; height: 62vw; right: -18vw; bottom: -22vw;
  background: radial-gradient(circle at center, rgba(63,80,100,0.20), rgba(63,80,100,0) 66%);
}
.hero__glow--c {
  width: 48vw; height: 48vw; left: 34vw; top: 34vh;
  background: radial-gradient(circle at center, rgba(236,227,211,0.9), rgba(236,227,211,0) 68%);
}
.hero__rule {
  position: absolute; top: 0; bottom: 0; width: 1px;
  background: linear-gradient(180deg, transparent, rgba(63,80,100,0.12) 28%, rgba(63,80,100,0.12) 72%, transparent);
}
.hero__rule--l { left: 17vw; }
.hero__rule--r { right: 17vw; }

/* ================= HERO (liquid reveal, recoloured) ================= */
.hero {
  position: relative; min-height: 100vh;
  display: flex; align-items: center; justify-content: center;
  background: var(--paper); overflow: hidden;
}
/* props layer: the cut-out photographs live on meshes inside this canvas so they can
   deform. Above the ghost wordmark, below the portrait, and transparent to the
   pointer — proximity is measured in JS, so it never steals the liquid reveal. */
.hero-props {
  position: absolute; inset: 0; width: 100%; height: 100%;
  z-index: 1; pointer-events: none;
}
.hero__ghost {
  position: absolute; top: 50%; left: 50%;
  font-family: var(--display); font-weight: 400;
  font-size: clamp(6rem, 20vw, 20rem); line-height: 0.9;
  color: transparent; -webkit-text-stroke: 1px rgba(63,80,100,0.14);
  white-space: nowrap; user-select: none; z-index: 0;
}
/* big, but with headroom — cropping his head to chase size looks worse than the
   extra 60px is worth */
/* Anchored to the bottom and deliberately taller than the hero, so the straight
   line where the source photo crops his torso falls BELOW the fold. The only
   edges you can see are his own silhouette. */
.hero__portrait-wrap {
  position: relative; z-index: 2;
  /* Centred, with the Springseil and the Widerstandsband flanking him.
     Three things fight for the same vertical space, and this is the only geometry
     that satisfies all three:
       1. The Stirnband (photo y .05-.30) has to be on screen.
       2. The kettlebell runs to photo y .999 and the photo's bottom row is fully
          opaque — the bell is cut off by the photograph itself, so the ONLY way to
          show more of it is to show more of the photo. Its bottom edge therefore
          sits flush with the viewport bottom (margin-bottom: 0), which shows the
          maximum available bell and parks that hard cut where nobody can see it.
       3. The corner captions share his vertical band, so he has to stay clear of
          them HORIZONTALLY. At 1440x900 he spans x 425-1015; the captions end at
          x 369 and start at x 1027, so they read against the background instead.
     Sizing by vh as well as vw keeps all three true on short laptop viewports. */
  align-self: flex-end; margin-bottom: 0;
  width: min(41vw, 66vh); transform-style: preserve-3d;
}
/* Clean edge, nothing else. The liquid-reveal brief bans strokes, outlines, halos
   and shadows around the mask — the feathered edge I had here WAS a halo, and it
   made the portrait read like a memorial photo. No card, no shadow, no fade. */
.liquid-reveal {
  position: relative; width: 100%; aspect-ratio: 3 / 4; overflow: hidden;
  border-radius: 0; box-shadow: none;
  /* His photo crops his arms at its own frame edge from 63% down, and the
     kettlebell lives in that same band — so the crop cannot be hidden by
     framing. Soften ONLY the outer 4% of the sides, and only below 55% height:
     his face and torso keep a hard edge, the low arms just fall off. */
  -webkit-mask-image:
    linear-gradient(to bottom, #000 0 55%, transparent 55%),
    linear-gradient(to right, transparent 0, #000 4.5%, #000 95.5%, transparent 100%);
  -webkit-mask-composite: source-over;
  mask-image:
    linear-gradient(to bottom, #000 0 55%, transparent 55%),
    linear-gradient(to right, transparent 0, #000 4.5%, #000 95.5%, transparent 100%);
  mask-composite: add;
}
.liquid-reveal img, .liquid-reveal canvas { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* Die <picture>-Huelle (AVIF vor WebP) darf keine eigene Box bilden: sie ist
   sonst ein Inline-Element im Fluss und erzeugt Leerraum unter dem Bild.
   display:contents loest sie auf, das <img> bleibt absolut zur .liquid-reveal. */
.liquid-reveal picture { display: contents; }
/* the bottom layer exists only as a texture source for the shader. Left visible it
   peeks out wherever its matte is wider than the top's — his sunlit hair wisps
   ringed the cut-out in gold. The canvas does all the blending. */
.liquid-reveal [data-lr-bottom] { opacity: 0; }
.liquid-reveal canvas { pointer-events: none; opacity: 0; }
.liquid-reveal canvas.is-live { opacity: 1; }
.hero__portrait { filter: saturate(1.02); }
/* corner captions: these are the only words in the hero, so they have to be read
   at a glance — display face for the name, quiet uppercase for the qualifier */
.hero__meta {
  position: absolute; bottom: 6vh; z-index: 3;
  display: flex; flex-direction: column; gap: 0.5rem;
  color: var(--slate-deep);
}
.hero__meta span:first-child {
  font-family: var(--display); font-weight: 400; text-transform: none;
  font-size: clamp(1.35rem, 2.3vw, 2.3rem); line-height: 1;
  letter-spacing: -0.01em;
}
.hero__meta span + span {
  /* 0.92vw only clears a 0.66rem floor at 1148px, so every 1024 and most 1280
     laptops rendered these at 10.6px — uppercase, 0.2em tracked. One of the two
     lines is "Neustadt am Rübenberge", the local-SEO string the whole schema block
     is built around. 0.75rem floors it at 12px; the cap is unchanged. */
  font-size: clamp(0.75rem, 0.92vw, 0.86rem); font-weight: 600;
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--slate);
}
.hero__meta--left { left: 3.5vw; }
.hero__meta--right { right: 3.5vw; text-align: right; }
.hero__meta .accent { color: var(--sage-deep); }
/* Zentrale Buchungs-CTA, siehe Begruendung im Markup (index.html). z-index 3
   wie die Ecktexte: ueber dem Portraet (2) und dem Hintergrund (0/1). */
/* Ueberarbeitet am 21.08.2026. Vorher: top:15vh, 43px hoch, slate-deep.
   Gemessen bei 1440x900 sass der Knopf 55px unter der Nav-Leiste, aber nur 22px
   ueber Patricks Haaransatz — er hing also sichtbar am Kopf statt frei im Band
   dazwischen. Dazu war er in Form und Farbe identisch mit .nav__cta und las sich
   deshalb wie ein aus der Leiste gefallener Nav-Knopf, nicht wie die Haupt-
   handlung der Seite.

   Drei Aenderungen: mittig ins Band zwischen Nav-Unterkante und Kopf gesetzt
   (gleiche Luft oben wie unten), deutlich groesser (52px Mindesthoehe, damit auch
   ueber der 44px-Grenze fuer Trefferflaechen), und in --sage-deep statt
   --slate-deep. Das Gruen ist die Marken-Handlungsfarbe (schon auf 4.5:1 gegen
   --cream abgedunkelt) und trennt ihn eindeutig von der dunklen Nav-Pille.

   top in clamp() statt vh: die Nav ist position:fixed und endet IMMER bei 80px,
   unabhaengig von der Fensterhoehe. Ein reiner vh-Wert rutschte auf niedrigen
   Fenstern in die Nav hinein. */
/* Der Knopf traegt seit dem 21.08.2026 seine Zusagen direkt bei sich, statt sie
   irgendwo weiter unten stehen zu lassen. Der Zweifel entsteht in der halben
   Sekunde vor dem Klick, und genau dort muss die Antwort stehen — nicht acht
   Bildschirme tiefer im Abschnitt "Haeufige Fragen".

   Die Positionierung ist deshalb vom Knopf auf diesen Block gewandert: der Knopf
   steht jetzt statisch darin und behaelt sein transform fuer sich. Das war vorher
   auch ein stiller Konflikt, weil magnet() in main.js dem Knopf per GSAP ein
   eigenes transform setzt und damit das translateX(-50%) der Zentrierung
   ueberschrieben haette. */
/* Zusagen ueber dem Knopf, nicht darunter: Patricks Kopf sitzt exakt in der
   Bildmitte, und eine mittige Zeile unter dem Knopf lief bei 1200px quer ueber
   sein Gesicht (gemessen 21.08.2026, 617px Zeilenbreite gegen ~260px Kopfbreite).
   Schmaler machen half nicht, weil jede zentrierte Zeile denselben Punkt trifft.
   Ueber dem Knopf ist das Band zwischen Nav und Haaransatz frei. */
.hero__offer {
  position: absolute; top: clamp(96px, 11.2vh, 138px); left: 50%;
  transform: translateX(-50%); z-index: 3;
  display: flex; flex-direction: column; align-items: center; gap: 0.75rem;
  width: max-content; max-width: 92vw;
}
.hero__cta {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.6rem;
  min-height: 52px; padding: 1rem 2.4rem; border-radius: 999px;
  background: var(--sage-deep); color: var(--cream);
  font-size: 0.86rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase;
  white-space: nowrap;
  box-shadow: 0 14px 38px rgba(20, 44, 34, .34);
  transition: transform .35s cubic-bezier(.22,1,.36,1), background .35s ease;
}
.hero__cta i { font-style: normal; transition: transform .35s cubic-bezier(.22,1,.36,1); }
/* Grundfarbe ist seit dem 21.08.2026 selbst sage-deep, der alte Hover war damit
   wirkungslos. Umgedreht wie bei .cta__b: Gruen im Ruhezustand, dunkel im Hover. */
.hero__cta:hover { background: var(--slate-deep); }
.hero__cta:hover i { transform: translateX(4px); }
.hero__cta:focus-visible { outline: 3px solid var(--sage-deep); outline-offset: 3px; }
/* Die Zusagen unter dem Knopf. Bewusst nur Dinge, die auf der Seite ohnehin
   schon gelten und die Patrick jederzeit einhalten kann: kostenloses
   Erstgespraech, keine Bindung, Antwortzeit. Keine erfundene Garantie — was
   Patrick nicht zugesagt hat, steht hier nicht.
   Klein und ruhig gesetzt: die Zeile ist die Fussnote zum Knopf, nicht sein
   Konkurrent. Zwei gleich laute Elemente heben sich gegenseitig auf. */
.hero__trust {
  display: flex; flex-wrap: wrap; justify-content: center; align-items: center;
  gap: 0.35rem 1.5rem;
  font-size: 0.78rem; font-weight: 500; letter-spacing: 0.02em; color: var(--slate);
  text-align: center;
}
.hero__trust span { display: inline-flex; align-items: center; gap: 0.4rem; white-space: nowrap; }
/* Die Garantie ist die staerkste der vier Zusagen und die einzige, die Geld
   kostet, wenn jemand sie zieht. Sie darf deshalb etwas lauter sein als die
   drei Selbstverstaendlichkeiten daneben — aber nur etwas, sonst kippt die Zeile
   zum zweiten Knopf. "Erste Stunde" bleibt bewusst im Satz: ohne diese zwei
   Worte liest sich die Zusage als Geld-zurueck auf ALLES, und das ist weder
   zugesagt noch kalkuliert (Grundlage: 21-garantie-entwuerfe.md, Entwurf 1,
   Hoechstbetrag 119 € pro Fall). */
.hero__trust--star { font-weight: 700; color: var(--slate-deep); }
/* Der Haken sitzt als ::before, damit im Text selbst kein Zeichen steht, das ein
   Vorleseprogramm mitliest. */
.hero__trust span::before {
  content: ''; width: 13px; height: 13px; flex: none; border-radius: 50%;
  background: var(--sage-deep);
  -webkit-mask: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M20 6 9 17l-5-5' fill='none' stroke='%23000' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/11px no-repeat, linear-gradient(#000,#000);
  -webkit-mask-composite: xor;
  mask: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M20 6 9 17l-5-5' fill='none' stroke='%23000' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/11px no-repeat;
}

/* Unter 900px NICHT anzeigen: dort gibt es bereits den Termin-Dock (cta-dock),
   der ab dem ersten Bildschirm fix am unteren Rand steht — ein zweites,
   zentrales Buchungs-Element wuerde sich mit Dock, Sofort-Check-Leiste und
   Scroll-Hinweis stapeln, genau das Ueberladen, das an anderer Stelle heute
   erst behoben wurde. Desktop hat dagegen noch gar keinen sichtbaren
   Buchungs-Knopf ausser der kleinen Pille in der Nav-Ecke, dort schliesst
   dieser Knopf eine echte Luecke. */
/* Auf dem Handy bleibt der KNOPF weg, die ZUSAGEN bleiben. Der Knopf waere der
   dritte gleichzeitig sichtbare "Termin sichern" (cta-dock unten, nav-cta im
   Menue) und genau das war hier schon einmal das Problem. Die Zusagen dagegen
   sind das, was den Klick auf den Dock ueberhaupt leichter macht, und ohne sie
   haette ausgerechnet die Haelfte der Besucher mit dem meisten Zweifel gar keine
   Risikoabsicherung im ersten Bildschirm.
   Gemessen bei 390x844: Meta-Block endet bei 223px, Haaransatz beginnt bei 399px.
   Die Zeile sitzt in diesem freien Band. */
@media (max-width: 900px) {
  .hero__cta { display: none; }
  .hero__offer {
    top: clamp(236px, 28.5vh, 320px);
    width: min(88vw, 21rem);
  }
  /* Handy war schon vorher stimmig, die Desktop-Werte oben wuerden hier nur
     unnoetig breit laufen. */
  .hero__trust { font-size: 0.74rem; gap: 0.45rem 0.9rem; }
}

.hero__scroll {
  position: absolute; bottom: 3vh; left: 50%; transform: translateX(-50%); z-index: 3;
  font-size: 0.55rem; letter-spacing: 0.3em; text-transform: uppercase; color: var(--slate);
  display: flex; align-items: center; gap: 0.6rem;
}
.hero__scroll span { display: block; width: 40px; height: 1px; background: var(--slate); }

/* ================= responsive base ================= */
@media (max-width: 900px) {
  .hero__portrait-wrap { width: min(96vw, 74vh); }
  .nav__links { display: none; }
  .hero-props { display: none; }   /* no room beside the portrait on phones */
}

/* Phones: the two corner captions are pinned to opposite edges of the same line,
   which collides head-on once the viewport is narrow — they literally overlapped
   into "Patrick HolEin gesünderes Leben". Stack them instead: the name sits above
   the location, both left-aligned, and the whole block clears the scroll cue. */
/* Breakpoint is 900px, matching the one that widens the portrait above. At 640 the
   portrait already filled a 768 viewport while the captions were still corner-pinned,
   so "Patrick Holz" landed on his shirt. Stack them above him whenever the portrait
   grows. */
@media (max-width: 900px) {
  .hero__meta { position: static; bottom: auto; left: auto; right: auto; }
  .hero__meta--right { text-align: left; }
  /* Sit them ABOVE him, not below. His photo is pinned to the bottom of the hero,
     so a bottom-anchored caption lands on his shirt; the band between nav and head
     is empty background. */
  .hero__meta-stack {
    position: absolute; top: 13vh; left: 6vw; right: 6vw; bottom: auto; z-index: 3;
    display: flex; flex-direction: column; gap: 0.85rem;
  }
  .hero__meta span:first-child { font-size: clamp(1.6rem, 7.5vw, 2.2rem); }
  .hero__meta span + span { font-size: 0.72rem; letter-spacing: 0.16em; }

  /* TAP TARGETS — measured on a 390px phone: the primary CTA "Termin sichern" was
     only 25px tall, the brand 30px and the footer legal links 26px. A thumb needs
     ~44px (Apple HIG / WCAG 2.5.8). These are the site's conversion controls, so
     being hard to hit costs real bookings. */
  /* The desktop .nav__cta is hidden on phones; the real one lives in the drawer as
     .nav__mobile-cta and measured only 25px tall — the least tappable element on the
     page was the booking button. */
  .nav__mobile-cta { min-height: 48px; display: flex; align-items: center; justify-content: center; }
  .nav__brand { min-height: 44px; display: flex; flex-direction: column; justify-content: center; }
}
