/* ============================================================
   Pammi Thai Massage — Basis-Styles (Phase 1: Fundament)
   Layout & Sektionen folgen in Phase 2. Nur Tokens, keine Literale.
   ============================================================ */

/* ---- Fonts (lokal, latin-Subset) ---- */
@font-face {
  font-family: 'Playfair Display';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/playfair-display-400.woff2') format('woff2');
}
@font-face {
  font-family: 'Playfair Display';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/playfair-display-500.woff2') format('woff2');
}
@font-face {
  font-family: 'Playfair Display';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../fonts/playfair-display-600.woff2') format('woff2');
}
@font-face {
  font-family: 'Playfair Display';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/playfair-display-400-italic.woff2') format('woff2');
}
@font-face {
  font-family: 'Source Sans 3';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/source-sans-3-400.woff2') format('woff2');
}
@font-face {
  font-family: 'Source Sans 3';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../fonts/source-sans-3-600.woff2') format('woff2');
}
@font-face {
  font-family: 'Source Sans 3';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/source-sans-3-700.woff2') format('woff2');
}
/* Referenz-Paar für die Startseiten-Sektionen (lokal, latin-Subset) */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/inter-400.woff2') format('woff2');
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../fonts/inter-600.woff2') format('woff2');
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/inter-700.woff2') format('woff2');
}

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

html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
}

html, body { overflow-x: clip; }

body {
  margin: 0;
  background: var(--surface-base);
  color: var(--text-body);
  font-family: var(--font);
  font-size: var(--fs-body);
  line-height: 1.6;
}

/* height:auto gehoert zu den width/height-Attributen, die seit 13.09.2026
   an jedem <img> stehen (Platzhalter gegen Layout-Springen beim Laden):
   Greift max-width, muss die Hoehe mitschrumpfen, sonst bleibt das
   Attribut-Mass stehen und das Bild verzerrt. Regeln mit eigener Hoehe
   (z. B. .fan__photo img mit height:100%) gewinnen ueber die Klasse. */
img { max-width: 100%; height: auto; display: block; }

/* ---- Typo-Rollen (Display über EINE Sammelregel) ----
   Standard = helle Fläche: Überschriften Violett, Text warmes Grau.
   Dunkle Kontexte (.hero, .page-head, .site-footer)
   überschreiben gezielt weiter unten. */
h1, h2, .display {
  font-family: var(--font-display);
  font-style: normal;
  font-weight: 500;
  letter-spacing: var(--tracking-display);
  line-height: 1.12;
  color: var(--text-primary);
  margin: 0 0 var(--space-3);
}
h1 { font-size: var(--fs-display); }
h2 { font-size: var(--fs-h2); }
h3 {
  font-family: var(--font);
  font-weight: 600;
  font-size: var(--fs-h3);
  color: var(--text-body);        /* h3 ist Arbeits-, keine Schmuckebene */
  margin: 0 0 var(--space-2);
}
p { margin: 0 0 var(--space-2); max-width: var(--measure); }

a {
  color: var(--text-primary);
  text-decoration-color: var(--accent-secondary);
  /* Farbwechsel aller Textlinks gleiten statt zu springen (Politur
     07.09.2026); Buttons und Karten führen eigene, vollständige Listen. */
  transition: color var(--dur-fast) var(--ease),
              text-decoration-color var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease);
}
a:hover { color: var(--text-body); }

/* ---- Sektions-Gerüst: Abstand NUR über --section-y ---- */
.sec__inner {
  max-width: var(--maxw);
  margin-inline: auto;
  padding-block: var(--section-y);
  padding-inline: var(--gutter);
}

/* Flächenstufen (nur diese Klassen vergeben Hintergründe).
   Die dunkle Fläche gibt es im Seitenverlauf genau einmal: .surface-deep
   am Footer. Hero und Seitenköpfe sind Fotos mit Schleier, keine Fläche. */
.surface-deep { background: var(--surface-deep); }
.surface-white { background: var(--surface-card); }
.surface-raised { background: var(--surface-raised); }

/* ---- Header: transparente Leiste, Logo-Badge + zwei Glas-Ellipsen ---- */
.site-header {
  position: fixed;
  top: .9rem;
  left: var(--gutter);
  right: var(--gutter);
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: .35rem;
  border-radius: 999px;
  background: transparent;
  border: 1px solid transparent;   /* immer gesetzt: kein Layout-Sprung beim Scrollen */
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}
/* Beim Scrollen über helle Inhalte bekommt die Leiste eine Glas-Unterlage */
.site-header.is-scrolled {
  background: hsl(var(--surface-deep-hsl) / 72%);
  backdrop-filter: blur(18px) saturate(150%);
  -webkit-backdrop-filter: blur(18px) saturate(150%);
  border-color: var(--line-on-deep);
  box-shadow: var(--shadow-deep-lg);
}

/* Logo-Badge + Schriftzug */
.brand { display: flex; align-items: center; gap: .7rem; text-decoration: none; }
.brand__badge {
  /* Kreisform wie die übrigen Buttons/Pills. Die Logo-Vorlage ist im
     Querformat aufgenommen; object-fit:cover füllt den Kreis randlos und
     beschneidet dafür links und rechts das Stoffmuster — Kartusche,
     Elefanten und Schriftzug liegen mittig und bleiben vollständig. */
  width: 2.6rem;
  height: 2.6rem;
  flex-shrink: 0;
  border-radius: 50%;
  overflow: hidden;
  display: block;
  background: var(--surface-deep-soft);
  border: 1px solid var(--line-on-deep-strong);
  box-shadow: var(--shadow-deep-sm);
}
.brand__badge img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.brand__name {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 600;
  font-size: 1.2rem;
  color: var(--text-on-deep);
  text-shadow: var(--shadow-text);
  white-space: nowrap;
}
.brand:hover .brand__name { color: var(--text-on-deep-hi); }

/* Ellipse 1: Navigationslinks in der Glas-Pille */
.site-nav { display: flex; align-items: center; gap: .55rem; }
.nav-pill {
  position: relative;              /* Bezug für den wandernden Gleiter */
  display: flex;
  align-items: center;
  gap: .15rem;
  padding: .32rem;
  border-radius: 999px;
  background: hsl(var(--surface-deep-hsl) / 50%);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border: 1px solid var(--line-on-deep);
  box-shadow: var(--shadow-deep);
}
.nav-pill a {
  position: relative;
  z-index: 1;                      /* Beschriftung liegt über dem Gleiter */
  padding: .5em 1.05em;
  border-radius: 999px;
  text-decoration: none;
  font-weight: 600;
  font-size: .8rem;
  letter-spacing: .04em;
  white-space: nowrap;
  color: var(--text-on-deep);
  transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.nav-pill a:hover { color: var(--text-on-deep); background: var(--glass-hover); }
.nav-pill a[aria-current="page"] { color: var(--text-primary); background: var(--text-on-deep); }

/* ---- Wandernde Aktiv-Pille ----
   Die weiße Fläche des aktiven Tabs ist mit JS ein eigenes Element, das
   seinen Platz wechselt, statt am jeweiligen Link zu kleben. Grund: Der
   Platzwechsel hing zuvor allein an der View-Transitions-API des Browsers;
   überspringt der Browser die Transition (langsames Laden, Neuladen,
   Zurück-Taste, oder ein Browser ohne diese Technik), sprang die Fläche
   ohne Bewegung. Der Gleiter trägt die Bewegung jetzt selbst — die
   Transition darf ausfallen, ohne dass etwas fehlt.
   Ohne JS gibt es den Gleiter nicht; dann färbt wie bisher der Link selbst
   seine Fläche (Regel darüber, sie gilt weiter, solange html.fx-navthumb
   fehlt). Maße kommen ausschließlich aus JS-gemessenen Variablen, damit
   kein Innenabstand doppelt gepflegt werden muss. */
.nav-pill__thumb {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 0;
  width: var(--thumb-w, 0px);
  height: var(--thumb-h, 0px);
  border-radius: 999px;
  background: var(--text-on-deep);
  transform: translate(var(--thumb-x, 0px), var(--thumb-y, 0px));
  pointer-events: none;
}
/* Sobald der Gleiter da ist, gibt der Link seine eigene Fläche ab —
   sonst läge unter der wandernden Pille eine zweite, stehende. */
html.fx-navthumb .nav-pill a[aria-current="page"] { background: transparent; }
/* Bewegung nur, während ein Platzwechsel läuft (Klasse setzt JS). Beim
   Laden und bei Größenänderungen sitzt der Gleiter ohne Übergang. */
.nav-pill__thumb.is-gliding {
  transition: transform var(--dur-morph) var(--ease), width var(--dur-morph) var(--ease);
}

/* Ellipse 2: CTA-Pille mit Pfeil-Kreis */
.btn-cta {
  display: inline-flex;
  align-items: center;
  gap: .55rem;
  padding: .32rem .32rem .32rem 1.1rem;
  border-radius: 999px;
  background: var(--accent);
  color: var(--text-on-accent);
  font-weight: 700;
  font-size: .82rem;
  letter-spacing: .02em;
  text-decoration: none;
  white-space: nowrap;
  box-shadow: 0 8px 24px var(--glow-accent);
  transition: background var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease),
              transform var(--dur) var(--ease);
}
.btn-cta:hover { background: var(--accent-hover); color: var(--text-on-accent); box-shadow: 0 10px 28px var(--glow-accent); }
.btn-cta__icon {
  width: 2rem;
  height: 2rem;
  flex-shrink: 0;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--text-on-accent);
  color: var(--accent-light);
}
.btn-cta__icon svg { width: .95rem; height: .95rem; }

/* ---- Menü-Button + Panel: nur am Handy, nur mit JS ----
   Ohne JS bleibt die normale Tab-Leiste sichtbar und bedienbar. */
.nav-toggle {
  display: none;                 /* Desktop: nie sichtbar */
  width: 2.9rem;
  height: 2.9rem;
  flex-shrink: 0;
  padding: 0;
  border-radius: 50%;
  border: 1px solid var(--line-on-deep);
  background: var(--glass);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  color: var(--text-on-deep);
  cursor: pointer;
  place-items: center;
  box-shadow: var(--shadow-deep);
  transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
html.nav-open .nav-toggle { color: var(--text-on-deep-hi); border-color: var(--line-on-deep-strong); }
.nav-toggle__lines { position: relative; display: block; width: 1.15rem; height: 2px; }
.nav-toggle__lines::before,
.nav-toggle__lines::after {
  content: '';
  position: absolute;
  left: 0;
  width: 100%;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
  transition: transform .35s var(--ease), top .35s var(--ease);
}
.nav-toggle__lines::before { top: -4px; }
.nav-toggle__lines::after  { top: 4px; }
html.nav-open .nav-toggle__lines::before { top: 0; transform: rotate(45deg); }
html.nav-open .nav-toggle__lines::after  { top: 0; transform: rotate(-45deg); }

.nav-panel {
  display: none;                 /* Desktop + ohne JS */
  position: fixed;
  inset: 0;
  z-index: 40;                   /* unter dem Header → Schließen-Button bleibt oben */
  flex-direction: column;
  justify-content: space-between;
  gap: var(--space-4);
  padding: calc(var(--header-h) + 2.5rem) var(--gutter) calc(var(--gutter) + .5rem);
  /* Transluzentes Plum-Glas: die Seite schimmert dahinter durch */
  background: linear-gradient(165deg,
    hsl(var(--surface-deep-soft-hsl) / 82%) 0%,
    hsl(var(--surface-deep-hsl) / 94%) 85%);
  backdrop-filter: blur(26px) saturate(140%);
  -webkit-backdrop-filter: blur(26px) saturate(140%);
  /* Wischt von rechts herein statt einzublenden */
  transform: translateX(100%);
  visibility: hidden;
  transition: transform .55s var(--ease), visibility .55s var(--ease);
}
.nav-panel::before {
  /* dasselbe warme Glühen wie Hero und Footer (Wiedererkennung) */
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(70% 50% at 85% 8%, var(--glow-deep), transparent 65%);
}
html.nav-open .nav-panel { transform: translateX(0); visibility: visible; }
.nav-panel__nav { display: flex; flex-direction: column; position: relative; }
.nav-panel__nav a {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: clamp(1.75rem, 8vw, 2.5rem);
  line-height: 1.2;
  color: var(--text-on-deep);
  text-decoration: none;
  padding-block: clamp(.7rem, 2.6vw, 1.05rem);
  border-bottom: 1px solid var(--line-on-deep);
}
.nav-panel__nav a[aria-current="page"] { color: var(--text-on-deep-hi); }
/* Die Punkte reihen sich nacheinander ein, während das Panel hereinwischt */
html.nav-open .nav-panel__nav a,
html.nav-open .nav-panel__foot { animation: panel-link-in .55s var(--ease) both; }
.nav-panel__nav a:nth-child(1) { animation-delay: .18s; }
.nav-panel__nav a:nth-child(2) { animation-delay: .28s; }
.nav-panel__nav a:nth-child(3) { animation-delay: .38s; }
.nav-panel__foot { animation-delay: .48s; }
@keyframes panel-link-in {
  from { opacity: 0; transform: translateX(34px); }
  to   { opacity: 1; transform: none; }
}
.nav-panel__foot { position: relative; display: grid; gap: var(--space-3); }
.nav-panel__contact { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.nav-panel__contact .btn-gold {
  padding: .6em 1.4em;
  font-size: .92rem;
}
.nav-panel__phone {
  font-family: var(--font);
  font-weight: 700;
  font-size: 1.02rem;
  color: var(--text-on-deep);
  text-decoration: none;
  border-bottom: 1px solid var(--line-on-deep-strong);
  padding-bottom: .1em;
}
.nav-panel__phone:hover { color: var(--text-on-deep-hi); }
.nav-panel__foot p {
  margin: 0;
  color: var(--text-on-deep-muted);
  font-size: var(--fs-label);
  line-height: 1.6;
  letter-spacing: .02em;
}

/* ---- Footer-Gerüst ---- */
.brandmark {
  display: inline-block;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 600;
  font-size: 1.25rem;
  color: var(--text-on-deep);
  text-decoration: none;
  margin-bottom: var(--space-1);
}
.brandmark:hover { color: var(--text-on-deep-hi); }
.site-footer {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  border-top: 1px solid transparent;
  /* Haarlinie im Sekundär-Akzent: die einzige Akzentlinie am Footer */
  border-image: linear-gradient(to right, transparent, var(--accent-secondary) 20%, var(--accent-secondary) 80%, transparent) 1;
  /* Die EINE dunkle Fläche im Seitenverlauf (Farbsystem 07.09.2026):
     flach in --surface-deep, kein Verlauf. Alle Texte darauf tragen die
     on-deep-Rollen — auch Adresse und Kontakt (vorher erbte die Adresse
     den Fließtext-Ton der hellen Seite und stand dunkel auf dunkel). */
  background: var(--surface-deep);
  color: var(--text-on-deep);
}
.site-footer::before {
  /* Dezentes warmes Glühen, dasselbe Motiv wie im Hero (Wiedererkennung) */
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: radial-gradient(60% 90% at 12% 0%, var(--glow-deep), transparent 60%);
}
.site-footer .sec__inner { padding-block: var(--space-5); }
.footer-brand {
  display: flex;
  align-items: center;
  gap: .65rem;
  margin-bottom: var(--space-1);
  text-decoration: none;
}
.footer-brand .brand__badge { width: 2.3rem; height: 2.3rem; }
.preview-note {
  color: var(--text-on-deep-muted);
  font-size: var(--fs-label);
  letter-spacing: .02em;
  max-width: var(--measure);
}
/* Der Gestaltungs-Credit ist der einzige Link mitten in einem Satz. Die
   Footer-Links daneben stehen als Liste für sich und kommen ohne
   Unterstreichung aus; hier wäre der Link ohne sie von der Umgebung
   nicht zu unterscheiden — gleiche Farbe, gleiche Größe, kein Kontext.
   Deshalb eine zurückgenommene Linie: gedämpft, beim Hover so hell wie
   der Text. hsl()-Schreibweise statt color-mix wegen älterer iPads.
   Selektor mit .footer-grid davor, weil `.footer-grid a` (Farbe, keine
   Unterstreichung) dieselbe Spezifität hat und weiter unten steht — ohne
   den Zusatz gewinnt die Listenregel und der Link bliebe unsichtbar. */
.footer-grid .preview-note a {
  color: var(--text-on-deep);
  text-decoration: underline;
  text-decoration-color: hsl(var(--text-on-deep-hsl) / 45%);
  text-underline-offset: .18em;
  transition: color var(--dur-fast) var(--ease),
              text-decoration-color var(--dur-fast) var(--ease);
}
.footer-grid .preview-note a:hover { text-decoration-color: currentColor; }

/* Sichtbare Platzhalter-Kennzeichnung (Konvention laut BUILD-PHASES §1).
   Statt gestricheltem Rahmen (wirkte laut auf dunklem Grund) derselbe
   Gold-Balken wie bei den Öffnungszeiten (hours-table) — ruhiger, und
   konsistent zum übrigen Marker-Motiv der Seite. */
.placeholder {
  position: relative;
  border-radius: var(--radius-sm);
  background: hsl(var(--text-on-deep-hsl) / 7%);
  padding: var(--space-2) var(--space-2) var(--space-2) 1.15rem;
  color: var(--text-on-deep-muted);
  font-size: var(--fs-label);
  letter-spacing: .01em;
}
.placeholder::before {
  content: '';
  position: absolute;
  left: 0;
  top: .4rem;
  bottom: .4rem;
  width: 3px;
  border-radius: 2px;
  background: var(--line-on-deep-strong);
}

/* ============================================================
   Phase 2 — Aufteilung & Bildwelt
   ============================================================ */

/* ---- Buttons (Pill-Form wie Referenz, Gold-Familie) ---- */
.btn-gold,
.btn-ghost {
  display: inline-flex;
  align-items: center;
  gap: .5em;
  padding: .8em 1.6em;
  border-radius: 999px;
  font-weight: 700;
  font-size: var(--fs-body);
  text-decoration: none;
  transition: background var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease),
              box-shadow var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              transform var(--dur) var(--ease);
}
.btn-gold { background: var(--accent); color: var(--text-on-accent); }
/* Hover: Gold-Ring plus weicher Goldschein darunter — der Schein trägt den
   2-px-Lift (Motion-Region), ohne ihn läse sich das Anheben nicht. */
.btn-gold:hover {
  background: var(--accent-hover);
  color: var(--text-on-accent);
  box-shadow: 0 0 0 5px var(--glow-accent), 0 12px 28px var(--glow-accent);
}
.btn-ghost { border: 1px solid var(--line-on-deep-strong); color: var(--text-on-deep); }
.btn-ghost:hover { border-color: var(--text-on-deep); color: var(--text-on-deep-hi); }

/* ---- Hero (nach Wellnest-Referenz: Bild bis zum Viewport-Ende, Fade erst
   ganz unten, Inhalt unten verankert, warme Lichtstimmung) ---- */
.hero {
  position: relative;
  /* Erst vh, dann svh: Safari kennt svh/lvh erst ab 15.4. Fehlt die Einheit,
     ist die GANZE Deklaration ungültig und der Hero hat gar keine
     Mindesthöhe mehr — gemessen am 08.09.2026 fiel er dadurch von 1180 auf
     452 px (Tablet hoch), von 768 auf 458 (Tablet quer) und von 844 auf
     413 (Telefon): aus dem bildschirmfüllenden Auftakt wurde ein Streifen.
     100vh ist der richtige Ersatz, weil vh auf altem iOS ohnehin die große
     Viewport-Höhe meint — also das, was weiter unten der Mobile-Block mit
     100lvh ausdrücklich haben will. */
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  align-items: stretch;
  overflow: hidden;
  isolation: isolate;
}
.hero__bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 65% 40%;
  z-index: -3;
  /* warmes Kerzenlicht-Grading statt kühlem Grau */
  filter: sepia(.14) saturate(1.12) brightness(.97);
}
.hero__scrim {
  position: absolute;
  inset: 0;
  z-index: -2;
  background:
    /* warmes Licht oben rechts */
    radial-gradient(110% 75% at 82% 12%, var(--hero-light), transparent 62%),
    /* Lesbarkeits-Scrim links, violett getönt */
    linear-gradient(100deg,
      hsl(var(--surface-deep-hsl) / 80%) 6%,
      hsl(var(--surface-deep-hsl) / 38%) 46%,
      hsl(var(--surface-deep-hsl) / 12%) 100%);
}

/* Übergang in die helle Seite als eigene Ebene.
   Vielstufige, geglättete Alpha-Kurve statt Zweistufen-Verlauf: ein
   `transparent`-Stopp würde über transparentes Schwarz interpolieren und
   eine sichtbare Kante erzeugen. Alle Stopps führen dieselbe Seitenfarbe
   (--surface-base, per color-mix mit Deckkraft), damit der Auslauf in
   jeder Variante nahtlos in die Grundfläche übergeht. */
.hero::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(to top,
    hsl(var(--surface-base-hsl) / 100%)     0%,
    hsl(var(--surface-base-hsl) / 99%)   2%,
    hsl(var(--surface-base-hsl) / 96%)   4%,
    hsl(var(--surface-base-hsl) / 91%)   6%,
    hsl(var(--surface-base-hsl) / 84%)   8%,
    hsl(var(--surface-base-hsl) / 76%)  10%,
    hsl(var(--surface-base-hsl) / 66%)  12.5%,
    hsl(var(--surface-base-hsl) / 56%)  15%,
    hsl(var(--surface-base-hsl) / 46%)  17.5%,
    hsl(var(--surface-base-hsl) / 36%)  20%,
    hsl(var(--surface-base-hsl) / 27%)  22.5%,
    hsl(var(--surface-base-hsl) / 19%)  25%,
    hsl(var(--surface-base-hsl) / 13%)  27.5%,
    hsl(var(--surface-base-hsl) / 8%)  30%,
    hsl(var(--surface-base-hsl) / 4.5%) 32.5%,
    hsl(var(--surface-base-hsl) / 2%)  35%,
    hsl(var(--surface-base-hsl) / 0.8%) 37.5%,
    hsl(var(--surface-base-hsl) / 0%)    40%);
}
.hero__inner {
  width: 100%;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;   /* Inhalt sitzt unten wie in der Referenz */
  /* bewusst kein --maxw: Text am Seitenrand */
  padding-inline: var(--gutter);
  /* Inhalt sitzt knapp über der Bildschirmkante statt mit großem Puffer
     darüber — deckt so weniger vom Bild ab. */
  padding-block: calc(var(--header-h) + var(--space-5)) clamp(.75rem, 2vh, 1.5rem);
  /* Eigener Stapelkontext, damit die Lesbarkeits-Wäsche unten ÜBER dem
     Creme-Auslauf (.hero::after, z-index -1) liegen kann. */
  position: relative;
  z-index: 0;
}

/* ---- Lesbarkeits-Wäsche hinter dem Hero-Textblock ----
   Gemessen am 07.09.2026 an den echten Glyphen-Positionen: ohne diese
   Schicht steht der Text auf dem Creme-Auslauf und erreicht nur
   1,15:1 (Gold-Akzent), 1,29–1,89:1 (Absatz) und 1,23:1 (Ghost-Button).
   Eine reine Farbänderung genügt nicht: Der Grund hinter dem Absatz
   reicht von Luminanz .14 bis .41; in dieser Spanne erreicht KEINE
   einzelne Textfarbe 4,5:1 (Optimum ~3,7:1 bei reinem Schwarz).
   Deshalb wird der Grund selbst abgedunkelt.
   Die Schicht liegt bewusst über dem Creme-Auslauf und läuft nach unten
   aus, damit Bewertungs- und Hinweiszeile auf Creme stehen bleiben und
   der Seitenübergang unverändert wirkt. Nach rechts läuft sie aus, damit
   das Foto rechts frei bleibt. */
/* @supports-Riegel: Ohne Masken-Unterstützung würde die Wäsche den gesamten
   Hero abdecken — auch die Bewertungszeile, die dann dunkel auf dunkel stünde.
   Fehlt die Maske, entfällt die Schicht lieber ganz. */
@supports (mask-image: none) or (-webkit-mask-image: none) {
.hero__inner::before {
  /* Alle vier Kanten als Variablen, weil der Textblock auf schmalen Fenstern
     fast die volle Breite und einen deutlich größeren Anteil der Hero-Höhe
     einnimmt — dieselben Prozentwerte greifen dort sonst zu kurz (gemessen:
     Headline nur 2,75:1 bei 375px). */
  --wash-x-mid: 38%;      /* bis hierhin trägt die Wäsche fast voll */
  --wash-x-end: 82%;      /* ab hier bleibt das Foto frei */
  --wash-bottom-solid: 21%;   /* ab hier volle Wäsche, darunter die Kurve */
  --wash-top-solid: 46%;
  --wash-top-end: 58%;
  --wash-top-span: 12%;       /* --wash-top-end minus --wash-top-solid */
  /* Deckkraft des Schleiers am linken Rand und an --wash-x-mid. Schmale
     Fenster ziehen sie an (siehe unten), weil der Absatz dort über hellere
     Bildpartien läuft. 07.09.2026 von 82/62 auf 68/48 gesenkt: Mit der
     stetigen Kurve trug der Absatz 6,7:1 und damit reichlich Reserve über
     der 4,5er-Schwelle — die ging in einen blasseren Übergang. */
  --wash-solid: 68%;
  --wash-mid: 48%;
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(to right,
    hsl(var(--surface-deep-hsl) / var(--wash-solid)) 0%,
    hsl(var(--surface-deep-hsl) / var(--wash-mid)) var(--wash-x-mid),
    transparent var(--wash-x-end));
  /* Beide Kanten als echte Smoothstep-Kurve t²·(3−2t) über neun Stopps.
     Entscheidend ist nicht die Zahl der Stopps, sondern dass die Steigung
     an beiden Enden auf null ausläuft: Die frühere Kurve traf bei
     --wash-bottom-solid mit voller Steigung auf die konstante Zone und
     erzeugte dort einen Knick. Ein Knick ist kein Helligkeitssprung, das
     Auge liest ihn trotzdem als Linie — gemessen am 07.09.2026 wechselte
     die Steigung dort von 0,0002 auf 0,005 Luminanz pro Zeile, und genau
     auf dieser Höhe steht die Subline. Jetzt ist die Steigung überall
     stetig; die Deckung an der Subline bleibt dabei gleich (78 % bei
     15 % Höhe gegenüber 76 % vorher). */
  -webkit-mask-image: linear-gradient(to top,
    transparent 0%,
    hsl(var(--mask-solid-hsl) / 3%)   calc(var(--wash-bottom-solid) * .1),
    hsl(var(--mask-solid-hsl) / 10%)  calc(var(--wash-bottom-solid) * .2),
    hsl(var(--mask-solid-hsl) / 22%)  calc(var(--wash-bottom-solid) * .3),
    hsl(var(--mask-solid-hsl) / 35%)  calc(var(--wash-bottom-solid) * .4),
    hsl(var(--mask-solid-hsl) / 50%)  calc(var(--wash-bottom-solid) * .5),
    hsl(var(--mask-solid-hsl) / 65%)  calc(var(--wash-bottom-solid) * .6),
    hsl(var(--mask-solid-hsl) / 78%)  calc(var(--wash-bottom-solid) * .7),
    hsl(var(--mask-solid-hsl) / 90%)  calc(var(--wash-bottom-solid) * .8),
    hsl(var(--mask-solid-hsl) / 97%)  calc(var(--wash-bottom-solid) * .9),
    var(--mask-solid) var(--wash-bottom-solid),
    var(--mask-solid) var(--wash-top-solid),
    hsl(var(--mask-solid-hsl) / 93%) calc(var(--wash-top-solid) + var(--wash-top-span) * .167),
    hsl(var(--mask-solid-hsl) / 74%) calc(var(--wash-top-solid) + var(--wash-top-span) * .333),
    hsl(var(--mask-solid-hsl) / 50%) calc(var(--wash-top-solid) + var(--wash-top-span) * .5),
    hsl(var(--mask-solid-hsl) / 26%) calc(var(--wash-top-solid) + var(--wash-top-span) * .667),
    hsl(var(--mask-solid-hsl) / 7%)  calc(var(--wash-top-solid) + var(--wash-top-span) * .833),
    transparent var(--wash-top-end));
          mask-image: linear-gradient(to top,
    transparent 0%,
    hsl(var(--mask-solid-hsl) / 3%)   calc(var(--wash-bottom-solid) * .1),
    hsl(var(--mask-solid-hsl) / 10%)  calc(var(--wash-bottom-solid) * .2),
    hsl(var(--mask-solid-hsl) / 22%)  calc(var(--wash-bottom-solid) * .3),
    hsl(var(--mask-solid-hsl) / 35%)  calc(var(--wash-bottom-solid) * .4),
    hsl(var(--mask-solid-hsl) / 50%)  calc(var(--wash-bottom-solid) * .5),
    hsl(var(--mask-solid-hsl) / 65%)  calc(var(--wash-bottom-solid) * .6),
    hsl(var(--mask-solid-hsl) / 78%)  calc(var(--wash-bottom-solid) * .7),
    hsl(var(--mask-solid-hsl) / 90%)  calc(var(--wash-bottom-solid) * .8),
    hsl(var(--mask-solid-hsl) / 97%)  calc(var(--wash-bottom-solid) * .9),
    var(--mask-solid) var(--wash-bottom-solid),
    var(--mask-solid) var(--wash-top-solid),
    hsl(var(--mask-solid-hsl) / 93%) calc(var(--wash-top-solid) + var(--wash-top-span) * .167),
    hsl(var(--mask-solid-hsl) / 74%) calc(var(--wash-top-solid) + var(--wash-top-span) * .333),
    hsl(var(--mask-solid-hsl) / 50%) calc(var(--wash-top-solid) + var(--wash-top-span) * .5),
    hsl(var(--mask-solid-hsl) / 26%) calc(var(--wash-top-solid) + var(--wash-top-span) * .667),
    hsl(var(--mask-solid-hsl) / 7%)  calc(var(--wash-top-solid) + var(--wash-top-span) * .833),
    transparent var(--wash-top-end));
}
/* Mittleres Band: einspaltiges Layout wie am Handy, aber der Ghost-Button ist
   hier noch sichtbar und braucht dunklen Grund. Die untere Kante liegt
   deshalb knapp über der Bewertungszeile statt darüber hinaus. */
@media (min-width: 641px) and (max-width: 819px) {
  .hero__inner::before {
    --wash-x-mid: 70%;
    --wash-x-end: 105%;
    --wash-bottom-solid: 19%;
    --wash-top-solid: 42%;
    --wash-top-end: 54%;
    --wash-top-span: 12%;
  }
}
/* Schmale Fenster: Wäsche über die volle Breite und höher hinauf. Die untere
   Kante rutscht mit, damit Bewertung und Hinweiszeile weiter auf Creme
   stehen; der Gold-Button dazwischen bringt seine eigene Fläche mit und
   braucht die Wäsche nicht. */
@media (max-width: 640px) {
  .hero__inner::before {
    /* Kräftiger als am Desktop: an den echten Glyphen gemessen
       (07.09.2026) kam der Absatz bei 375 px sonst auf 4,1–4,4:1, sobald
       die dunkle Fläche etwas wärmer und heller ausfiel. Die Reserve
       bleibt bewusst stehen, damit ein Motivwechsel im Hero sie nicht
       sofort aufbraucht. */
    /* 78 % → 88 %: Auch mit voller Deckung bis zur Unterkante blieb der
       Grund unter der Bewertungszeile ein Mittelton (119,109,116) — die
       kleine Zusatzzeile kam dort nur auf 2,67:1. */
    /* 08.09.2026: 88/78 → 86/76, so leicht, wie es die Zusatzzeile
       (gedämpfter Ton, 4,5:1 nötig) und die Goldzeile zulassen — beide
       stehen danach noch bei rund 4,8:1, der weiße Text ab 6,9:1. */
    --wash-solid: 86%;
    /* Die Wäsche läuft auch nach RECHTS aus. Die Bewertungszeile reicht bis
       82 % der Breite und lag damit im Mittelbereich — dort half die
       stärkere Grundeckung nichts, der schlechteste Punkt blieb exakt
       gleich. Deshalb auch den Mittelwert anheben. */
    --wash-mid: 76%;
    --wash-x-mid: 84%;
    --wash-x-end: 128%;
    /* 29 % → 11 %: Die volle Deckung begann so weit über der Unterkante,
       dass die Bewertungszeile in der Auslaufzone lag und nur auf halber
       Waschstärke stand. Gemessen am 07.09.2026 kam sie dort auf 3,65:1
       (nach der Hero-Kürzung sogar 2,32:1) — beides unter den 4,5:1 für
       Normaltext. Jetzt reicht die Deckung bis fast an die Kante, die
       Zeile steht auf verlässlichem Grund und trägt die hellen
       Textrollen (siehe Mobile-Block weiter unten). */
    /* Seit der Zweiteilung des Heros (Überschrift oben, Handlungsteil
       unten) braucht die Wäsche BEIDE Enden, nicht mehr nur ein Band in
       der unteren Hälfte. Gemessen: Die Bewertungszeile sitzt bei 4–7 %
       Höhe und lag damit unter dem alten Dichtebeginn von 11 % (1,04:1),
       die Überschrift bei 70–83 % und damit in der oberen Auslaufzone
       (2,92:1). Jetzt ist der Streifen dicht von 3 % bis 86 %; die
       verbleibenden Ausläufe liegen dort, wo ohnehin kein Text steht. */
    /* Nach der Rückkehr auf die volle Hero-Höhe verschieben sich alle
       Prozentmarken erneut: Die Überschrift sitzt jetzt bei rund 77–87 %,
       die Bewertungszeile bei 3 %. Statt die Marken bei jeder
       Höhenänderung nachzujustieren, ist der Streifen am Telefon nun
       praktisch durchgehend dicht — er lebt ohnehin von seinem
       HORIZONTALEN Verlauf nach rechts, die senkrechten Ausläufe waren
       nur für die frühere Anordnung nötig. Übrig bleibt ein schmaler
       Auslauf unten, damit die Kante nicht auf den Creme-Übergang trifft. */
    /* 08.09.2026 (Matteo: „zu lila, so natürlich wie möglich, nur leicht
       lila unten"): Die Wäsche deckt nicht mehr den ganzen Hero, sondern
       nur den Textblock unten. Von unten dicht bis 26 % (Subline, Button,
       Bewertung), darüber die Smoothstep-Kurve bis 54 % — die Überschrift
       (23–35 % von unten) steht damit noch auf 67–95 % Deckung, das Foto
       oberhalb der Steine bleibt frei. Die Unterkante läuft nicht mehr in
       2 % Höhe aus, sondern über den Streifen unterhalb des kleinen
       Viewports (Browserleiste ausgefahren) plus 1rem: Dort liegt kein
       Text, dort darf der Creme-Übergang (.hero::after) durchscheinen. */
    /* Ohne lvh/svh (Safari vor 15.4) gibt es keinen Streifen unterhalb des
       kleinen Viewports — der Wert ist dort schlicht 1rem. Er MUSS hier
       stehen und die Fassung mit den neuen Einheiten weiter unten in einem
       @supports: Eine Custom Property nimmt jeden Wert klaglos an und
       scheitert erst beim Einsetzen. Stünde nur die calc-Fassung da, wäre
       damit die ganze mask-image ungültig — und die Wäsche läge ohne Maske
       über dem gesamten Hero statt nur über dem Textblock. */
    --wash-bottom-solid: 1rem;
    --wash-top-solid: 26%;
    --wash-top-end: 54%;
    --wash-top-span: 28%;
  }
  @supports (height: 1svh) {
    .hero__inner::before { --wash-bottom-solid: calc(100lvh - 100svh + 1rem); }
  }
}
} /* Ende @supports: Lesbarkeits-Wäsche */
.hero__title {
  /* Dieselbe Display-Serif wie alle Überschriften (Vorgabe 07.09.2026).
     600 statt der früheren 800 einer Grotesk: Playfair trägt bei gleicher
     optischer Wucht weniger Gewicht, 800 würde die Haarstriche zudrücken. */
  font-family: var(--font-display);
  font-style: normal;
  font-weight: 600;
  letter-spacing: -.005em;
  line-height: 1.12;
  font-size: clamp(2rem, 3.4vw, 3.6rem);
  color: var(--text-on-deep);
  margin-bottom: var(--space-2);
}
.hero__title em {
  /* Gleiche Schrift und Lage wie der Rest der Zeile, die Goldfolie ist
     das Signature-Element des Heros. Sie ist damit die
     einzige Stelle, an der der Akzent nicht an einem CTA, einem Stern oder
     einer Haarlinie sitzt — bewusste Ausnahme, weil das Wort die Marke
     trägt. Kontrast über dem Schleier gemessen (Großtext, Schwelle 3:1):
     siehe docs/CHANGELOG.md. */
  font-style: normal;
  font-weight: inherit;
  background: linear-gradient(105deg, var(--accent) 0%, var(--accent-light) 45%, var(--accent) 90%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}
/* Die Headline bricht an den Satzpunkten, nie mitten im Teilsatz — deshalb
   je Teilsatz ein Block statt eines <br>. Bewusst auf allen Breiten: bei
   375px passt jeder Teilsatz in eine Zeile, der Umbruch stimmt also überall.
   width: fit-content hält den Gold-Verlauf des Akzents auf Textbreite,
   sonst spannte er über die ganze Zeilenbreite und verlöre seinen Glanz. */
.hero-line { display: block; width: fit-content; }
/* Ab sehr breiten Fenstern zweizeilig: die ersten beiden Teilsätze teilen
   sich eine Zeile, der Gold-Akzent behält seine eigene. Drei Zeilen wären
   hier unnötig hoch. */
@media (min-width: 1500px) {
  .hero__title span.hero-line { display: inline; }
}
.hero__sub {
  /* Volle on-deep-Helligkeit statt des Nebentext-Tons: An den echten
     Glyphen gemessen (07.09.2026) erreichte der gedämpfte Ton über dem
     Foto nur 3,0–3,7:1 (p95); mit --text-on-deep liegt der Absatz auf
     allen Breiten über 4,5:1. */
  color: var(--text-on-deep);
  font-size: .95rem;
  max-width: 40ch;
  margin-bottom: var(--space-3);
}
.hero__cta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin-bottom: clamp(.85rem, 2.2vw, 1.5rem); }
.hero__cta .btn-gold { font-size: .95rem; padding: .7em 1.5em; }

/* Sekundär-Button wie Bild-Referenz: Outline-Pill + angehängter
   Pfeil-Kreis, wirkt als ein zusammengesetztes grafisches Element. */
.hero__cta .btn-ghost {
  display: inline-flex;
  align-items: center;
  gap: .7rem;
  font-size: .95rem;
  padding: .3rem .3rem .3rem 1.4rem;
  border-color: var(--line-on-deep-strong);
  /* Eigene dunkle Glasfläche statt bloßer Outline: Der Button sitzt genau
     dort, wo die Lesbarkeits-Wäsche nach unten ausläuft, und erreichte auf
     1920px sonst nur 3,64:1. Mit eigener Fläche trägt er seinen Kontrast
     selbst — unabhängig davon, was das Foto an dieser Stelle zeigt.
     07.09.2026 von 55 auf 64 % angehoben: Bei 55 % lag der Text auf einer
     etwas helleren dunklen Fläche noch bei 4,3:1 (hellstes Glyphen-Pixel,
     1440 px). Aktuell trägt er 5,8:1. */
  background: hsl(var(--surface-deep-hsl) / 64%);
}
.hero__cta .btn-ghost:hover {
  border-color: var(--text-on-deep);
  background: hsl(var(--surface-deep-hsl) / 76%);
}
.btn-ghost__icon {
  width: 2.15rem;
  height: 2.15rem;
  flex-shrink: 0;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--surface-base);
  color: var(--text-primary);
}
.btn-ghost__icon svg { width: .85rem; height: .85rem; }

/* Grundlinie unten: Bewertung sitzt an der Fußzeile des Hero-Inhalts */
.hero__foot {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.rating { display: flex; align-items: center; flex-wrap: wrap; gap: .6em; }
/* Im Hero sitzt die Bewertung auf dem hellen Auslauf → dunkle Schrift (wie Referenz). */
.hero .rating { font-size: .95rem; align-self: flex-start; }

/* Bewertungsblock: Sternzeile führt zum Google-Profil, auf dem Creme-Auslauf. */
.hero__rating {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: .4rem;
}
.rating--link { text-decoration: none; color: inherit; }
.rating--link:hover .rating__value {
  text-decoration: underline;
  text-underline-offset: .22em;
}
/* Sitzt im Hero über dem Foto — der Ring kommt deshalb aus --focus-ring und
   ist dort das warme Weiß. Eigene Regel nur wegen Radius und größerem Abstand. */
.rating--link:focus-visible {
  outline: 2px solid var(--focus-ring, var(--text-primary));
  outline-offset: 4px;
  border-radius: var(--radius-sm);
}

.hero .rating__stars { color: var(--accent-secondary); }
.hero .rating__value { color: var(--text-primary); }
.hero .rating__note { color: var(--text-body); }
.rating__stars { display: inline-flex; gap: .15em; color: var(--accent-secondary); }
.rating__stars svg { width: 1.05em; height: 1.05em; fill: currentColor; }
.rating__value { font-weight: 700; color: var(--text-on-deep); }
.rating__note { color: var(--text-on-deep-muted); font-size: var(--fs-label); letter-spacing: .03em; }

/* ============================================================
   Startseiten-Sektionen nach Referenz (14.08.2026)
   Maße und Bildzuschnitte aus den Referenz-Screenshots übernommen.
   Die Überschriften tragen seit 07.09.2026 dieselbe Display-Serif wie
   der Rest der Seite; die Grotesk (Inter) blieb für Fließtext, Zitate
   und Meta-Zeilen dieser Sektionen.
   ============================================================ */

.surface-veil { background: var(--surface-raised); }

/* Editorial-Überschriften: Sans REGULAR + Italic-Serif-Akzent (Referenz
   setzt nur die S1-Überschrift fett → --bold-Variante). Farbe erbt,
   Gold bleibt auf hell verboten. */
.h-ref {
  font-family: var(--font-display);
  font-style: normal;
  font-weight: 400;
  letter-spacing: -.005em;
  line-height: 1.28;
  /* Fast-Schwarz mit Violett-Kern wie das Near-Black der Referenz —
     ruhiger/edler als die helle Plum-Stufe der Unterseiten-Headlines. */
  color: var(--text-primary);
  margin: 0 0 var(--space-3);
}
.h-ref em {
  /* Akzentwort in der Kursiven derselben Familie: der Kontrast kommt jetzt
     aus der Schriftlage, nicht mehr aus einem zweiten Schriftschnitt. */
  font-style: italic;
  font-weight: 400;
  letter-spacing: 0;
  color: inherit;
}
/* Durchgehend aufrecht (Vorgabe 07.09.2026): Die Referenzbilder setzen
   solche Sektions-Überschriften in einer Lage. Bei diesen beiden brach das
   kursive Akzentwort die Zeile sichtbar auf; „Unsere Behandlungen" und
   „Ein Blick ins Studio." behalten den Wechsel bewusst. Das <em> bleibt
   im Markup — die Betonung gehört zum Satz, sie wird hier nur nicht mehr
   über die Schriftlage ausgespielt. */
.h-ref--plain em { font-style: normal; }
.h-ref--bold {
  font-weight: 600;
  letter-spacing: -.01em;
  line-height: 1.05;
}

/* ---- S1: Behandlungs-Ovale (Kapselform oben+unten rund) ---- */
.sec-treatments .sec__head {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-3);
  margin-bottom: clamp(2rem, 4vw, 3.5rem);
}
.sec-treatments h2 { font-size: clamp(2.4rem, 5.2vw, 4.6rem); margin: 0; }
.oval-row {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(1rem, 2.2vw, 1.9rem);
}
.oval-card { display: block; text-decoration: none; transition: opacity var(--dur) var(--ease); }
/* Drift-Ebene (gegenläufiges Schweben beim Scrollen) getrennt vom
   Reveal-Element — ein Element, EIN Transform-Besitzer. */
.oval-card__float { display: block; }
/* Galerie-Fokus: die Nachbarn treten beim Hover leicht zurück */
@media (hover: hover) and (pointer: fine) {
  .oval-row:hover .oval-card:not(:hover) { opacity: .72; }
}
.oval-card__media {
  position: relative;
  display: block;
  overflow: hidden;
  border-radius: 999px;
  aspect-ratio: 41 / 60;
  isolation: isolate;
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.oval-card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform .6s var(--ease);
}
/* Feiner Gold-Ring legt sich beim Hover in die Kapsel */
.oval-card__media::before {
  content: '';
  position: absolute;
  inset: 10px;
  z-index: 1;
  border-radius: 999px;
  border: 1px solid hsl(var(--text-on-deep-hsl) / 85%);
  opacity: 0;
  transform: scale(.96);
  transition: opacity .45s var(--ease), transform .45s var(--ease);
  pointer-events: none;
}
/* Licht-Streifen beim Hover (Muster der früheren Teaser-Karten) */
.oval-card__media::after {
  content: '';
  position: absolute;
  top: 0; bottom: 0;
  left: -45%;
  width: 38%;
  z-index: 1;
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(105deg,
    transparent 0%, var(--sheen-soft) 42%,
    var(--sheen) 50%, var(--sheen-soft) 58%, transparent 100%);
  transform: skewX(-14deg);
}
.oval-card__label {
  display: block;
  margin-top: .9rem;
  text-align: center;
  font-family: var(--font-ref);
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: .01em;
  color: var(--text-body);
  transition: color var(--dur-fast) var(--ease);
}
@media (hover: hover) and (pointer: fine) {
  .oval-card:hover .oval-card__media { transform: translateY(-4px); box-shadow: var(--shadow-card-hover); }
  .oval-card:hover .oval-card__media img { transform: scale(1.05); }
  .oval-card:hover .oval-card__media::before { opacity: .9; transform: scale(1); }
  .oval-card:hover .oval-card__media::after { animation: card-shine .95s var(--ease); }
  .oval-card:hover .oval-card__label { color: var(--text-primary); }
}
@keyframes card-shine {
  0%   { opacity: 0; transform: skewX(-14deg) translateX(-40%); }
  14%  { opacity: 1; }
  100% { opacity: 0; transform: skewX(-14deg) translateX(400%); }
}
.sec-treatments .link-gold { display: inline-block; margin-top: clamp(1.5rem, 3vw, 2.5rem); }
.carousel-arrow { display: none; }   /* Desktop: alle vier Karten sichtbar */

@media (max-width: 760px) {
  /* Karussell: aktive Karte mittig, links und rechts je ein angeschnittenes
     Nachbarbild. Der Innenabstand sorgt dafür, dass auch erste und letzte
     Karte mittig einrasten; main.js startet auf Karte 2, damit von Anfang
     an auf beiden Seiten ein halbes Bild zu sehen ist. */
  .oval-carousel { position: relative; }
  .oval-row {
    --card-w: 56vw;
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    gap: .9rem;
    margin-inline: calc(-1 * var(--gutter));
    padding-inline: calc((100vw - var(--card-w)) / 2);
    padding-bottom: .5rem;
    scrollbar-width: none;
  }
  .oval-row::-webkit-scrollbar { display: none; }
  .oval-card { flex: 0 0 var(--card-w); scroll-snap-align: center; }

  /* Pfeile liegen auf den angeschnittenen Nachbarbildern, auf Bildmitte.
     Höhe der Kapsel = Breite × 60/41 (aspect-ratio), also halbe Höhe für
     die vertikale Mitte. */
  .carousel-arrow {
    --card-w: 56vw;
    position: absolute;
    top: calc(var(--card-w) * 60 / 41 / 2);
    z-index: 3;
    display: grid;
    place-items: center;
    width: 2.75rem;                /* 44px — Mindestmaß fürs Tippen */
    height: 2.75rem;
    padding: 0;
    border-radius: 50%;
    border: 1px solid var(--line-on-deep-strong);
    background: var(--glass);
    backdrop-filter: blur(10px) saturate(130%);
    -webkit-backdrop-filter: blur(10px) saturate(130%);
    color: var(--text-on-deep);
    cursor: pointer;
    transform: translateY(-50%);   /* Basis, falls keine Animation läuft */
  }
  .carousel-arrow svg { width: 1.1rem; height: 1.1rem; }
  .carousel-arrow--l { left: -.25rem; animation: arrow-nudge-l 2.4s var(--ease) infinite; }
  .carousel-arrow--r { right: -.25rem; animation: arrow-nudge-r 2.4s var(--ease) infinite; }
  .carousel-arrow:active { background: var(--glass-strong); }
}
/* Die Pfeile rutschen rhythmisch nach außen — die Geste, die gemeint ist.
   translateY(-50%) muss in jedem Keyframe mitlaufen. */
@keyframes arrow-nudge-l {
  0%, 100% { transform: translate(0, -50%);     opacity: .75; }
  45%      { transform: translate(-6px, -50%);  opacity: 1; }
}
@keyframes arrow-nudge-r {
  0%, 100% { transform: translate(0, -50%);     opacity: .75; }
  45%      { transform: translate(6px, -50%);   opacity: 1; }
}

/* ---- S2: Studio-Fächer — sieben Kacheln, absolut um die Mitte gelegt.
   Pro Kachel nur Variablen (Versatz --fx/--fy als Anteil der Kachel,
   Größe --fs, Drehung --fr, Ebene --fz); Geometrie rechnet das Layout.
   Grundzustand = Endzustand, damit ohne JS alles sichtbar ist. ---- */
.fan { text-align: center; }
.fan__title {
  /* Eigene, größere Stufe statt --fs-h2-sec (07.09.2026, Wunsch Matteo):
     Der Titel wirkte neben dem vergrößerten Fächer zu klein. Eigenes
     Token statt hier direkt zu skalieren, weil --fs-h2-sec auch die
     Stimmen-Überschrift trägt und die unverändert bleiben soll. */
  font-size: var(--fs-h2-lg);
  margin: 0 auto;
  max-width: 18ch;
}
.fan__stage {
  /* Bezugsgröße einer Kachel. 07.09.2026 nochmals um 10 % angehoben
     (vorher clamp(7.5rem, 18.7vw, 16.5rem), davor 17vw/15rem): Fächer und
     Titel wirkten gegenüber den übrigen Sektionen zu klein. Bewusst diese
     Variable statt der --fs-Faktoren, weil sie Kachelgröße UND Versatz
     gemeinsam skaliert — die Überlappungen bleiben im selben Verhältnis.
     Nur die --fs zu erhöhen hätte die vierte Kachel fast vollständig
     hinter der fünften verschwinden lassen.
     Die Untergrenze bleibt bei 7.5rem: Am Handy steht der Fächer schon
     randnah (13 px links), dort ist kein Platz zu gewinnen — mit 8.25rem
     blieb links nur 1 px. */
  --fan-tile: clamp(7.5rem, 20.6vw, 18.15rem);
  --fan-spread: 1;                           /* Fächerbreite, am Handy enger */
  position: relative;
  /* Höhe folgt der tiefsten Kachel: --fy + --fs ergibt bei Kachel 3 mit 1.12
     den größten Wert, der Rest ist Luft zum Sektionsabstand. */
  height: calc(var(--fan-tile) * 1.2);
  margin-top: clamp(1.75rem, 4vw, 3.25rem);
}
.fan__photo {
  --fx: 0; --fy: 0; --fs: 1; --fr: 0deg; --fz: 1;
  position: absolute;
  top: 0;
  left: 50%;
  width: calc(var(--fan-tile) * var(--fs));
  aspect-ratio: 1;
  margin: 0 0 0 calc(var(--fan-tile) * var(--fs) / -2);
  z-index: var(--fz);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-fan);
  /* Grundton, solange das Foto noch lädt: die neutrale Grundfläche, nicht
     die getönte — ein Lavendelblitz vor dem Bild wäre auffälliger. */
  background: var(--surface-base);
  transform:
    translate(calc(var(--fan-tile) * var(--fx) * var(--fan-spread)),
              calc(var(--fan-tile) * var(--fy)))
    rotate(var(--fr));
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
  user-select: none;
  -webkit-user-drag: none;
}
.fan__photo img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Die sieben Positionen: Mitte am größten und vorne, Ränder kleiner und
   tiefer; Drehung wechselt das Vorzeichen wie handgelegte Abzüge.
   13.09.2026 von fünf auf sieben Kacheln erweitert (zwei Fotos kamen links
   und rechts dazu). Der Schrittabstand ist dafür von .66 auf .60 Kachel-
   breiten zusammengerückt: Bei .66 wäre der Fächer 1437 px breit geworden
   und damit breiter als der Inhaltsbereich (1328 px bei --maxw 1440 minus
   Gutter) — die Randkachel hätte über den Seitenrand hinausgeragt. Mit .60
   und den kleineren Außenkacheln misst er 1272 px und bleibt drin, bei
   schmaleren Fenstern skaliert er über --fan-tile (20.6vw) mit. */
.fan__photo:nth-child(1) { --fx: -1.80; --fy: .32; --fs: .78; --fr:  2.5deg; --fz: 2; }
.fan__photo:nth-child(2) { --fx: -1.20; --fy: .14; --fs: .86; --fr: -3deg;   --fz: 4; }
.fan__photo:nth-child(3) { --fx:  -.60; --fy: .20; --fs: .92; --fr:  2deg;   --fz: 3; }
.fan__photo:nth-child(4) { --fx:     0; --fy:   0; --fs: 1.08; --fr: -1.5deg; --fz: 6; }
.fan__photo:nth-child(5) { --fx:   .60; --fy: .15; --fs: .90; --fr:  2.5deg; --fz: 3; }
.fan__photo:nth-child(6) { --fx:  1.20; --fy: .26; --fs: .84; --fr: -2deg;   --fz: 4; }
.fan__photo:nth-child(7) { --fx:  1.80; --fy: .34; --fs: .76; --fr:  2deg;   --fz: 2; }
/* Hover: Kachel wächst, dreht minimal weiter und kommt nach vorne. */
.fan__photo:hover {
  z-index: 9;
  box-shadow: var(--shadow-fan-hi);
  transform:
    translate(calc(var(--fan-tile) * var(--fx) * var(--fan-spread)),
              calc(var(--fan-tile) * var(--fy)))
    rotate(calc(var(--fr) * 1.6))
    scale(1.1);
}
@media (max-width: 600px) {
  .fan__stage { --fan-spread: .74; }
}

/* Einheitliches, dezentes Farb-Grading über alle Startseiten-Fotos:
   nimmt den grellen Mischlicht-Fotos die Spitzen und zieht Ovale und
   Studio-Fächer auf eine ruhige, warme Stimmung. Sanfte
   filter-Transition, damit der Wechsel nach dem Reveal (Keyframe endet
   auf filter:none) unmerklich einschwingt. */
.oval-card__media img,
.fan__photo img {
  filter: saturate(.86) sepia(.06) contrast(1.03);
  transition: filter .4s var(--ease);
}
.oval-card__media img { transition: transform .6s var(--ease), filter .4s var(--ease); }

/* ---- S3b: Stimmen — zwei gegenläufige Bänder aus Google-Rezensionen ----
   Aus der Bild-Referenz ist NUR der Rhythmus übernommen: das Band läuft
   über beide Bildschirmränder hinaus (wischbar; am Desktop treibt main.js
   den Strom). Fläche, Typo und Kartenanatomie kommen aus den Tokens.
   Farbsystem 07.09.2026: Die Sektion liegt auf der mittleren Fläche
   (--surface-raised) — einem zarten Lavendel-Rosé aus der Logo- und
   Studiowelt —, nicht auf der dunklen; die gibt es nur im Footer.
   Text- und Fotokarten wechseln sich ab wie in der Bild-Referenz. Die
   Karten stehen in --surface-card und heben sich damit hell von der
   getönten Fläche ab, mit dünner Linie und sehr weichem Schatten. */
.sec-voices {
  padding-block: var(--section-y);
  color: var(--text-primary);
}
.voices__head {
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
  margin-bottom: clamp(1.6rem, 3vw, 2.6rem);
}
.sec-voices h2 {
  color: var(--text-primary);
  font-size: var(--fs-h2-sec);
  margin-bottom: 0;
}
/* Aggregatangabe als Untertitel unter der Überschrift (09.09.2026, Wunsch
   Matteo; Anordnung am 09.09.2026 auf eine Zeile umgestellt, Sterne links,
   Text rechts davon). Vorher stand sie als unterstrichener Link am Fuß der
   Sektion und trug dort dieselbe Last wie die Offenlegung. Jetzt führt sie
   die Sektion an.
   Sans unter der Display-Serif der Überschrift — das ist das Schriftpaar
   der ganzen Seite, und der Gewichts- und Größensprung macht die Zeile zum
   Untertitel statt zum Fließtext.
   Bewusst kein Link: Der Weg zum Profil steht einmal in der Quellenzeile am
   Fuß. Zweimal derselbe Link wäre Dekoration, und eine unterstrichene Zeile
   direkt unter der Überschrift zieht mehr Blick als die Überschrift selbst. */
.voices__aggregate {
  display: flex;
  align-items: center;
  gap: .5em;
  margin: var(--space-2) 0 0;
  font-family: var(--font-ref);
  font-size: var(--fs-lead);
  font-weight: 600;
  letter-spacing: .02em;
  color: var(--text-body);
}
/* Die Sterne erben Form, Farbe und Größe von .rating__stars (dieselbe
   Grafik wie im Hero, dort auf dunklem Grund). Keine eigene Schriftgröße —
   sie messen 1.05em der Untertitelzeile und wachsen damit mit ihr.
   flex-shrink: 0, damit lange Übersetzungen die Sterne nie stauchen. */
.voices__aggregate-stars {
  display: flex;
  flex-shrink: 0;
}

/* Quellen- und Offenlegungszeile nach § 2 Abs 6b UWG, im Stil einer
   Bildquellenangabe: kleinste Schriftstufe der Seite, gedämpfte Farbe,
   zentriert, knapper Abstand nach oben. --fs-micro ist im Token-Kopf genau
   für „Monogramm, Quellenzeile" ausgewiesen.
   Bewusst INNERHALB von .sec-voices: Die Sektion liegt auf --surface-raised,
   und nur für diese Fläche ist --text-muted mit 4,65:1 nachgerechnet
   (docs/AUDIT.md, knappster Wert der Seite).
   --maxw, nicht --measure: Bei --fs-micro sind 62ch nur rund 390 px, die
   Zeile brach am Desktop auf drei Zeilen und las sich als Textblock statt
   als Quellenangabe (gemessen 09.09.2026 bei 1440 px). Mit --maxw steht sie
   am Desktop auf einer Zeile und bricht erst am Telefon. */
.voices__source {
  max-width: var(--maxw);
  margin: var(--space-2) auto 0;
  padding-inline: var(--gutter);
  font-size: var(--fs-micro);
  line-height: 1.5;
  text-align: center;
  color: var(--text-muted);
}
/* Der Link trägt die Farbe der Zeile und nur eine feine Unterstreichung —
   erkennbar als Link, ohne die leise Zeile laut zu machen. */
.voices__source a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: .22em;
}
.voices__source a:hover { color: var(--text-primary); }

/* Band: Grundzustand ist ein normal wischbares Scroll-Band — so bleibt es
   ohne JS und bei prefers-reduced-motion vollständig lesbar. Erst
   html.fx-voices macht daraus den fließenden Strom (siehe Motion-Region).
   padding-block gibt dem Hover-Lift Luft, bevor overflow:hidden schneidet. */
.voices-band {
  overflow-x: auto;
  overflow-y: hidden;
  padding-block: clamp(.8rem, 1.4vw, 1.15rem);
  scrollbar-width: none;
  scroll-snap-type: x proximity;
  -webkit-mask-image: linear-gradient(90deg, transparent, var(--mask-solid) 7%, var(--mask-solid) 93%, transparent);
          mask-image: linear-gradient(90deg, transparent, var(--mask-solid) 7%, var(--mask-solid) 93%, transparent);
}
.voices-band::-webkit-scrollbar { display: none; }
.voices-track {
  display: flex;
  align-items: stretch;
  gap: clamp(.9rem, 1.6vw, 1.4rem);
  width: max-content;
  padding-inline: var(--gutter);
}

.voice,
.voice-photo {
  flex: 0 0 auto;
  scroll-snap-align: center;
  border-radius: 20px;
  box-shadow: var(--shadow-card-soft);
}

/* Textkarte: Zitat oben, Signatur immer an der Unterkante (margin-top:auto),
   damit kurze und lange Rezensionen dieselbe Grundlinie teilen. Drei
   Breiten statt einer — die Länge der echten Rezension bestimmt das Maß. */
.voice {
  inline-size: 20rem;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin: 0;
  padding: clamp(1.15rem, 1.8vw, 1.5rem);
  background: var(--surface-card);
  border: 1px solid var(--line);
}
.voice--sm { inline-size: 15.5rem; }
.voice--lg { inline-size: 24.5rem; }

/* Fünf Sterne als EIN Element: Stern-Maske im Sekundär-Akzent (A/B Messing,
   C Salbei). Ohne mask-Support bleibt der Platz leer statt als Balken. */
.voice__stars {
  display: block;
  font-size: var(--fs-mark);
  inline-size: 6.4em;
  block-size: 1em;
}
@supports ((-webkit-mask-image: url('')) or (mask-image: url(''))) {
  .voice__stars {
    background: var(--accent-secondary);
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 19'%3E%3Cpath d='M10 .5l2.9 5.9 6.6 1-4.8 4.6 1.2 6.5L10 15.4 4.1 18.5l1.2-6.5L.5 7.4l6.6-1z'/%3E%3C/svg%3E");
            mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 19'%3E%3Cpath d='M10 .5l2.9 5.9 6.6 1-4.8 4.6 1.2 6.5L10 15.4 4.1 18.5l1.2-6.5L.5 7.4l6.6-1z'/%3E%3C/svg%3E");
    -webkit-mask-repeat: repeat-x;
            mask-repeat: repeat-x;
    -webkit-mask-size: 1.28em 1em;
            mask-size: 1.28em 1em;
  }
}

.voice__quote {
  font-family: var(--font-ref);
  font-size: var(--fs-quote);
  line-height: 1.55;
  color: var(--text-body);
  margin: 0;
  text-wrap: pretty;
}
.voice__by {
  display: flex;
  align-items: center;
  gap: .7rem;
  margin-top: auto;
  padding-top: var(--space-2);
  border-top: 1px solid var(--line);
}
/* Monogramm statt Profilbild — dieselbe Lösung, die Google selbst als
   Fallback zeigt, und ohne fremde Fotos. Kreis in der mittleren Fläche,
   Initialen im Primärtext. */
.voice__avatar {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  inline-size: 2.25rem;
  block-size: 2.25rem;
  border-radius: 50%;
  background: var(--surface-raised);
  color: var(--text-primary);
  font-family: var(--font-ref);
  font-size: var(--fs-micro);
  font-weight: 700;
  letter-spacing: .04em;
}
.voice__who { display: flex; flex-direction: column; line-height: 1.3; }
.voice__name { font-family: var(--font-ref); font-size: var(--fs-meta); font-weight: 600; color: var(--text-primary); }
.voice__src  { font-family: var(--font-ref); font-size: var(--fs-micro); color: var(--text-muted); }

/* Fotokarte: trägt nie zur Zeilenhöhe bei (absolut gesetztes Bild), sie
   nimmt die Höhe, die die Textkarten vorgeben. Gleiches Farb-Grading wie
   die übrigen Startseiten-Fotos — die frühere Extra-Abdunklung entfällt,
   sie war für die dunkle Plumfläche gedacht. */
.voice-photo {
  position: relative;
  inline-size: 14rem;
  overflow: hidden;
}
.voice-photo--wide { inline-size: 18.5rem; }
.voice-photo img {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  filter: saturate(.86) sepia(.06) contrast(1.03);
}

@media (hover: hover) and (pointer: fine) {
  .voice,
  .voice-photo {
    transition: transform .5s var(--ease),
                border-color .35s var(--ease),
                box-shadow .5s var(--ease);
  }
  .voice-photo img { transition: transform .8s var(--ease), filter .5s var(--ease); }
  .voice:hover,
  .voice-photo:hover { transform: translateY(-6px); }
  .voice:hover {
    border-color: var(--line-strong);
    box-shadow: var(--shadow-card);
  }
  .voice-photo:hover {
    box-shadow: var(--shadow-card);
  }
  .voice-photo:hover img {
    transform: scale(1.05);
    filter: saturate(.95) sepia(.04) contrast(1.03);
  }
}

@media (max-width: 640px) {
  /* ---- Am Telefon: zwei Zeilen aus reinen Textkarten, seitlich wischbar
     (07.09.2026, Matteo nach Vorlage): Die Fotokarten entfallen, jede
     Karte hat dieselbe Breite, die nächste lugt rechts herein und zeigt,
     dass es weitergeht. Die drei Breitenstufen des Desktops brauchen die
     Zeilen dort, weil dort Lang- und Kurzrezensionen nebeneinander
     rhythmisch stehen; am Telefon steht immer nur eine Karte ganz im
     Bild, und eine feste Breite macht jedes Wischen gleich weit.
     Einrasten an der Rinne statt in der Mitte: So steht nach jedem Wisch
     eine Karte bündig am Seitenrand. Der Strom-Effekt bleibt am Telefon
     aus (Gate in main.js), das Band scrollt nur, wenn man es wischt. */
  .voice-photo { display: none; }
  /* 08.09.2026 (Matteo): Karten und Schrift eine Stufe kleiner — bei
     18,5rem und 14,9px Zitat stand pro Zeile kaum ein halber Satz, und
     auf Telefonen mit hochgestellter Schriftgröße noch weniger.
     Die Größen bleiben Token: Die Karte setzt die Stufen nur FÜR SICH
     neu, statt unten Rohwerte in die Komponenten zu streuen. Damit
     behalten Desktop und alle anderen Sektionen ihre Skala, und die
     Verhältnisse innerhalb der Karte bleiben die gewohnten. */
  .voice,
  .voice--sm,
  .voice--lg { inline-size: min(64vw, 15rem); }
  .voice {
    --fs-quote: .82rem;
    --fs-meta: .8rem;
    --fs-micro: .7rem;
    --fs-mark: .7rem;
    padding: 1rem;
    gap: .7rem;
    scroll-snap-align: start;
  }
  .voice__quote { line-height: 1.5; }
  .voice__by { gap: .55rem; padding-top: .7rem; }
  .voice__avatar { inline-size: 2rem; block-size: 2rem; }
  .voices-band { scroll-padding-inline: var(--gutter); }
}

/* ---- S4: Häufige Fragen (natives <details>: ohne JS und per Tastatur
   bedienbar; `name` gibt den Akkordeon-Modus nativ, ohne Skript) ---- */
.faq__head { text-align: center; margin-bottom: clamp(2.25rem, 4.4vw, 3.5rem); }
.faq__head h2 { font-size: var(--fs-h2-lg); margin-bottom: 0; }

/* ---- Fragen als eigenständige Karten (07.09.2026, Referenz Untitled-UI-FAQ).
   Übernommen sind nur die Achsen: zentrierte große Überschrift, viel Luft,
   Chevron statt Plus, erste Frage offen. Bewusst NICHT übernommen: die
   Filter-Pills (Verbotsliste; bei fünf Fragen ohnehin ohne Funktion), die
   organischen Linienformen im Hintergrund (dekorative Fläche hinter Text)
   und die generischen Icons je Zeile.
   Warum Karten statt nackter Zeilen: Die Sektion war der einzige Abschnitt
   ohne eigene Ebene — Creme auf Creme, während Behandlungen, Studio und
   Stimmen mit Bild oder Fläche arbeiten. Die Karten nehmen die Anatomie der
   Zitatkarten auf und binden die Sektion damit an den Rest. ---- */
.faq-list {
  max-width: 52rem;
  margin-inline: auto;
  display: grid;
  gap: clamp(.6rem, 1.1vw, .9rem);
}
.faq-item {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-card);
  box-shadow: var(--shadow-card);
  transition: box-shadow .45s var(--ease), border-color .35s var(--ease),
              transform .45s var(--ease);
}
/* Signature: eine goldene Haarlinie läuft beim Öffnen von links durch die
   Oberkante und codiert damit den Zustand — dasselbe Motiv wie der
   Weiterlese-Link in der Behandlungs-Sektion. */
.faq-item::before {
  content: '';
  position: absolute;
  inset-inline: 0;
  top: 0;
  height: 2px;
  z-index: 1;
  background: linear-gradient(90deg, var(--accent), var(--accent-light) 60%, transparent);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform .55s var(--ease);
  pointer-events: none;
}
.faq-item[open] { border-color: var(--line-strong); box-shadow: var(--shadow-card-hover); }
.faq-item[open]::before { transform: scaleX(1); }

.faq-item summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: clamp(1.1rem, 2vw, 1.45rem) clamp(1.15rem, 2.4vw, 1.75rem);
  cursor: pointer;
  list-style: none;            /* Standard-Dreieck entfernen */
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item__q {
  font-family: var(--font-ref);
  font-size: 1.02rem;
  font-weight: 600;
  line-height: 1.35;
  color: var(--text-primary);
  transition: color var(--dur-fast) var(--ease);
}
/* Chevron: dreht beim Öffnen um 180°, sitzt in einem eigenen Träger, damit
   Drehung und Hover-Versatz nie denselben Transform besitzen. */
.faq-item__marker {
  flex-shrink: 0;
  display: grid;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
  color: var(--text-muted);
  background: hsl(var(--text-primary-hsl) / 5%);
  transition: transform .45s var(--ease), color var(--dur-fast) var(--ease),
              background var(--dur-fast) var(--ease);
}
.faq-item__marker svg { width: .95rem; height: .95rem; display: block; }
.faq-item[open] .faq-item__marker {
  transform: rotate(180deg);
  color: var(--accent-secondary);
  background: hsl(var(--accent-secondary-hsl) / 12%);
}
@media (hover: hover) and (pointer: fine) {
  .faq-item:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-card-hover);
    border-color: var(--line-strong);
  }
  .faq-item summary:hover .faq-item__q { color: var(--text-body); }
  .faq-item summary:hover .faq-item__marker {
    color: var(--accent-secondary);
    background: hsl(var(--accent-secondary-hsl) / 12%);
  }
}
.faq-item__a {
  padding: 0 clamp(1.15rem, 2.4vw, 1.75rem) clamp(1.15rem, 2vw, 1.5rem);
}
.faq-item__a p {
  font-family: var(--font-ref);
  font-size: .92rem;
  line-height: 1.65;
  color: var(--text-muted);
  max-width: 62ch;
  margin: 0;
}
.faq-item__a a { color: var(--text-primary); text-decoration: none; border-bottom: 1px solid var(--accent-secondary); }
.faq-item__a a:hover { color: var(--text-body); }
/* Weiches Auf-/Zuklappen. Progressiv: ohne ::details-content bzw.
   interpolate-size klappt es schlicht ohne Animation auf — kein Artefakt. */
@supports (interpolate-size: allow-keywords) {
  :root { interpolate-size: allow-keywords; }
}
.faq-item::details-content {
  block-size: 0;
  overflow: hidden;
  transition: block-size .45s var(--ease), content-visibility .45s allow-discrete;
  transition-behavior: allow-discrete;
}
.faq-item[open]::details-content { block-size: auto; }

/* Auf hellen Flächen: violetter Link mit Gold-Unterstrich (Gold selbst nie Text) */
.link-gold {
  color: var(--text-primary);
  font-weight: 600;
  text-decoration: none;
  border-bottom: 1px solid var(--accent-secondary);
  padding-bottom: .15em;
  transition: color var(--dur-fast) var(--ease);
}
.link-gold:hover { color: var(--text-body); }

/* ---- Weiterlese-Link mit Pfeil (Startseite, Sektion Behandlungen) ----
   Bleibt ein Textlink, kein Button: die goldene Linie liegt in Ruhe
   zurückgenommen da und zündet beim Hover von links in vollem Gold durch;
   beim Verlassen läuft der Strich nach rechts wieder hinaus. Der Pfeil
   sitzt in einem eigenen Träger — Hover verschiebt den Träger, der
   Ruhe-Impuls läuft auf dem SVG darin, damit nie zwei Regeln denselben
   Transform besitzen. */
.link-gold.link-more {
  border-bottom: 0;
  padding-bottom: 0;
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: .55em;
  padding-block: .1em .35em;
  transition: color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.link-gold.link-more::before,
.link-gold.link-more::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  pointer-events: none;
}
.link-gold.link-more::before { background: hsl(var(--accent-secondary-hsl) / 42%); }
.link-gold.link-more::after {
  background: var(--accent-secondary);
  transform: scaleX(0);
  transform-origin: right center;
  transition: transform var(--dur) var(--ease);
}
.link-more__arrow {
  display: inline-flex;
  color: var(--accent-secondary);
  transition: transform var(--dur) var(--ease);
}
.link-more__arrow svg { width: 1.05em; height: auto; overflow: visible; }
.link-more:active { transform: translateY(1px); }

@media (hover: hover) and (pointer: fine) {
  .link-gold.link-more:hover::after { transform: scaleX(1); transform-origin: left center; }
  .link-more:hover .link-more__arrow { transform: translateX(.3em); }
}

/* Ruhe-Impuls: der Pfeil tippt alle ~6,5 s kurz nach rechts an — im
   Shimmer-Takt der Gold-CTAs, und erst, wenn die Zeile eingeblendet ist. */
@media (prefers-reduced-motion: no-preference) {
  html.js .link-more.in .link-more__arrow svg {
    animation: arrow-tap 6.5s var(--ease) infinite;
    animation-delay: 1.6s;
  }
  html.js .link-more:hover .link-more__arrow svg { animation-play-state: paused; }
  @keyframes arrow-tap {
    0%, 88%, 100% { transform: translateX(0); }
    92%           { transform: translateX(.22em); }
    96%           { transform: translateX(0); }
  }
}

/* ---- Seitenkopf für Unterseiten (schmales Banner) ---- */
.page-head {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  align-items: flex-end;
  min-height: clamp(220px, 34vh, 340px);
}
.page-head img {
  position: absolute; inset: 0; z-index: -2;
  width: 100%; height: 100%; object-fit: cover; object-position: center 30%;
}
.page-head::after {
  content: '';
  position: absolute; inset: 0; z-index: -1;
  background:
    linear-gradient(to top, var(--surface-deep) 4%, transparent 70%),
    hsl(var(--surface-deep-hsl) / 55%);
}
.page-head .sec__inner { width: 100%; padding-block: calc(var(--header-h) + var(--space-4)) var(--space-4); }
.page-head h1 { font-size: var(--fs-h2); margin-bottom: var(--space-1); color: var(--text-on-deep); }
.page-head p { color: var(--text-on-deep-muted); margin: 0; }
.page-head .link-gold { color: var(--text-on-deep); border-bottom-color: var(--line-on-deep-strong); white-space: nowrap; }
.page-head .link-gold:hover { color: var(--text-on-deep-hi); }

/* ---- Rechtsseiten: Impressum und Datenschutz (09.09.2026) ----
   Beide Seiten sind reine Textseiten mit acht kurzen Abschnitten. Sie
   liefen bisher auf der globalen h2-Stufe (--fs-h2, bis 3rem) — derselben
   Groesse wie die H1 im Seitenkopf. Damit war die Hierarchie flach und
   „Medieninhaberin und Diensteanbieterin" dominierte die Seite staerker
   als ihr Titel.

   Der Abstand wandert dabei von unten nach oben: Eine Ueberschrift
   gehoert dicht an den Text, den sie benennt, und weit weg vom Absatz
   davor. Die globale Regel machte es genau umgekehrt (margin-bottom
   --space-3, kein Abstand oben). `p + h2` trifft nur Ueberschriften, die
   auf einen Absatz folgen — die erste Ueberschrift der Seite bleibt
   dadurch ohne Vorspann, ganz ohne :first-child-Sonderregel.

   line-height etwas offener als die globale 1.12: „Medieninhaberin und
   Diensteanbieterin" und „Unternehmensgegenstand und Blattlinie" brechen
   am Telefon auf zwei Zeilen. */
.legal h2 {
  font-size: var(--fs-h2-legal);
  line-height: 1.2;
  margin: 0 0 var(--space-1);
}
.legal p + h2 { margin-top: var(--space-4); }
/* Standzeile am Fuss der Rechtsseiten: bindet den veroeffentlichten Text an
   die unterschriebene Freigabe. Gedaempft wie die Hinweiszeilen unter der
   Preisliste, aber mit eigenem Namen - .price-note gehoert zur Preisliste
   und soll nicht zur allgemeinen Kleinschrift werden. */
.legal__stand {
  margin-top: var(--space-4);
  color: var(--text-muted);
  font-size: var(--fs-label);
  letter-spacing: .03em;
}

/* ---- Preislisten (goldene Punktlinien-Leader, helle Fläche) ---- */
.price-section h2 { margin-top: var(--space-5); }
.price-list { display: grid; gap: var(--space-2); max-width: 46rem; }
.price-row { display: flex; align-items: baseline; gap: .75em; }
.price-row__name { font-weight: 600; color: var(--text-body); }
.price-row__name small { display: block; font-weight: 400; color: var(--text-muted); font-size: var(--fs-label); letter-spacing: .02em; }
.price-row__leader { flex: 1; border-bottom: 1px dotted var(--line-accent); transform: translateY(-.3em); min-width: 2rem; }
.price-row__price { font-weight: 700; color: var(--text-primary); white-space: nowrap; }
.price-row__price.is-open { font-weight: 400; font-style: italic; color: var(--text-muted); }
.price-note { color: var(--text-muted); font-size: var(--fs-label); letter-spacing: .03em; margin-top: var(--space-4); }
/* Zweite Hinweiszeile (Abgrenzung Wellness/ärztliche Behandlung, 09.09.2026)
   rückt an die erste heran: --space-4 zweimal hätte die beiden Notizen
   auseinandergerissen, obwohl sie als Block unter der Liste zusammengehören. */
.price-note + .price-note { margin-top: var(--space-1); }

/* ---- Kontakt-Seite: Kontaktwege + Zeiten links, Nachricht rechts ---- */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.contact-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(2rem, 4vw, 3.5rem);
  align-items: start;
  max-width: 64rem;
  margin-inline: auto;
}
@media (min-width: 820px) { .contact-grid { grid-template-columns: 1fr 1fr; } }

/* Kontaktwege: Strich-Icon + Label + Wert (kein Icon-Kasten-Grid) */
.contact-lines { list-style: none; margin: 0 0 var(--space-5); padding: 0; }
.contact-lines li {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  padding-block: var(--space-3);
  border-bottom: 1px solid var(--line);
}
.contact-lines li:first-child { padding-top: 0; }
.contact-line__icon {
  flex-shrink: 0;
  width: 1.35rem;
  height: 1.35rem;
  margin-top: .95rem;
  color: var(--accent-secondary);
}
.contact-lines li:first-child .contact-line__icon { margin-top: .8rem; }
.contact-line__icon svg { width: 100%; height: 100%; display: block; }
.contact-line__body { display: flex; flex-direction: column; gap: .3rem; min-width: 0; }
.contact-line__label {
  font-size: var(--fs-label);
  font-weight: 700;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-muted);
}
.contact-line__value {
  color: var(--text-body);
  line-height: 1.5;
  text-decoration: none;
  /* break-word statt anywhere: bricht bevorzugt am <wbr> nach dem @
     (E-Mail-Markup) statt mitten im Wort wie zuvor bei "gmail.c­om". */
  overflow-wrap: break-word;
}
/* Telefon und E-Mail sind am Handy die eigentlichen Aktionen dieser Seite.
   Die Zeile ist nur 27px hoch; Polsterung plus gleich großes negatives Außen-
   maß hebt die Trefferfläche auf 44px, ohne die Zeile zu verschieben. */
a.contact-line__value {
  color: var(--text-primary);
  font-weight: 600;
  padding-block: .55rem;
  margin-block: -.55rem;
}
a.contact-line__value:hover { color: var(--text-body); }
.contact-lines .link-gold { align-self: flex-start; margin-top: .25rem; font-size: .9rem; }

/* Größer und kräftiger als der Standard-h2 dieser Seite (07.09.2026,
   Wunsch Matteo): eigene Stufe --fs-h3-lg plus Gewicht 600 statt der
   globalen 500 aus der h1/h2-Regel — bleibt Playfair Display, wirkt aber
   eigenständiger als die übrigen Fließtext-großen Unterüberschriften. */
.contact-sub { font-size: var(--fs-h3-lg); font-weight: 600; margin-bottom: var(--space-2); }

/* Öffnungszeiten als Karte: Goldbalken markiert die geöffneten Tage */
.hours-card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-card);
  overflow: hidden;
  box-shadow: var(--shadow-card-soft);
}
.hours-table { width: 100%; border-collapse: collapse; }
.hours-table th, .hours-table td { padding: .8em 1rem; font-weight: 400; }
.hours-table tr + tr th, .hours-table tr + tr td { border-top: 1px solid var(--line); }
.hours-table th {
  position: relative;
  text-align: left;
  padding-left: 1.6rem;
  color: var(--text-body);
  font-weight: 400;
}
.hours-table th::before {
  content: '';
  position: absolute;
  left: .8rem;
  top: 50%;
  width: 3px;
  height: 1.1em;
  border-radius: 2px;
  background: var(--accent-secondary);
  transform: translateY(-50%);
}
.hours-table td { text-align: right; font-weight: 600; color: var(--text-primary); white-space: nowrap; }
.hours-table .is-closed th, .hours-table .is-closed td { color: var(--text-muted); font-weight: 400; }
.hours-table .is-closed th::before { display: none; }
.hours-hint {
  margin-top: var(--space-3);
  color: var(--text-muted);
  font-size: .92rem;
  line-height: 1.55;
}

/* Anfahrts-Karte: übernimmt die Kartenbehandlung, die zuvor die
   Nachricht-Karte trug (Rahmen, Radius, Innenabstand, Schatten), damit die
   rechte Spalte optisch an derselben Stelle steht wie vorher. */
.map-card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-card);
  padding: clamp(1.5rem, 3vw, 2.25rem);
  box-shadow: var(--shadow-card);
}
.map-card h2 { font-size: var(--fs-h3-lg); font-weight: 600; margin-bottom: var(--space-3); }
.map-card__figure { margin: 0; }
/* width/height stehen im HTML und halten das Seitenverhältnis reserviert —
   height:auto skaliert mit, ohne Layout-Shift beim Nachladen. */
.map-card__img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius-sm);
  background: var(--surface-base);
}
/* Lizenzzeile (ODbL): dezent, aber lesbar — --ink-soft trägt 5.8:1 auf
   der Karte und bleibt damit über der AA-Schwelle. */
.map-card__attrib {
  margin-top: var(--space-1);
  font-size: var(--fs-micro);
  line-height: 1.5;
  color: var(--text-muted);
}
.map-card__attrib a { color: inherit; text-underline-offset: .2em; }
.map-card__attrib a:hover { color: var(--text-primary); }
.map-card__address {
  font-style: normal;
  margin: var(--space-3) 0 0;
  font-size: var(--fs-body);
  line-height: 1.6;
  color: var(--text-body);
}
/* Routen-Ziele: am Handy untereinander, ab 640px nebeneinander. */
.map-card__actions {
  display: grid;
  gap: var(--space-2);
  margin-top: var(--space-3);
}
/* auto-fit statt starrer 1fr 1fr: Ab 640px ist die Karte breit genug für
   zwei Spalten. In der zweispaltigen Desktop-Ansicht (ab 820px) ist sie aber
   nur noch gut 410px breit — dort bräuchte „Route mit Google Maps“ bei 18px
   Schrift allein 182px Textbreite und bräche zweizeilig um. Mit auto-fit
   stehen die Buttons nebeneinander, sobald beide ganz hineinpassen, und
   rutschen sonst sauber untereinander. */
@media (min-width: 640px) {
  .map-card__actions { grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); }
}
.map-card__btn {
  justify-content: center;
  text-align: center;
  min-height: 44px;              /* Touch-Ziel, auch bei kurzem Text */
}
/* Liegt auf der hellen Karte, --focus-ring ist dort die Plum-Stufe
   (13,5:1 gegen den Kartengrund). */
.map-card__btn:focus-visible {
  outline: 2px solid var(--focus-ring, var(--text-primary));
  outline-offset: 3px;
}
/* Outline-Button für HELLE Flächen. .btn-ghost trägt helle Schrift für
   dunkle Flächen und wäre auf der Karte zu kontrastarm. */
.btn-outline {
  display: inline-flex;
  align-items: center;
  gap: .5em;
  padding: .8em 1.6em;
  border-radius: 999px;
  border: 1px solid var(--line-strong);
  background: transparent;
  color: var(--text-primary);
  font-weight: 700;
  font-size: var(--fs-body);
  text-decoration: none;
  transition: background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              box-shadow var(--dur-fast) var(--ease),
              transform var(--dur) var(--ease);
}
.btn-outline:hover {
  border-color: var(--text-primary);
  background: var(--surface-raised);
  box-shadow: var(--shadow-card-soft);
}

/* ---- Footer-Ausbau ---- */
.footer-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-4);
}
@media (min-width: 820px) { .footer-grid { grid-template-columns: 2fr 1fr 1fr; } }
.footer-grid h3 { font-size: var(--fs-label); letter-spacing: var(--tracking-label); text-transform: uppercase; color: var(--text-on-deep-muted); }
.footer-grid ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .5em; color: var(--text-on-deep-muted); }
.footer-grid a { color: var(--text-on-deep-muted); text-decoration: none; }
.footer-grid a:hover { color: var(--text-on-deep); }
/* Listenlinks rücken beim Hover ein Stück vor — nur die Listen, die
   Wortmarke darüber ist ein Flex-Container und bleibt unangetastet. */
.footer-grid ul a {
  display: inline-block;
  transition: color var(--dur-fast) var(--ease), transform var(--dur) var(--ease);
}
@media (hover: hover) and (pointer: fine) {
  .footer-grid ul a:hover { transform: translateX(3px); }
}

/* ---- Mobile-Header ----
   Mit JS: eine Zeile aus Wortmarke + Menü-Button, Tabs wandern ins Panel.
   Ohne JS: die alte zweizeilige Tab-Leiste bleibt als Fallback bestehen.

   Obergrenze 08.09.2026 von 900 px auf 767 px gesenkt (Tablet-Durchgang).
   Gemessen wurde die Eigenbreite der vollen Desktop-Leiste — Wortmarke plus
   Tab-Pille plus Anruf-Knopf brauchen 636 px: ab 768 px bleiben 58 px Luft,
   bei 700 px fehlen bereits 5 px. 768 px ist zugleich die schmalste
   Tablet-Hochkantbreite (iPad 9,7"), also steht auf JEDEM Tablet dieselbe
   Leiste wie am Desktop statt des Menü-Knopfs mit bildschirmfüllendem
   Panel. Darunter (Telefon, auch quer) bleibt alles wie bisher. */
@media (max-width: 767px) {
  .site-header {
    top: .6rem;
    left: .6rem;
    right: .6rem;
    flex-wrap: wrap;
    row-gap: .5rem;
    border-radius: var(--radius);
  }
  .brand__badge { width: 2.2rem; height: 2.2rem; }
  .brand__name { font-size: 1rem; }
  .site-nav { width: 100%; gap: .45rem; }
  .nav-pill { flex: 1; justify-content: space-between; gap: 0; }
  .nav-pill a { padding: .5em .55em; font-size: .68rem; letter-spacing: .01em; }
  .btn-cta { padding: .28rem .28rem .28rem .85rem; font-size: .72rem; gap: .4rem; }
  .btn-cta__icon { width: 1.7rem; height: 1.7rem; }

  html.js .site-header { flex-wrap: nowrap; row-gap: 0; border-radius: 999px; }
  html.js .site-nav { display: none; }      /* Tabs + CTA leben jetzt im Panel */
  html.js .nav-toggle { display: grid; }
  html.js .nav-panel { display: flex; }
  /* Scroll-Sperre nur im Handy-Bereich: beim Wechsel auf Desktop
     kann keine gesperrte Seite zurückbleiben. */
  html.nav-open, html.nav-open body { overflow: hidden; }
}

/* ---- Handy: Startseite entschlacken ---- */
@media (max-width: 640px) {
  /* Hero: Kurzfassung der Subline, ein CTA, keine Glaskarte */
  .hero__sub-rest { display: none; }
  .hero__cta .btn-ghost { display: none; }
  .hero__cta { margin-bottom: clamp(.9rem, 2.6vw, 1.4rem); }

  /* ---- Ein Textblock unten statt Zweiteilung ----
     Zwischenzeitlich hielt die Überschrift per margin-bottom:auto den
     oberen Bereich. Auf Ansage von Matteo (07.09.2026) wieder
     zusammengeholt: Die Überschrift steht direkt über der Subline, der
     ganze Block sitzt an der Unterkante. Darüber bleibt das Foto frei.
     Kein eigener Wert nötig — ohne den auto-Abstand greift wieder das
     justify-content: flex-end des Basis-Layouts. */
  /* Die Überschrift fraß fünf Zeilen, weil jeder Teilsatz zweizeilig
     umbrach. Entscheidend ist nicht „kleiner", sondern dass jeder
     Teilsatz in EINE Zeile passt — dann sind es drei. 6.4vw hält das
     von 320px bis 640px durch (an der realen Textbreite gemessen,
     nicht geschätzt); die Obergrenze bleibt unter dem bisherigen Wert. */
  /* 6.4vw → 7.6vw (07.09.2026, Matteo: Überschrift am Telefon schwer
     lesbar): Ein Fünftel größer, die Ein-Zeilen-Regel bleibt — an der
     realen Textbreite gemessen trägt „Weniger Verspannung." bei 320 px
     bis 27,4 px, bei 360 px bis 31,3 px; 7.6vw bleibt mit 24,3 bzw.
     27,4 px jeweils darunter. Die Obergrenze fängt breite Telefone ab. */
  .hero__title { font-size: clamp(1.5rem, 7.6vw, 2.05rem); line-height: 1.14; }
  /* Die Subline wirkte am Telefon größer als ihre Rolle: bei 15,2 px auf
     1,6er Zeilenhöhe stand sie fast so breit wie die Überschrift und mit
     mehr Luft. Jetzt die Meta-Stufe mit engerer Zeile; balance verteilt
     die zwei Zeilen gleichmäßig statt „… an der / Krems." zu hinterlassen. */
  .hero__sub {
    font-size: var(--fs-meta);
    line-height: 1.5;
    text-wrap: balance;
  }
  /* Näher an die Unterkante als bisher (war clamp(3rem, 8vh, 6rem)) —
     Button und Sterne sollen dort sitzen, nicht darüber schweben. */
  .hero__inner { padding-bottom: clamp(1.1rem, 3vh, 1.9rem); }
  /* ---- Die helle Fläche darunter setzt erst später an ----
     100svh endet dort, wo die Browserleiste beim Laden steht. Sobald die
     Leiste beim ersten Wisch einklappt, wächst der Bildschirm nach unten
     und die Creme-Sektion stand sofort als heller Balken da. Der Hero
     füllt jetzt die GROSSE Viewport-Höhe (Leiste eingeklappt); der
     Textblock bleibt trotzdem an der Kante des kleinen Viewports, weil
     der Unterschied beider Höhen als Polster darunter liegt. Beide
     Einheiten sind fest (kein dvh), also kein Nachlayouten beim Scrollen.
     Browser ohne lvh/svh behalten die Zeilen darüber. */
  .hero { min-height: 100lvh; }
  .hero__inner { padding-bottom: calc(100lvh - 100svh + clamp(1.1rem, 3vh, 1.9rem)); }

  /* ---- Zwei helle Flächen, die am Telefon stören, entfallen ----
     1. Der Creme-Auslauf am Hero-Fuß (.hero::after) blendet die untere
        Bildhälfte zur Seitenfarbe hoch — am Desktop ein weicher Übergang,
        am Telefon der „weiße Anfang", den man beim Ankommen sieht,
        obwohl die nächste Sektion noch gar nicht beginnt. Die Kante zur
        hellen Fläche entsteht jetzt hart an der Hero-Unterkante.
     2. Der warme Lichtfleck oben rechts im Scrim las sich am schmalen
        Bild als weißer Schleier hinter Wortmarke und Menüknopf. Der
        Lesbarkeits-Scrim von links bleibt unverändert — er trägt die
        Textkontraste. */
  /* 08.09.2026, Matteo: „Das Bild ist zu lila. So natürlich wie möglich,
     nur leicht lila unten und dann Übergang zu Weiß — den sieht man aber
     erst, wenn man scrollt." Drei Schichten neu gedacht:
     1. Der Creme-Übergang ist wieder da, aber ausschließlich in dem
        Streifen unterhalb des kleinen Viewports (Browserleiste ausgefahren)
        bis zur Hero-Unterkante. Beim Ankommen ist davon nichts zu sehen,
        nach dem ersten Wisch läuft das Bild von leicht Lila in die
        Seitenfarbe aus. Ist die Leiste eingeklappt geladen, sind beide
        Höhen gleich und der Streifen 0 px hoch — dann gibt es keinen
        Übergang, aber auch keinen falschen.
     2. Der violette Seitenschleier des Desktops (80 % → 12 %) tönte das
        ganze Foto; er bleibt nur als Hauch. Die Textkontraste trägt am
        Telefon allein die Wäsche unten (siehe --wash-* oben).
     3. Der Kopfverlauf deckt nur noch die helle Wand hinter Wortmarke und
        Menüknopf und läuft unter der Kopfleiste aus, statt bis zu den
        Steinen zu reichen. */
  .hero::after {
    inset: auto 0 0 0;
    height: calc(100lvh - 100svh);
    background: linear-gradient(to top,
      var(--surface-base) 0%,
      hsl(var(--surface-base-hsl) / 62%) 50%,
      transparent 100%);
  }
  .hero__scrim {
    background:
      linear-gradient(to bottom,
        hsl(var(--surface-deep-hsl) / 74%) 0%,
        hsl(var(--surface-deep-hsl) / 62%) 9%,
        hsl(var(--surface-deep-hsl) / 34%) 24%,
        transparent 40%),
      linear-gradient(100deg,
        hsl(var(--surface-deep-hsl) / 22%) 6%,
        hsl(var(--surface-deep-hsl) / 9%) 46%,
        transparent 100%);
  }
  /* Ohne das warme Kerzenlicht-Grading des Desktops: Der Sepia-Anteil
     zog die Haut ins Gelbliche, mit dem violetten Schleier darüber wurde
     daraus der beanstandete Stich. Am Telefon bleibt das Foto neutral,
     nur eine Spur satter und heller. */
  .hero__bg { filter: saturate(1.06) brightness(1.03); }

  /* Lange Kategorie-Überschriften („Gesichtsbehandlungen") passten in der
     kursiven Display-Schrift nicht in eine Zeile und wurden beschnitten:
     kleiner setzen, mehr Zeilenluft für Unterlängen, Trennung erlauben. */
  .price-section h2,
  .page-head h1 {
    font-size: clamp(1.5rem, 6vw, 1.9rem);   /* kleiner als der globale Wert */
    line-height: 1.26;                       /* Luft für Unterlängen (g, y) */
    overflow-wrap: break-word;
    hyphens: auto;
  }

  /* ---- Preiszeilen stehen sofort, nur Überschrift und Goldstrich kommen
     an (07.09.2026, Matteo): Am Telefon ruckelte das gestaffelte
     Einblenden der Zeilen — jede trägt Aufsteigen plus Unschärfe-Filter,
     und bis zu neun davon laufen gleichzeitig. Die Zeilen sind jetzt vom
     ersten Bild an da; die Kategorie-Überschrift behält ihr Ankommen samt
     Strich, der sich von links zeichnet. Das Skript merkt davon nichts:
     Es setzt .in und räumt data-reveal wie bisher per Fallback-Timer ab. */
  html.js .price-row[data-reveal]:not(.in) { opacity: 1; }
  html.js .price-row[data-reveal].in { animation: none; }

  /* Preise dürfen umbrechen statt abgeschnitten zu werden */
  .price-row { flex-wrap: wrap; }
  .price-row__leader { display: none; }
  .price-row__name { flex: 1 1 100%; }
  .price-row__price {
    flex: 1 1 100%;
    white-space: normal;
    margin-top: .3rem;
    font-size: .95rem;
  }

  /* ---- Hero füllt den Start vollständig ----
     Zwischenzeitlich stand hier 76svh, damit die nächste Sektion
     hereinlugt. Auf Ansage von Matteo (07.09.2026) wieder entfernt: Beim
     Ankommen soll AUSSCHLIESSLICH der Hero zu sehen sein, keine
     angeschnittene Folgeüberschrift. Ohne eigene Angabe gilt damit der
     Basiswert 100svh — svh ist die kleine Viewport-Höhe, also genau der
     Zustand mit ausgefahrener Browserleiste. Der gewonnene Platz geht in
     die offene Bildfläche zwischen Überschrift und Handlungsteil. */
  /* Die Bewertungszeile trägt am Desktop dunkle Textrollen, weil sie dort
     unterhalb des Schleiers auf dem hellen Foto sitzt. Am Telefon reicht
     die Wäsche jetzt bis an die Unterkante — auf diesem dunklen Grund
     brauchen Wert und Zusatz die hellen Rollen. Gemessen nach der
     Umstellung: siehe docs/AUDIT.md. */
  .hero .rating__value { color: var(--text-on-deep); }
  .hero .rating__note { color: var(--text-on-deep-muted); }

  /* ---- Behandlungs-Karussell: Ränder weich auslaufen ----
     Die angeschnittenen Nachbarkarten schnitten ihre Beschriftung mitten
     im Wort ab („…massage", „F…") — das las sich wie ein Fehler. Dieselbe
     Maskentechnik wie beim Stimmen-Band lässt die Kante ausblenden statt
     abschneiden. 13 % je Seite: Mit 6 % war die Auslaufzone nur 23 px
     breit und die angeschnittenen Wörter blieben klar lesbar — die
     Maske half dann gar nichts. */
  .oval-row {
    -webkit-mask-image: linear-gradient(90deg, transparent 0%, var(--mask-solid) 13%, var(--mask-solid) 87%, transparent 100%);
            mask-image: linear-gradient(90deg, transparent 0%, var(--mask-solid) 13%, var(--mask-solid) 87%, transparent 100%);
  }

  /* ---- Studio-Fächer wird am Telefon ein Coverflow-Band ----
     Der Fächer legt sieben Kacheln überlappend nebeneinander. Am Telefon
     blieben davon 104–133 px je Foto — man erkannte nichts, und genau
     das Erkennen ist die Aufgabe dieser Sektion. Stattdessen ein
     wischbares Band wie beim Behandlungs-Karussell: das mittlere Foto
     steht groß und flach, die Nachbarn kippen perspektivisch weg.
     Ohne JS und bei Reduced Motion bleibt schlicht ein normales
     Wischband übrig — voll bedienbar, nichts sieht kaputt aus. */
  .fan__stage {
    /* 70vw: 58 war zu zaghaft, die Fotos blieben klein. Bei 70 bleibt
       links und rechts trotzdem genug Rand für die angeschnittenen
       Nachbarn (rund 58px je Seite bei 390px Bildbreite). */
    --karte: 66vw;
    height: auto;
    display: flex;
    align-items: center;
    gap: .6rem;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    /* Kürzere Brennweite als zuvor (900px): Die Kippung der Nachbarn wird
       dadurch spürbar räumlich statt nur schräg. */
    perspective: 760px;
    perspective-origin: 50% 50%;
    margin-inline: calc(-1 * var(--gutter));
    padding-inline: calc((100vw - var(--karte)) / 2);
    /* Luft für Kippung und Schatten, bevor overflow schneidet */
    padding-block: 1.25rem;
    scrollbar-width: none;
  }
  .fan__stage::-webkit-scrollbar { display: none; }
  /* ---- Die beiden Telefon-Fotos und die Schleifen-Klone einblenden ----
     Beide tragen das hidden-Attribut: die zusätzlichen Fotos im HTML, die
     Klone gesetzt von main.js. Am Desktop bleiben sie damit ohne eine
     einzige Regel außerhalb dieses Blocks aus dem Rendering — der Fächer
     dort ist weiter genau die Fünfer-Komposition mit den :nth-child-
     Plätzen. Hier gehören sie zum Band und werden wieder eingeblendet;
     die Klone tragen zusätzlich aria-hidden, damit Screenreader die
     Fotofolge nur einmal vorfinden. */
  .fan__stage .fan__photo[hidden] { display: block; }
  /* ---- Solange main.js das Band führt, rastet das Skript ein ----
     Das native Einrasten misst seine Flächen an der TRANSFORMIERTEN
     Kachel, also samt Kippung. Die Kippung wiederum hängt am Scrollstand.
     Beides treibt sich gegenseitig: Der Browser rastet auf die gekippte
     Mitte, die Kippung stellt sich auf den neuen Stand ein, der Browser
     rastet nach — gemessen als Nachkriechen um rund ein Pixel je Bild,
     das nach 800 ms noch lief. Das Band kam so nie zum Stillstand, und im Stillstand rechnet
     die Schleife ihren Scrollstand zurück. Deshalb schaltet main.js das
     native Einrasten ab, sobald es das Band führt, und scrollt selbst
     weich zur nächsten Kartenmitte (reine Layoutrechnung, ohne Kippung).
     Ohne JS bleibt das native Einrasten von oben unangetastet. */
  .fan__stage.is-frei { scroll-snap-type: none; }
  /* Das eine Bild, in dem die Schleife den Scrollstand zurücksetzt: Die
     Karten, die durch den Sprung neu ins Bild rücken, tragen noch die
     starke Kippung ihrer alten Lage weit außerhalb. Bliebe die 150-ms-
     Überblendung an, drehten sie sichtbar nach; so springen sie sofort
     auf den Wert, den sie an dieser Stelle ohnehin hätten. */
  .fan__stage.is-springt .fan__photo,
  .fan__stage.is-springt .fan__photo img { transition: none; }
  .fan__photo {
    /* Die Fächer-Keyframe rechnet mit --fx/--fy/--fr. Auf null gesetzt
       wird aus dem Auffächern ein schlichtes Aufblenden an Ort und
       Stelle — so bleibt EIN Transform-Besitzer, ohne die Reveal-Regel
       mit !important zu überstimmen. */
    --fx: 0; --fy: 0; --fr: 0deg;
    position: relative;
    top: auto;
    left: auto;
    flex: 0 0 var(--karte);
    width: var(--karte);
    margin: 0;
    scroll-snap-align: center;
    /* Hochformat statt des quadratischen Desktop-Zuschnitts: bei gleicher
       Breite rund ein Drittel mehr Bildfläche — der wirksamste Hebel gegen
       „zu klein", ohne den Nachbarn ihren Platz zu nehmen. */
    aspect-ratio: 3 / 4;
    /* --neigung/--tiefe/--skala/--deckkraft/--drehpunkt schreibt main.js,
       sonst gelten die neutralen Vorgaben und das Band ist einfach flach.
       Der Drehpunkt sitzt auf der zur Mitte zeigenden Kante: Die Karten
       klappen dadurch weg wie Buchseiten, statt um ihre eigene Mitte zu
       schwenken. Mit Drehpunkt „center" schwang die nahe Kante weg und vom
       Nachbarn blieben nur 19–29px sichtbarer Splitter. */
    transform-origin: var(--drehpunkt, center);
    transform:
      rotateY(var(--neigung, 0deg))
      translateZ(var(--tiefe, 0px))
      scale(var(--skala, 1));
    opacity: var(--deckkraft, 1);
    transition: transform .15s linear, opacity .15s linear;
  }
  /* Am Telefon gibt es kein echtes Hover; bliebe die Regel aktiv, würde
     ein hängengebliebener Tap-Zustand die Kippung überschreiben. */
  .fan__photo:hover {
    transform-origin: var(--drehpunkt, center);
    transform:
      rotateY(var(--neigung, 0deg))
      translateZ(var(--tiefe, 0px))
      scale(var(--skala, 1));
    box-shadow: var(--shadow-fan);
  }
  /* Parallaxe IM Bild: Das Foto ist etwas breiter als sein Rahmen und
     wandert darin gegen die Wischrichtung. Dadurch wirkt der Rahmen wie
     ein Fenster, hinter dem der Raum liegt — das ist der Unterschied
     zwischen „gekippte Kachel" und Tiefe. Nur transform, keine Layoutlast. */
  .fan__photo img {
    width: 124%;
    /* Ohne max-width:none klemmt die globale Bildregel (max-width:100%)
       die Überbreite ab. Der Rahmen zeigte dann rechts einen cremefarbenen
       Streifen seines eigenen Hintergrunds. */
    max-width: none;
    margin-left: -12%;
    transform: translateX(var(--bildversatz, 0%));
    transition: transform .15s linear;
  }
}

/* ============================================================
   Phase 4 (vorgezogen) — Motion & Mikrointeraktionen
   Architektur nach scroll-motion-architektur:
   - Verstecken NUR unter html.js UND (prefers-reduced-motion: no-preference)
     → ohne JS und für Reduced-Motion ist immer alles sichtbar
   - Nur transform/opacity/Farbe; JS schreibt ausschließlich CSS-Variablen
   - Ein Element = EIN Transform-Besitzer (Reveals räumen sich nach dem
     Ausspielen selbst weg, siehe main.js)
   ============================================================ */

/* ---- Marken-Details (fast gratis) ---- */
::selection { background: var(--text-primary); color: var(--surface-base); }

html {
  scrollbar-width: thin;
  /* Erst der einfache Wert, dann der gemischte: Browser ohne color-mix
     (Safari vor 16.2) behalten so einen getönten Balken statt des
     Standard-Graus. Diese beiden Mischungen verrechnen zwei deckende
     Farben und lassen sich nicht als Deckkraft schreiben — anders als der
     Rest des Farbsystems, siehe Kanal-Tokens in tokens.css. */
  scrollbar-color: var(--text-muted) transparent;
  scrollbar-color: color-mix(in srgb, var(--text-muted) 70%, var(--surface-base)) transparent;
}
::-webkit-scrollbar { width: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--text-muted);
  background: color-mix(in srgb, var(--text-muted) 55%, var(--surface-base));
  border-radius: 999px;
  border: 2px solid var(--surface-base);
}
::-webkit-scrollbar-thumb:hover { background: var(--text-primary); }

/* Sprungmarke über den Seitenkopf hinweg (WCAG 2.4.1). Liegt außerhalb des
   Bildes, bis sie den Fokus bekommt — kein visuelles Verstecken per
   display:none, sonst wäre sie nicht mehr anspringbar. */
.skip-link {
  position: absolute;
  top: 0;
  left: var(--gutter);
  z-index: 100;
  transform: translateY(-120%);
  padding: .7rem 1.1rem;
  border-radius: 0 0 var(--radius-sm) var(--radius-sm);
  background: var(--surface-deep);
  color: var(--text-on-deep);
  font-weight: 600;
  text-decoration: none;
  transition: transform var(--dur-fast) var(--ease);
}
/* Bewusst :focus statt :focus-visible — die Marke ist ohnehin nur per
   Tastatur erreichbar, und sie muss auch dann erscheinen, wenn ein Browser
   die Sichtbarkeits-Heuristik anders auslegt. */
.skip-link:focus {
  transform: translateY(0);
  outline: 2px solid var(--text-on-deep);
  outline-offset: -6px;
}
/* Ziel der Sprungmarke: nimmt den Fokus an, ohne selbst tabbar zu werden. */
main:focus { outline: none; }

/* Der Fokusring wechselt mit der Fläche, auf der er liegt: auf hellem Grund
   die dunkelste Plum-Stufe (12,9:1), auf den dunklen Flächen das warme Weiß
   (15,0:1). Ohne diese Umschaltung lag --text-primary auf --surface-deep bei
   1,21:1 und war in Kopf, Menü, Hero und Footer praktisch unsichtbar. */
:focus-visible { outline: 2px solid var(--focus-ring, var(--text-primary)); outline-offset: 3px; }
.site-header,
.nav-panel,
.hero,
.page-head,
.surface-deep { --focus-ring: var(--text-on-deep); }

/* Goldene Scroll-Fortschritts-Haarlinie: reines CSS (Scroll-Timeline),
   ohne Browser-Support bleibt sie schlicht unsichtbar — kein JS beteiligt. */
.scroll-progress { display: none; }
@supports (animation-timeline: scroll()) {
  .scroll-progress {
    display: block;
    position: fixed;
    top: 0; left: 0;
    width: 100%; height: 2px;
    z-index: 60;
    pointer-events: none;
    transform-origin: 0 50%;
    transform: scaleX(0);
    background: var(--accent-secondary);
    animation: progress-grow auto linear both;
    animation-timeline: scroll(root);
    view-transition-name: scroll-progress;
  }
  @keyframes progress-grow { from { transform: scaleX(0); } to { transform: scaleX(1); } }
}

/* ---- Scroll-Reveals: Fade + Rise + Blur-Fokus, Stagger 80 ms ----
   main.js entfernt data-reveal nach dem Ausspielen wieder (animationend),
   damit Hover-Transforms (Karten, Preiszeilen) den Transform zurückbekommen. */
@media (prefers-reduced-motion: no-preference) {
  html.js [data-reveal]:not(.in) { opacity: 0; }
  html.js [data-reveal].in {
    animation: reveal-rise var(--dur-reveal) var(--ease) both;
    animation-delay: var(--reveal-delay, 0ms);
  }
  /* Tabellenzeilen: nur Fade (Transforms auf tr sind fragil) */
  html.js tr[data-reveal].in { animation-name: reveal-fade; }
  /* Kapsel-Karten: Aufsteigen + sanftes Wachsen aus der Unschärfe */
  html.js [data-reveal="pop"].in { animation-name: reveal-pop; }

  /* Eyebrow-Strich unter den Preis-Überschriften zeichnet sich von links */
  html.js .price-section h2:not(.in)::after { transform: scaleX(0); }

  /* Studio-Fächer: die Bühne selbst nur einblenden, die Kacheln fächern
     gestaffelt aus der Mitte auf. Selektor hängt an .in (bleibt), nicht
     an data-reveal (wird von main.js nach dem ersten animationend
     entfernt) — sonst würden die späteren Kacheln abgeschnitten.
     Fill "backwards": nach dem Ausspielen gilt wieder der Grundzustand,
     damit der Hover-Transform frei ist. */
  html.js .fan__stage[data-reveal].in { animation-name: reveal-fade; }
  html.js .fan__stage.in .fan__photo {
    animation: fan-spread .85s var(--ease) backwards;
    /* 85 statt 110 ms je Stufe (13.09.2026): Mit sieben Kacheln sind es
       sechs Stufen statt vier. Bei 110 ms hätte die äußerste erst nach
       660 ms begonnen und der Fächer über 1,5 s gebraucht; mit 85 ms
       bleibt der Aufbau bei rund 1,3 s und damit im selben Tempo wie
       vorher. */
    animation-delay: calc(var(--fan-order, 0) * 85ms);
  }
  .fan__photo:nth-child(1) { --fan-order: 6; }
  .fan__photo:nth-child(2) { --fan-order: 4; }
  .fan__photo:nth-child(3) { --fan-order: 2; }
  .fan__photo:nth-child(4) { --fan-order: 0; }
  .fan__photo:nth-child(5) { --fan-order: 1; }
  .fan__photo:nth-child(6) { --fan-order: 3; }
  .fan__photo:nth-child(7) { --fan-order: 5; }
  @keyframes fan-spread {
    from {
      opacity: 0;
      transform: translate(0, calc(var(--fan-tile) * .12)) rotate(0deg) scale(.92);
    }
    to {
      opacity: 1;
      transform:
        translate(calc(var(--fan-tile) * var(--fx) * var(--fan-spread)),
                  calc(var(--fan-tile) * var(--fy)))
        rotate(var(--fr));
    }
  }
}
[data-reveal-delay="1"] { --reveal-delay: 80ms; }
[data-reveal-delay="2"] { --reveal-delay: 160ms; }
[data-reveal-delay="3"] { --reveal-delay: 240ms; }
[data-reveal-delay="4"] { --reveal-delay: 320ms; }
[data-reveal-delay="5"] { --reveal-delay: 400ms; }
[data-reveal-delay="6"] { --reveal-delay: 480ms; }
[data-reveal-delay="7"] { --reveal-delay: 560ms; }
@keyframes reveal-rise {
  from { opacity: 0; transform: translateY(26px); filter: blur(6px); }
  to   { opacity: 1; transform: none; filter: none; }
}
@keyframes reveal-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes reveal-pop {
  from { opacity: 0; transform: translateY(30px) scale(.94); filter: blur(6px); }
  to   { opacity: 1; transform: none; filter: none; }
}

/* Goldstrich unter den Katalog-Überschriften (codiert den Kategorienwechsel) */
.price-section h2::after {
  content: '';
  display: block;
  width: 2.6rem;
  height: 2px;
  margin-top: .45rem;
  background: linear-gradient(90deg, var(--accent-secondary), transparent);
  transform-origin: left center;
  transition: transform .7s var(--ease) .18s;
}

/* ---- Hero-Entrance beim Direktaufruf (bei Seiten-Wisch unterdrückt,
   damit sich Ankunfts-Animationen nicht stapeln) ---- */
@media (prefers-reduced-motion: no-preference) {
  html.js:not([data-nav-dir]) .site-header { animation: header-drop .7s var(--ease) both .05s; }
  html.js:not([data-nav-dir]) .hero__title { animation: hero-rise .85s var(--ease) both .12s; }
  html.js:not([data-nav-dir]) .hero__sub   { animation: hero-rise .85s var(--ease) both .24s; }
  html.js:not([data-nav-dir]) .hero__cta   { animation: hero-rise .85s var(--ease) both .36s; }
  html.js:not([data-nav-dir]) .hero__foot  { animation: hero-rise .85s var(--ease) both .5s; }
}
@keyframes hero-rise  { from { opacity: 0; transform: translateY(26px); } }
@keyframes header-drop { from { opacity: 0; transform: translateY(-14px); } }

/* ---- Goldfolien-Sheen: Licht wandert langsam durch das Akzentwort ---- */
@media (prefers-reduced-motion: no-preference) {
  .hero__title em {
    background: linear-gradient(105deg,
      var(--accent) 0%, var(--accent-light) 30%, var(--accent-sheen) 46%,
      var(--accent-light) 60%, var(--accent) 90%);
    -webkit-background-clip: text;
    background-clip: text;
    background-size: 230% 100%;
    animation: gold-sheen 7.5s ease-in-out infinite alternate;
    filter: drop-shadow(0 0 16px var(--glow-accent));
  }
  @keyframes gold-sheen {
    from { background-position: 0% 0; }
    to   { background-position: 100% 0; }
  }
}

/* ---- Glanz-Sweep über die Gold-CTAs (Badge-Shimmer-Motiv, alle ~7,5 s) ---- */
.btn-gold, .btn-cta { position: relative; overflow: hidden; }
@media (prefers-reduced-motion: no-preference) {
  .btn-gold::after, .btn-cta::after {
    content: '';
    position: absolute;
    top: -25%; bottom: -25%;
    left: 0;
    width: 36%;
    pointer-events: none;
    background: linear-gradient(100deg, transparent, var(--sheen) 50%, transparent);
    transform: translateX(-170%) skewX(-18deg);
    animation: btn-shine 7.5s var(--ease) infinite;
    animation-delay: 2.4s;
  }
  .btn-cta::after { animation-delay: 5.8s; }
  @keyframes btn-shine {
    0%   { transform: translateX(-170%) skewX(-18deg); }
    17%  { transform: translateX(480%) skewX(-18deg); }
    100% { transform: translateX(480%) skewX(-18deg); }
  }
}

/* ---- Magnetische Gold-CTAs (Desktop): JS schreibt nur --mx/--my.
   Die Transform selbst steht in der Button-Regel der Politur-Region
   (komponiert aus Magnet, Lift und Druck) — hier nur die kürzere
   Transform-Transition, damit der Zug direkt am Cursor hängt. ---- */
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  [data-magnet] {
    transition: background var(--dur-fast) var(--ease),
                color var(--dur-fast) var(--ease),
                box-shadow var(--dur-fast) var(--ease),
                border-color var(--dur-fast) var(--ease),
                transform .22s var(--ease);
  }
}

/* ---- Parallax-Tiefe (Desktop, nur wenn Modul aktiv: html.fx-parallax) ----
   Drei Ebenen aus einem Modul: Hintergrundbilder (negative Geschwindigkeit)
   bleiben zurück, der Hero-Textblock (positive) zieht leicht voraus. JS
   schreibt nur --py; die Obergrenze je Element steht als data-parallax-max
   im HTML. Die allgemeine Regel zuerst, die Bild-Regeln mit Skalierung
   überschreiben sie. */
html.fx-parallax [data-parallax] { transform: translate3d(0, var(--py, 0px), 0); }
html.fx-parallax .hero__inner { will-change: transform; }
html.fx-parallax .hero__bg {
  transform: translate3d(0, var(--py, 0px), 0) scale(1.12);
  will-change: transform;
}
/* Unterseiten-Kopf: dieselbe Tiefe wie der Hero. Die Skalierung deckt die
   Verschiebung (max. 20 px bei mindestens 220 px Bannerhöhe). */
html.fx-parallax .page-head img[data-parallax] {
  transform: translate3d(0, var(--py, 0px), 0) scale(1.18);
  will-change: transform;
}
html.fx-parallax [data-drift] { transform: translate3d(0, var(--dy, 0px), 0); }

/* ---- Stimmen-Bänder: „ruhiger Strom“ (Desktop, nur wenn Modul aktiv).
   Ohne html.fx-voices bleibt das Band ein normal wischbares Scroll-Band —
   also ohne JS, ohne Hover-Zeiger und bei prefers-reduced-motion.
   JS schreibt ausschließlich --vx, nie style.transform. ---- */
html.fx-voices .voices-band { overflow: hidden; }
html.fx-voices .voices-track {
  padding-inline: 0;
  transform: translate3d(var(--vx, 0px), 0, 0);
  will-change: transform;
}

/* ---- Preiszeilen: Zeile rückt vor, Punktlinie leuchtet gold ---- */
.price-row__leader { transition: border-color var(--dur-fast) var(--ease); }
@media (hover: hover) and (pointer: fine) {
  .price-row { transition: transform var(--dur) var(--ease); }
  .price-row:hover { transform: translateX(6px); }
  .price-row:hover .price-row__leader { border-bottom-color: var(--accent-secondary); }
}

/* ---- „Heute“ in den Öffnungszeiten: getönte Zeile + kräftigerer Balken ---- */
.hours-table .is-today { background: hsl(var(--text-primary-hsl) / 5%); }
.hours-table .is-today th,
.hours-table .is-today td { color: var(--text-primary); font-weight: 700; }
.hours-table .is-today th::before { width: 4px; height: 1.45em; background: var(--accent-secondary); }

/* ---- Seiten-Wisch (Cross-Document View Transitions) ----
   Progressiv: ohne Browser-Support normaler Seitenwechsel. Die Leiste
   bleibt als eigene Ebene stehen; die goldene Aktiv-Pille gleitet als
   Shared Element zum neuen Tab. Richtung (fwd/back) setzt das Inline-
   Skript im <head> aus der Tab-Reihenfolge. ---- */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }

  .site-header { view-transition-name: site-header; }
  /* Der Name sitzt am Gleiter, nicht mehr am Link: So morpht eine reine
     Fläche von Platz zu Platz, statt zwei Beschriftungen ineinander zu
     blenden. Fehlt der Gleiter (kein JS), gibt es hier auch nichts zu
     morphen — den Platzwechsel trägt dann ohnehin niemand. */
  .nav-pill__thumb { view-transition-name: active-tab; }
  /* Die Tab-Texte bekommen eigene Ebenen, damit sie ÜBER dem wandernden
     Gleiter liegen. Ein verschachteltes Element wird aus dem Schnappschuss
     seines Vorfahren herausgelöst und danach gezeichnet — ohne eigene
     Ebene lägen die Texte im Kopf-Schnappschuss UNTER der weißen Fläche
     und wären während der Bewegung verdeckt (Befund 07.09.2026: „Feld
     weiß, Schrift geht nicht durch"). Reihenfolge ergibt sich aus der
     Zeichenfolge (Gleiter z 0 vor Texten z 1) und ist per z-index
     zusätzlich festgezurrt. Die Texte blenden an Ort und Stelle von hell
     auf dunkel, im Takt des Gleiters. */
  .nav-pill a[data-page="start"]      { view-transition-name: tab-start;      view-transition-class: nav-tab; }
  .nav-pill a[data-page="leistungen"] { view-transition-name: tab-leistungen; view-transition-class: nav-tab; }
  .nav-pill a[data-page="kontakt"]    { view-transition-name: tab-kontakt;    view-transition-class: nav-tab; }

  ::view-transition-group(active-tab) {
    z-index: 1;
    animation-duration: var(--dur-morph);
    animation-timing-function: var(--ease);
  }
  ::view-transition-group(.nav-tab) {
    z-index: 2;
    animation-duration: var(--dur-morph);
    animation-timing-function: var(--ease);
  }
  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation-duration: var(--dur-page);
    animation-timing-function: var(--ease);
  }
  ::view-transition-old(root) { animation-name: vt-out-left; }
  ::view-transition-new(root) { animation-name: vt-in-right; }
  html[data-nav-dir="back"]::view-transition-old(root) { animation-name: vt-out-right; }
  html[data-nav-dir="back"]::view-transition-new(root) { animation-name: vt-in-left; }

  @keyframes vt-out-left  { to   { opacity: 0; transform: translateX(-5%); } }
  @keyframes vt-in-right  { from { opacity: 0; transform: translateX(5%); } }
  @keyframes vt-out-right { to   { opacity: 0; transform: translateX(5%); } }
  @keyframes vt-in-left   { from { opacity: 0; transform: translateX(-5%); } }
}

/* ============================================================
   Politur-Durchgang 07.09.2026 — Mikrointeraktionen, wo noch keine waren
   Konzept: EINE Bewegungssprache (--dur/--ease), nur transform/opacity/
   Farbe, Hover nur für (hover: hover)-Geräte, alles Reduced-Motion-sicher,
   ohne JS kein Artefakt. Die größeren Momente bleiben orchestriert
   (Ankunft, Tiefe beim Scrollen, Überschriften-Reveal); alles andere ist
   klein und ruhig. Was NICHT gebaut wurde und warum: docs/CHANGELOG.md.
   ============================================================ */

/* ---- Buttons: Lift beim Hover, Druck beim Klick — als Variablen in EINER
   komponierten Transform, damit der Magnet-Zug (--mx/--my aus JS) und der
   Lift (--lift) sich nie gegenseitig überschreiben. --press gibt auch am
   Handy ein spürbares Klick-Feedback. ---- */
.btn-gold, .btn-cta, .btn-ghost, .btn-outline {
  --lift: 0px;
  --press: 1;
  transform: translate(var(--mx, 0px), calc(var(--my, 0px) + var(--lift))) scale(var(--press));
}
.btn-gold:active, .btn-cta:active, .btn-ghost:active, .btn-outline:active { --press: .97; --lift: 0px; }
/* Pfeil-Icons rücken beim Hover ein Stück in ihre eigene Richtung (oben rechts) */
.btn-cta__icon svg, .btn-ghost__icon svg { transition: transform var(--dur) var(--ease); }
.btn-ghost__icon { transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease); }
@media (hover: hover) and (pointer: fine) {
  .btn-gold:hover, .btn-cta:hover, .btn-ghost:hover, .btn-outline:hover { --lift: -2px; }
  .btn-gold:active, .btn-cta:active, .btn-ghost:active, .btn-outline:active { --lift: 0px; }
  .btn-cta:hover .btn-cta__icon svg,
  .btn-ghost:hover .btn-ghost__icon svg { transform: translate(1px, -1px); }
  /* Der Pfeil-Kreis des Hero-Sekundärbuttons zündet beim Hover in Gold */
  .hero__cta .btn-ghost:hover .btn-ghost__icon { background: var(--accent); color: var(--text-on-accent); }
}

/* ---- Logo-Badge: wächst beim Hover der Wortmarke minimal (Kopf und Fuß) ---- */
.brand__badge { transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease); }
@media (hover: hover) and (pointer: fine) {
  .brand:hover .brand__badge,
  .footer-brand:hover .brand__badge { transform: scale(1.07); box-shadow: var(--shadow-deep); }
}

/* ---- Hero-Bewertung: die fünf Sterne heben sich beim Hover nacheinander;
   die Staffelung gilt NUR im Hover-Zustand, zurück fallen alle gemeinsam. ---- */
.rating__stars svg { transition: transform var(--dur) var(--ease); }
@media (hover: hover) and (pointer: fine) {
  .rating--link:hover .rating__stars svg { transform: scale(1.18); }
  .rating--link:hover .rating__stars svg:nth-child(2) { transition-delay: 45ms; }
  .rating--link:hover .rating__stars svg:nth-child(3) { transition-delay: 90ms; }
  .rating--link:hover .rating__stars svg:nth-child(4) { transition-delay: 135ms; }
  .rating--link:hover .rating__stars svg:nth-child(5) { transition-delay: 180ms; }
}

/* ---- Sektions-Überschriften kommen eine Spur größer und langsamer an als
   die Zeilen darunter — der eine „größere“ Moment je Sektion. ---- */
@media (prefers-reduced-motion: no-preference) {
  html.js :is(.h-ref, .price-section h2, .contact-sub)[data-reveal].in {
    animation-name: reveal-rise-lg;
    animation-duration: .95s;
  }
  @keyframes reveal-rise-lg {
    from { opacity: 0; transform: translateY(36px); filter: blur(8px); }
    to   { opacity: 1; transform: none; filter: none; }
  }
}

/* ---- Unterseiten-Kopf: Ankunft wie im Hero (Titel und Zeile steigen auf),
   beim Seiten-Wisch unterdrückt, damit sich nichts stapelt. ---- */
@media (prefers-reduced-motion: no-preference) {
  html.js:not([data-nav-dir]) .page-head h1 { animation: hero-rise .85s var(--ease) both .12s; }
  html.js:not([data-nav-dir]) .page-head p  { animation: hero-rise .85s var(--ease) both .24s; }
}

/* ---- Studio-Fächer: derselbe feine Lichtring wie in den Behandlungs-
   Kapseln legt sich beim Hover in die Kachel — ein Motiv für alle
   Fotorahmen der Seite. ---- */
.fan__photo::after {
  content: '';
  position: absolute;
  inset: 8px;
  border-radius: calc(var(--radius-lg) - 8px);
  border: 1px solid hsl(var(--text-on-deep-hsl) / 85%);
  opacity: 0;
  transform: scale(.96);
  transition: opacity .45s var(--ease), transform .45s var(--ease);
  pointer-events: none;
}
@media (hover: hover) and (pointer: fine) {
  .fan__photo:hover::after { opacity: .9; transform: scale(1); }
}

/* ---- Behandlungs-Kapseln: kurzer Druck beim Klick/Tipp (Desktop wie Handy);
   steht nach den Hover-Regeln, damit der Druck den Lift ablöst. ---- */
.oval-card:active .oval-card__media { transform: scale(.985); }

/* ---- Handy-Karussell: die mittlere Kapsel steht voll da, die angeschnittenen
   Nachbarn treten leicht zurück — rein per CSS-View-Timeline, nur auf
   Touch-Geräten (dort läuft kein Drift auf derselben Ebene, ein Element
   bleibt EIN Transform-Besitzer). Ohne Browser-Support bleibt schlicht
   alles gleich groß. ---- */
@supports (animation-timeline: view()) {
  @media (max-width: 760px) and (hover: none) {
    .oval-card__float {
      animation: card-focus linear both;
      animation-timeline: view(inline);
    }
    @keyframes card-focus {
      0%   { transform: scale(.92); opacity: .6; }
      50%  { transform: scale(1);   opacity: 1; }
      100% { transform: scale(.92); opacity: .6; }
    }
  }
}

/* ---- FAQ: die Antwort blendet beim Öffnen weich ein und rückt nach; die
   Goldlinie zeigt beim Hover einer geschlossenen Frage ein kurzes Stück
   als Vorgriff; das Chevron nickt beim Hover in seine Richtung. ---- */
@media (prefers-reduced-motion: no-preference) {
  .faq-item[open] .faq-item__a { animation: faq-answer-in .6s var(--ease) both .12s; }
  @keyframes faq-answer-in {
    from { opacity: 0; transform: translateY(-.35rem); }
    to   { opacity: 1; transform: none; }
  }
}
.faq-item__marker svg { transition: transform var(--dur-fast) var(--ease); }
@media (hover: hover) and (pointer: fine) {
  .faq-item summary:hover .faq-item__marker svg { transform: translateY(1px); }
}

/* ---- Goldlicht wandert am Kartenrand mit dem Cursor ----
   Ersetzt den früheren Vorgriff, der als kurzer Strich fest in der linken
   oberen Ecke saß. Jetzt liegt ein goldener Lichtkegel an der Cursor-
   position, den eine Maske auf die Innenkante der Karte beschneidet: Es
   leuchtet immer das Randstück, das dem Zeiger am nächsten ist.
   Zwei übereinandergelegte Masken lassen nur den Ring stehen — die innere
   (content-box) wird von der äußeren abgezogen, `padding` ist die
   Strichstärke. Ohne Masken-Unterstützung entfällt der Ring komplett
   statt als Fläche über der Karte zu liegen.
   Nur Paint, kein Layout: JS schreibt ausschließlich --gx/--gy. */
@supports ((-webkit-mask-composite: xor) or (mask-composite: exclude)) {
  html.fx-faq-edge .faq-item::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 2;
    pointer-events: none;
    padding: 1.5px;
    border-radius: calc(var(--radius) - 1px);
    background: radial-gradient(12rem 7rem at var(--gx, 50%) var(--gy, 50%),
      var(--accent-light) 0%, var(--accent) 34%, transparent 70%);
    -webkit-mask: linear-gradient(var(--mask-solid) 0 0) content-box,
                  linear-gradient(var(--mask-solid) 0 0);
            mask: linear-gradient(var(--mask-solid) 0 0) content-box,
                  linear-gradient(var(--mask-solid) 0 0);
    -webkit-mask-composite: xor;
            mask-composite: exclude;
    opacity: 0;
    transition: opacity var(--dur) var(--ease);
  }
  html.fx-faq-edge .faq-item:hover::after { opacity: 1; }
}

/* ---- Kontaktseite und Preisliste: Zeilen antworten auf den Cursor ----
   Kontaktzeilen: Icon hebt sich, Trennlinie wechselt auf Gold (dasselbe
   Motiv wie die Punktlinie der Preiszeilen). Öffnungszeiten: Zeile tönt
   sich, der Goldbalken streckt sich. Preiszeilen: die Beschreibung tritt
   aus dem Nebentext hervor. */
.contact-lines li { transition: border-color var(--dur) var(--ease); }
.contact-line__icon { transition: transform var(--dur) var(--ease); }
.hours-table tbody tr { transition: background var(--dur-fast) var(--ease); }
.hours-table th::before { transition: transform var(--dur-fast) var(--ease); }
.price-row__name small { transition: color var(--dur-fast) var(--ease); }
@media (hover: hover) and (pointer: fine) {
  .contact-lines li:hover { border-bottom-color: var(--line-accent); }
  .contact-lines li:hover .contact-line__icon { transform: translateY(-2px); }
  .hours-table tbody tr:not(.is-today):hover { background: hsl(var(--text-primary-hsl) / 3%); }
  .hours-table tbody tr:hover th::before { transform: translateY(-50%) scaleY(1.3); }
  .price-row:hover .price-row__name small { color: var(--text-body); }
}

/* ---- Reduced Motion: Pflicht ---- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  /* Ohne Bewegung bleiben die Karussell-Pfeile stehen — voll sichtbar,
     und die vertikale Zentrierung darf nicht verloren gehen. */
  .carousel-arrow { opacity: 1; transform: translateY(-50%) !important; }
  /* Scroll-gebundene Animationen haben keine Dauer, die sich kürzen ließe:
     Karussell-Fokus aus, alle Kapseln gleich groß. */
  .oval-card__float { animation: none; }
  /* Aus demselben Grund greift die Dauer-Kürzung unten auch bei der
     Fortschritts-Haarlinie nicht — sie hängt an der Scrollposition. Sie ist
     rein dekorativ und entfällt hier. */
  .scroll-progress { display: none; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}
