/* ==========================================================================
   Schönleben Photography — Design System
   Editorial magazine style: cream / white / deep green.
   ========================================================================== */

@font-face {
  font-family: 'Cormorant Garamond';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('../fonts/cormorant-garamond-variable.woff2') format('woff2');
}
@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 300 800;
  font-display: swap;
  src: url('../fonts/manrope-variable.woff2') format('woff2');
}

/* ---------- Design tokens ---------- */
:root {
  /* Colors */
  --color-bg: #FAF7F2;
  --color-bg-alt: #FFFFFF;
  --color-surface: #FFFFFF;
  --color-ink: #18160F;
  --color-ink-soft: #4A4638;
  /* Darkened from #8C8672 (was 3.41:1 on --color-bg, under the WCAG AA
     4.5:1 minimum for text) — same warm-greige hue, just deep enough to
     read reliably. Palette otherwise untouched. */
  --color-muted: #696455;
  --color-border: #E1D9C8;
  --color-dark: #1B2A20;
  --color-dark-ink: #F5F1E6;
  --color-dark-muted: #9FAE9C;
  --color-dark-border: #30412F;
  --color-focus: #18160F;
  /* Restrained accent, used only for nav-link hover, the active DE/EN
     toggle and the slider arrows — nowhere else (eyebrows etc. keep the
     neutral --color-muted above). Two shades so it clears WCAG AA 4.5:1
     on both light and dark section backgrounds. */
  --color-accent: #8A6A2A;
  --color-accent-dark: #C9A24B;

  /* Typography */
  --font-display: 'Cormorant Garamond', 'Times New Roman', serif;
  --font-body: 'Manrope', -apple-system, BlinkMacSystemFont, sans-serif;

  --fs-body: 1rem;
  --fs-small: 0.8125rem;
  --fs-label: 0.75rem;
  --fs-h1: clamp(2.25rem, 3.6vw + 1rem, 4.5rem);
  --fs-h2: clamp(1.85rem, 1.3vw + 1rem, 2.375rem);
  --fs-h3: clamp(1.35rem, 1vw + 1rem, 1.75rem);
  --fs-lead: clamp(1.05rem, 0.6vw + 1rem, 1.375rem);

  --lh-tight: 1.05;
  --lh-heading: 1.2;
  --lh-body: 1.7;

  --tracking-label: 0.18em;
  --tracking-wide: 0.06em;

  /* Spacing (spacious scale) */
  --space-1: 0.5rem;
  --space-2: 1rem;
  --space-3: 1.5rem;
  --space-4: 2rem;
  --space-5: 3rem;
  --space-6: 4.5rem;
  --space-7: 6rem;
  --space-8: 8rem;
  --space-9: 10rem;

  --container-max: 1400px;
  --container-pad: clamp(1.25rem, 4vw, 4rem);

  /* Motion */
  --ease-default: cubic-bezier(.65, 0, .35, 1);
  --ease-out: cubic-bezier(.16, 1, .3, 1);
  --dur-fast: 220ms;
  --dur-base: 420ms;
  --dur-slow: 700ms;

  --radius-none: 0px;
}

/* ---------- Reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; padding: 0; }

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

body {
  background: var(--color-bg);
  color: var(--color-ink);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img, video { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }
ul { list-style: none; }
input, textarea, select { font: inherit; color: inherit; }

::selection { background: var(--color-ink); color: var(--color-bg); }

:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 3px;
}

/* ---------- Typography ---------- */
h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 500;
  line-height: var(--lh-heading);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

h1 { font-size: var(--fs-h1); line-height: var(--lh-tight); }

/* Location/SEO pages: H1 shares a row with an image, so it uses the
   secondary heading scale to match the H2s used everywhere else */
html[data-page^="loc"] h1 { font-size: var(--fs-h2); }
/* Contact: heading sits as a lead-in above the intro paragraph rather
   than as a page title, so it runs two steps down the scale (user request) */
html[data-page="contact"] h1 { font-size: var(--fs-h3); }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); }

.eyebrow {
  display: inline-block;
  font-family: var(--font-body);
  font-size: var(--fs-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-muted);
  margin-bottom: var(--space-2);
}

.lead {
  font-size: var(--fs-body);
  font-family: var(--font-body);
  font-weight: 400;
  color: var(--color-ink-soft);
  line-height: 1.6;
}

.serif { font-family: var(--font-display); }

/* ---------- Layout ---------- */
.container {
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--container-pad);
}

.section { padding-block: var(--space-8); }
.section--tight { padding-block: var(--space-6); }
/* The homepage's 5-image feature slider and the SEO pages' 3-image trio are
   both immediately followed by a text-only section; on mobile the combined
   padding (this section's bottom + the next one's top) read as way too much
   empty space. Tighten just these two cases, not .section--tight generally. */
@media (max-width: 767px) {
  .section--tight:has(.feature-slider-wrap),
  .section--tight:has(.seo-trio) { padding-bottom: var(--space-3); }
  /* SEO trio specifically: its gap to the hero text above (hero's default
     72px bottom + trio's default 72px top = 144px) read as much bigger
     than its gap to the extra-text below (already-tightened 24px bottom
     above + extra-text's own reduced 64px top = 88px). Bring trio's own
     top down to 16px so both sides land on the same 88px, without
     touching the hero section itself. Feature-slider-wrap is unaffected
     (not part of this request). */
  .section--tight:has(.seo-trio) { padding-top: var(--space-2); }
}
.section--dark { background: var(--color-dark); color: var(--color-dark-ink); }
.section--alt { background: var(--color-bg-alt); }

/* ---------- Unified spacing at every section background-color change (mobile only) ---------- */
/* Reference gap picked by the user 2026-09-07: about.html's intro→duo
   transition, 72px trailing + 128px leading = 200px combined. Detects any
   plain/.section--alt/.section--dark boundary, in either direction, and
   applies that same split. Mobile-only per explicit correction — this was
   first written without a media query, which also changed desktop (not
   wanted); desktop keeps whatever each section's own original inline
   style/base class already gave it. !important is needed here because a
   few of these sections carry their own original inline padding style
   (kept for desktop), which a plain external rule can never outrank
   regardless of specificity — only !important (or moving the value out of
   inline entirely) can win at the mobile breakpoint without touching that
   inline value's desktop behavior. .hero sections are excluded. */
@media (max-width: 767px) {
  section:not(.section--alt):not(.hero) + section.section--alt,
  section:not(.section--dark):not(.hero) + section.section--dark,
  section.section--alt + section:not(.section--alt):not(.section--dark):not(.hero),
  section.section--dark + section:not(.section--alt):not(.section--dark):not(.hero) {
    padding-top: var(--space-8) !important;
  }

  section:not(.section--alt):not(.hero):has(+ section.section--alt),
  section:not(.section--dark):not(.hero):has(+ section.section--dark),
  section.section--alt:has(+ section:not(.section--alt):not(.section--dark):not(.hero)),
  section.section--dark:has(+ section:not(.section--alt):not(.section--dark):not(.hero)) {
    padding-bottom: var(--space-6) !important;
  }
}

.rule {
  border: none;
  border-top: 1px solid var(--color-border);
}

.section--dark .rule { border-color: var(--color-dark-border); }
.section--dark .eyebrow { color: var(--color-dark-muted); }
.section--dark .lead { color: var(--color-dark-muted); }

.grid {
  display: grid;
  gap: var(--space-3);
}

@media (min-width: 768px) {
  .grid--2 { grid-template-columns: repeat(2, 1fr); }
  .grid--3 { grid-template-columns: repeat(3, 1fr); }
}
@media (min-width: 1024px) {
  .grid--4 { grid-template-columns: repeat(4, 1fr); }
}

/* Homepage "Vanessa Schönleben" intro: heading/text/media as named grid
   areas so the layout can reshuffle per breakpoint without display:contents
   (which would silently break the GSAP [data-reveal] fade on the heading/
   text, since animating opacity on a boxless element has no visual effect).
   Desktop: heading+text stacked in one column, image fills the other,
   full row height (matches the old single-div layout this replaced).
   Mobile: image shrinks into a small thumbnail next to the heading (row 1,
   vertically centered against each other), body text drops below full
   width (row 2) — per explicit user request 2026-09-07. */
@media (min-width: 768px) {
  /* heading+text share ONE grid area ("content") via the wrapper below,
     instead of each being its own row spanned by the tall image. That
     spanning was the actual cause of the huge heading→text gap: CSS Grid
     inflates a short row to help satisfy a tall row-spanning item's height,
     even with the gap itself set to a tiny value — confirmed by inspecting
     the resolved row track sizes (heading's own row was computed at 164px
     tall for a 42px-tall heading). A single-row layout with heading+text
     stacking in normal flow inside one wrapper item sidesteps that
     entirely, matching how this looked before the mobile rework. */
  .home-intro-grid {
    grid-template-columns: 1fr 1fr;
    grid-template-areas: "content media";
    align-items: start;
  }
  .home-intro-grid__content { grid-area: content; }
  .home-intro-grid__heading { margin-bottom: var(--space-1); }
}
@media (max-width: 767px) {
  .home-intro-grid {
    grid-template-columns: 1fr clamp(150px, 46vw, 210px);
    grid-template-areas: "heading media" "text text";
    /* Name is also the heading for the text below it, so it anchors to
       the BOTTOM of the image's height (closer to the text that follows)
       instead of floating centered against the image — explicit user
       request 2026-09-08. */
    align-items: end;
    column-gap: var(--space-3);
  }
  /* Let heading/text escape the desktop wrapper and act as independent
     grid items again, so the "heading media" / "text text" areas above
     can place them individually. Safe here (unlike the wrapper this
     project avoided elsewhere for [data-reveal]): data-reveal sits on the
     heading/text themselves, not on this wrapper, so display:contents on
     the wrapper doesn't touch anything GSAP is animating. */
  .home-intro-grid__content { display: contents; }
  html[data-page="home"] .home-intro-grid .img-overlap-up {
    max-width: 100%;
    margin: 0;
    position: static;
  }
  /* Heading a bit smaller, image a bit bigger, per explicit user request
     2026-09-07 (was the plain --fs-h2 clamp, flat at 1.85rem on mobile).
     Targets the nested h2, not .home-intro-grid__heading itself — that
     class now sits on the eyebrow+h2 wrapper, and h2's own element-level
     font-size rule would otherwise win over a class rule on its parent. */
  .home-intro-grid__heading h2 { font-size: 1.2rem; overflow-wrap: break-word; }
  .home-intro-grid__heading .eyebrow { font-size: 10px; }

  /* "Meine Handschrift", mobile: pull the whole block (image+heading+text,
     kept exactly as currently arranged — image is the tallest thing in
     row 1, so it sets that row's height and its own top edge lines up
     with the grid's own top) up as one unit, so the image overlaps 20%
     into the white quote section above (bumped up from an initial ~10%
     per explicit follow-up request 2026-09-08). -11.5rem = this section's
     own 128px padding-top + ~56px actual overlap (20% of the ~269px
     rendered image height at 390px width) — same "clear the padding-top
     first" math used for the tablet/Standort versions of this overlap. */
  .home-intro-grid { margin-top: -11.5rem; }
  /* Extend the white background it's overlapping into, so there's real
     breathing room between the quote's own text and the image — re-tuned
     after the overlap above went from ~10% to 20% (the extra overlap ate
     into the same fixed cushion, measured gap grew to 152px, clearly
     bigger than every other gap on this page). 144px (128+16) lands the
     gap back around 64px, checked live after the change. Scoped to the
     home page's quote specifically (also used verbatim on about.html,
     which isn't part of this request). Needs !important (the quote
     section carries its own inline padding-bottom) AND enough extra
     specificity to beat the general mobile color-transition system above
     (line ~211), which already targets this same section.section--alt at
     equal !important — a first attempt at equal specificity silently lost
     to it. The redundant but true .section--alt + .quote--wide qualifiers
     push this rule's specificity clearly above the general one instead of
     relying on a source-order tiebreak. */
  html[data-page="home"] section.section--alt:has(> .container.quote.quote--wide) {
    padding-bottom: calc(var(--space-8) + var(--space-2)) !important;
  }
}
.home-intro-grid__heading { grid-area: heading; }
.home-intro-grid__text { grid-area: text; }
.home-intro-grid__media { grid-area: media; }

/* ---------- Buttons & links ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--fs-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  padding: 1.15em 2.4em;
  border: 1px solid currentColor;
  border-radius: var(--radius-none);
  background-color: transparent;
  white-space: nowrap;
  transition: background-color 0.5s var(--ease-default), color 0.5s var(--ease-default), border-color 0.5s var(--ease-default);
}

.btn:hover, .btn:focus-visible {
  background-color: var(--color-dark);
  border-color: var(--color-dark);
  color: var(--color-dark-ink);
}

.section--dark .btn:hover, .section--dark .btn:focus-visible {
  background-color: var(--color-dark-ink);
  border-color: var(--color-dark-ink);
  color: var(--color-dark);
}

.btn--ghost { border-color: transparent; padding-inline: 0; }
.btn--ghost:hover, .btn--ghost:focus-visible { background: none; color: inherit; }

.text-link {
  font-size: var(--fs-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  padding-bottom: 0.3em;
  opacity: 0.55;
  transition: opacity var(--dur-base) var(--ease-default), color var(--dur-base) var(--ease-default);
}

.text-link:hover, .text-link:focus-visible, .text-link.is-active { opacity: 1; color: var(--color-accent); }
.section--dark .text-link:hover, .section--dark .text-link:focus-visible, .section--dark .text-link.is-active,
.site-footer .text-link:hover, .site-footer .text-link:focus-visible, .site-footer .text-link.is-active {
  color: var(--color-accent-dark);
}

/* Inline link within running text: no label styling, just a subtle underline */
.link-inline {
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-color: var(--color-muted);
}

/* ---------- Header / Navigation ---------- */
.site-header {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  padding-block: var(--space-3);
  transition: transform var(--dur-base) var(--ease-default), background var(--dur-base) var(--ease-default), padding var(--dur-base) var(--ease-default), border-color var(--dur-base) var(--ease-default), color var(--dur-base) var(--ease-default);
  border-bottom: 1px solid transparent;
}

/* Any page whose hero already has a real photo (not just the placeholder
   icon) needs light nav text plus a small top scrim for legibility,
   since the header sits directly on that photo before scrolling —
   regardless of which .hero--* variant the page uses. Kept short
   (roughly the header's own ~68px height plus a brief fade) so it only
   ever covers the menu strip itself, not the photo below it — a taller
   scrim visibly shifts the photo's own colors over too much of the
   image, which the user does not want (photos must render in their
   original color). */
body:has(.hero__media img) .site-header {
  color: var(--color-dark-ink);
}

body:has(.hero__media img) .site-header::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 100px;
  background: linear-gradient(180deg, rgba(27,42,32,0.5) 0%, rgba(27,42,32,0.2) 60%, rgba(27,42,32,0) 100%);
  z-index: -1;
  pointer-events: none;
  transition: opacity var(--dur-base) var(--ease-default);
}

.site-header.is-scrolled {
  background: rgba(250, 247, 242, 0.92);
  backdrop-filter: blur(10px);
  padding-block: var(--space-2);
  border-color: var(--color-border);
}

/* Higher specificity than the "body:has(.hero__media img) .site-header" rule
   above (which also matches here) so the scrolled state reliably wins
   back to dark text + hides the top scrim. */
body:has(.hero__media img) .site-header.is-scrolled {
  color: var(--color-ink);
}

body:has(.hero__media img) .site-header.is-scrolled::before { opacity: 0; }

.site-header.is-hidden { transform: translateY(-100%); }

.site-header .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

@media (min-width: 1024px) {
  .site-header .container {
    display: grid;
    grid-template-columns: 1fr auto 1fr auto;
    align-items: center;
    gap: var(--space-4);
  }
}

.logo {
  font-family: var(--font-display);
  font-size: clamp(0.95rem, 4.5vw, 1.3rem);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  white-space: nowrap;
}

.nav-desktop {
  display: none;
}

@media (min-width: 1024px) {
  .nav-desktop {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    font-size: var(--fs-label);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
  }
  .nav-desktop--left { justify-self: end; margin-right: var(--space-7); }
  .nav-desktop--right { justify-self: start; margin-left: var(--space-7); }
}

.nav-right {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.lang-switch {
  display: flex;
  align-items: center;
  gap: 0.4em;
  font-size: var(--fs-label);
  letter-spacing: var(--tracking-label);
}

/* Below 1024px the header's own DE/EN moves into the mobile menu instead
   (.mobile-menu__lang, styled separately above) — hide only this instance,
   not .lang-switch generally, so the mobile-menu's copy stays visible. */
@media (max-width: 1023px) {
  .site-header .lang-switch { display: none; }
}

/* Tablet only (768-1023px): .nav-desktop is hidden below 1024px, so the
   header container's plain flex space-between has nothing on the left to
   balance the logo against — it reads flush-left instead of centered.
   Absolute-center it instead, regardless of the hamburger/lang group's own
   width on the right. Briefly extended to mobile too (2026-09-09) but
   reverted the same day — the centered look wasn't wanted there after all,
   mobile keeps the plain flush-left flex layout from the base rule. */
@media (min-width: 768px) and (max-width: 1023px) {
  .site-header .container { position: relative; justify-content: flex-end; }
  .site-header .logo { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); }
}

.lang-switch a { opacity: 0.4; transition: opacity var(--dur-fast), color var(--dur-fast); }
.lang-switch a.is-active { opacity: 1; color: var(--color-accent); text-decoration: underline; text-underline-offset: 3px; text-decoration-color: currentColor; }
.lang-switch a:hover { opacity: 1; }
.mobile-menu .lang-switch a.is-active,
.hero .lang-switch a.is-active,
.site-header:not(.is-scrolled) .lang-switch a.is-active { color: var(--color-accent-dark); }

.menu-toggle {
  display: flex;
  flex-direction: column;
  gap: 5px;
  width: 26px;
  z-index: 210;
}

@media (min-width: 1024px) { .menu-toggle { display: none; } }

.menu-toggle span {
  display: block;
  height: 1px;
  background: currentColor;
  transition: transform var(--dur-base) var(--ease-default), opacity var(--dur-fast);
}

.menu-toggle.is-open { color: var(--color-dark-ink); }
.menu-toggle.is-open span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.menu-toggle.is-open span:nth-child(2) { opacity: 0; }
.menu-toggle.is-open span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

.mobile-menu {
  position: fixed;
  inset: 0;
  background: var(--color-dark);
  color: var(--color-dark-ink);
  z-index: 200;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: var(--container-pad);
  transform: translateY(-100%);
  transition: transform var(--dur-slow) var(--ease-default);
}

.mobile-menu.is-open { transform: translateY(0); }

.mobile-menu ul { display: flex; flex-direction: column; gap: var(--space-2); }

.mobile-menu a {
  font-family: var(--font-display);
  font-size: clamp(2rem, 8vw, 3rem);
  text-transform: uppercase;
  letter-spacing: 0.02em;
  display: inline-block;
}

.mobile-menu .mobile-menu__footer {
  margin-top: var(--space-3);
  display: flex;
  gap: var(--space-3);
  font-size: var(--fs-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-dark-muted);
}

/* .mobile-menu a above sets a big display-font size for the nav links;
   the footer handle is a small label, not a nav heading, so reset it. */
.mobile-menu .mobile-menu__footer a {
  font-family: var(--font-body);
  font-size: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  display: inline;
}

/* DE/EN moved here from the closed-state header on mobile (see .site-header
   .lang-switch below) — sits top-left, roughly where the old close-arrow
   button used to be. */
.mobile-menu__lang {
  position: absolute;
  top: var(--container-pad);
  left: var(--container-pad);
  color: var(--color-dark-ink);
}

/* Closes the menu; now styled as plain 3 lines (matching the closed-state
   .menu-toggle look, not an X or arrow) and moved to top-right, mirroring
   where .menu-toggle sits when the menu is closed. */
.mobile-menu__close {
  position: absolute;
  top: var(--container-pad);
  right: var(--container-pad);
  display: flex;
  flex-direction: column;
  gap: 5px;
  width: 26px;
  padding: 0;
  background: none;
  border: none;
  color: var(--color-dark-ink);
  cursor: pointer;
}
.mobile-menu__close span {
  display: block;
  height: 1px;
  background: currentColor;
}

@media (min-width: 1024px) { .mobile-menu { display: none; } }

/* ---------- Hero ---------- */
.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  align-items: flex-end;
  overflow: hidden;
}

.hero__media { position: absolute; inset: 0; z-index: 0; }
.hero__media .ph-image { height: 100%; border-radius: 0; }

.hero__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(27,42,32,0.15) 0%, rgba(27,42,32,0.45) 100%);
  z-index: 1;
}

.hero__content {
  position: relative;
  z-index: 2;
  color: var(--color-dark-ink);
  padding-block: var(--space-6);
  width: 100%;
}

.hero__content .eyebrow { color: var(--color-dark-muted); }

.hero--simple {
  min-height: 75svh;
  align-items: center;
}

/* Home: full-bleed photo, single headline set low in the frame, nothing else */
.hero--cover {
  min-height: 100svh;
  align-items: flex-end;
  justify-content: center;
  text-align: center;
}

/* No dark scrim here: the photo should show in its natural color, unlike
   the .hero--simple pages (e.g. Contact) where the general scrim above applies. */
.hero--cover .hero__scrim {
  background: none;
}

.hero--cover .hero__content {
  display: flex;
  justify-content: center;
  padding-bottom: var(--space-8);
}

.hero--cover h1 {
  font-size: clamp(1.25rem, 6vw, 2.375rem);
}

/* Plain text-only page heading (no photo) for inner pages */
.page-heading {
  padding-top: calc(var(--space-9) + var(--space-2));
  padding-bottom: var(--space-5);
  text-align: center;
}

/* ---------- Placeholder image component ---------- */
.ph-image {
  position: relative;
  width: 100%;
  aspect-ratio: var(--ph-ratio, 4 / 5);
  background: linear-gradient(155deg, #EFE9DC 0%, #E4DCC9 60%, #DCD3BC 100%);
  overflow: hidden;
  isolation: isolate;
}

.ph-image::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: repeating-linear-gradient(45deg, rgba(24,22,15,0.035) 0 2px, transparent 2px 12px);
  mix-blend-mode: multiply;
}

/* Real photos/video don't need the placeholder texture/tint */
.ph-image:has(img, video)::before {
  content: none;
}

.ph-image__icon {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: rgba(24, 22, 15, 0.28);
}

.ph-image__icon svg { width: 15%; min-width: 28px; max-width: 56px; height: auto; }

.ph-image__label {
  position: absolute;
  left: var(--space-2);
  bottom: var(--space-2);
  right: var(--space-2);
  font-size: var(--fs-label);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: rgba(24, 22, 15, 0.4);
}

.ph-image--video .ph-image__icon { color: rgba(24,22,15,0.4); }
.ph-image--video::after {
  content: '';
  position: absolute;
  inset: 0;
  border: 1px dashed rgba(24, 22, 15, 0.25);
  margin: 10px;
  pointer-events: none;
}

/* Autoplay hero video: no placeholder tint behind it (so any rounding
   gap shows the section's own dark background, not a light stripe).
   The footage has its own vintage black frame baked in, with roughly
   even thickness on all four straight edges but hard 90° corners —
   this radius is calibrated to just smooth that corner turn so the
   black frame stays visibly consistent all the way round, rather
   than cropping the corners away to nothing (too small a radius
   leaves the hard corner; too large erases the frame at the corner
   entirely — this sits between the two).
   The radius is applied on the <video> element itself too, not just
   this wrapper: once the video is actually decoding/playing (as
   opposed to showing the static poster), some browsers promote it to
   its own GPU compositing layer that ignores an ancestor's
   overflow:hidden + border-radius clip, so the corners silently go
   sharp again after playback starts — a known cross-browser video
   quirk, not a caching issue. Rounding the video element directly
   keeps the corners correct in both states. */
.hero-video {
  --video-radius: 28px;
  background: transparent;
  border-radius: var(--video-radius);
}
.hero-video video {
  border-radius: var(--video-radius);
}

/* Video feature section (home): video takes more width than the text
   column, with a wider gap between them than the standard .grid--2. */
@media (min-width: 768px) {
  .video-feature-grid { grid-template-columns: 1.4fr 1fr; gap: var(--space-6); }
}

/* ratio helpers */
.ph-image--square { --ph-ratio: 1 / 1; }
.ph-image--wide { --ph-ratio: 16 / 10; }
.ph-image--tall { --ph-ratio: 3 / 4; }
.ph-image--full { --ph-ratio: auto; height: 100%; }

/* ---------- Portfolio grid & cards ---------- */
.portfolio-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-2);
}

@media (min-width: 768px) { .portfolio-grid { grid-template-columns: repeat(3, 1fr); gap: var(--space-3); } }

.portfolio-card { position: relative; overflow: hidden; }
.portfolio-card .ph-image { transition: transform var(--dur-slow) var(--ease-default); }
.portfolio-card:hover .ph-image { transform: scale(1.035); }

.portfolio-card__tag {
  position: absolute;
  top: var(--space-2);
  left: var(--space-2);
  font-size: var(--fs-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  background: var(--color-bg);
  padding: 0.4em 0.8em;
  z-index: 2;
}

/* Masonry-ish variant using row spans */

/* Trio of images for SEO location pages: always 3 equal columns side by
   side, each cropped to 2:3 — same format as the weddings.html/lifestyle.html
   galleries, per explicit user request 2026-09-07 (supersedes the earlier
   "2 stacked + 1 tall lead" layout entirely, at every viewport width). */
.seo-trio {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-3);
}

/* SEO location pages' top "next to text" hero photo — shrunk to 80% at
   every viewport per explicit user request 2026-09-07 (was full-width on
   desktop/tablet, 75% on mobile only); text stays vertically centered next
   to it via the grid's own align-items:center, which just re-centers
   against the now-shorter row height automatically. */
.seo-hero-image { max-width: 80%; margin-inline: auto; }
/* Mobile: image moves above the heading/text instead of following it in
   source order — text-div keeps default order:0, so this alone is enough
   to reorder the row without touching the text side. */
@media (max-width: 767px) {
  .seo-hero-image { order: -1; }
}

/* ---------- Filter tabs ---------- */
.filter-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
  font-size: var(--fs-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
}

.filter-tabs button {
  position: relative;
  padding-bottom: 0.5em;
  color: var(--color-muted);
  transition: color var(--dur-fast);
}

.filter-tabs button::after {
  content: '';
  position: absolute;
  left: 0; bottom: 0;
  width: 100%; height: 1px;
  background: var(--color-ink);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-base) var(--ease-out);
}

.filter-tabs button.is-active { color: var(--color-ink); }
.filter-tabs button.is-active::after { transform: scaleX(1); }

/* ---------- Carousel ---------- */
.carousel { position: relative; }

.carousel__track {
  display: flex;
  gap: var(--space-3);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  padding-bottom: var(--space-1);
}

.carousel__track::-webkit-scrollbar { display: none; }

.carousel__track > * {
  flex: 0 0 auto;
  width: min(78vw, 460px);
  scroll-snap-align: start;
}

.carousel__nav {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.carousel__nav button {
  width: 44px; height: 44px;
  border: 1px solid var(--color-border);
  display: flex; align-items: center; justify-content: center;
  transition: background var(--dur-fast), border-color var(--dur-fast);
}

.carousel__nav button:hover { background: var(--color-ink); color: var(--color-bg); border-color: var(--color-ink); }
.carousel__nav svg { width: 16px; height: 16px; }

/* Large feature slider — bigger frames, e.g. signature-work style gallery */
.carousel--large .carousel__track { gap: var(--space-4); }
.carousel--large .carousel__track > * { width: min(70vw, 640px); }

/* ---------- Feature slider: center slide larger than its neighbours, real gaps, no overlap ---------- */
.feature-slider-wrap { display: grid; gap: var(--space-4); }

.feature-slider {
  position: relative;
  height: min(78vw, 600px);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

.feature-slider__track {
  position: relative;
  width: min(40vw, 380px);
  height: 100%;
}
@media (max-width: 767px) {
  /* On mobile the 40vw/78vw split (width/height, both viewport-relative
     below their px caps) landed on a fixed ~0.51:1 frame — narrower and
     more portrait than the site's usual 2:3 photo crop, per explicit user
     complaint 2026-09-07. Let width derive from the unchanged height via
     aspect-ratio instead, landing on exactly 2:3. Desktop untouched. */
  .feature-slider__track { width: auto; aspect-ratio: 2 / 3; }
}

.feature-slider__slide {
  position: absolute;
  inset: 0;
  transition: transform 0.8s var(--ease-default), opacity 0.8s var(--ease-default);
}

.feature-slider__slide .ph-image { height: 100%; width: 100%; }

.feature-slider__nav {
  display: flex;
  justify-content: space-between;
  max-width: var(--container-max);
  margin-inline: auto;
  width: 100%;
  padding-inline: var(--container-pad);
}

.feature-slider__arrow {
  z-index: 20;
  color: var(--color-ink);
  padding: var(--space-1) 0;
  transition: opacity 0.4s var(--ease-default), transform 0.4s var(--ease-default), color 0.4s var(--ease-default);
}

.feature-slider__arrow svg { width: 56px; height: 15px; display: block; }
.feature-slider__arrow:hover { opacity: 1; color: var(--color-accent); }
.feature-slider__arrow--prev:hover { transform: translateX(-5px); }
.feature-slider__arrow--next:hover { transform: translateX(5px); }

/* ---------- Before / After slider ---------- */
.compare {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  cursor: ew-resize;
  user-select: none;
  touch-action: pan-y;
}

.compare__after,
.compare__before {
  position: absolute;
  inset: 0;
}

.compare__before {
  clip-path: inset(0 calc(100% - var(--pos, 50%)) 0 0);
  will-change: clip-path;
}

.compare__before .ph-image,
.compare__after .ph-image { height: 100%; width: 100%; }

.compare__handle {
  position: absolute;
  top: 0; bottom: 0;
  left: var(--pos, 50%);
  width: 1px;
  background: var(--color-bg);
  transform: translateX(-0.5px);
  pointer-events: none;
}

.compare__handle::before {
  content: '';
  position: absolute;
  top: 50%; left: 50%;
  width: 44px; height: 44px;
  border-radius: 50%;
  background: var(--color-bg);
  transform: translate(-50%, -50%);
}

.compare__handle::after {
  content: '↔';
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -55%);
  font-size: 1rem;
  color: var(--color-ink);
}

.compare__label {
  position: absolute;
  bottom: var(--space-2);
  font-size: var(--fs-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  background: rgba(250,247,242,0.85);
  padding: 0.4em 0.8em;
  z-index: 3;
}

.compare__label--before { left: var(--space-2); }
.compare__label--after { right: var(--space-2); }

/* ---------- Duo / About section ---------- */
.duo {
  display: grid;
  gap: var(--space-4);
}

@media (min-width: 900px) { .duo { grid-template-columns: repeat(2, 1fr); gap: var(--space-6); } }

.duo__member { display: grid; align-content: start; gap: var(--space-3); justify-items: center; text-align: center; }
.duo__member .ph-image { max-width: 340px; width: 100%; }
.duo__role { color: var(--color-dark-muted); }
/* 75% of the original mobile size (was 70%, bumped up same day). */
@media (max-width: 767px) {
  .duo__member .ph-image { max-width: 75%; }
}

/* ---------- Quote ---------- */
.quote {
  max-width: 46rem;
  margin-inline: auto;
  text-align: center;
}

.quote blockquote {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  line-height: 1.4;
  letter-spacing: 0.01em;
}

/* The Vanessa+Robert photo is actually two stacked photos combined into one
   tall image (--ph-ratio ~0.38), so at full mobile width it reads far too
   large. Shrunk to ~65% of its former (100%) mobile width per explicit user
   request 2026-09-07 — both stacked halves scale down together. */
@media (max-width: 767px) {
  .about-intro-grid .ph-image { max-width: 65%; margin-inline: auto; }
}

@media (min-width: 768px) {
  .about-intro-grid { grid-template-columns: 1fr 4fr; gap: var(--space-5); }
}

@media (min-width: 1200px) {
  .about-intro-grid { grid-template-columns: 247px 470px; justify-content: center; }
}

.quote--wide {
  max-width: 58rem;
}

.quote--wide blockquote {
  font-size: clamp(1.15rem, 0.7vw + 1rem, 1.5rem);
}

.quote cite {
  display: block;
  margin-top: var(--space-3);
  font-style: normal;
  font-size: var(--fs-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-muted);
}

/* ---------- Testimonial grid (3-up) ---------- */
.testimonial-grid {
  display: grid;
  gap: var(--space-6);
  text-align: center;
}

@media (min-width: 900px) {
  .testimonial-grid { grid-template-columns: repeat(3, 1fr); gap: var(--space-4); text-align: left; }
}

.testimonial-grid blockquote {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--fs-body);
  line-height: 1.6;
  font-style: italic;
}

.testimonial-grid cite {
  display: block;
  margin-top: var(--space-2);
  font-style: normal;
  font-size: var(--fs-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-muted);
}

/* ---------- FAQ accordion (native details/summary, no JS) ---------- */
.faq {
  max-width: 38rem;
  margin-inline: auto;
}

.faq-item {
  border-top: 1px solid var(--color-border);
}

.faq-item:last-child { border-bottom: 1px solid var(--color-border); }

.faq-item summary {
  cursor: pointer;
  padding-block: var(--space-2);
  font-family: var(--font-body);
  font-weight: 500;
  text-transform: none;
  letter-spacing: normal;
  font-size: 0.95rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  list-style: none;
}

.faq-item summary::-webkit-details-marker { display: none; }

.faq-item summary::after {
  content: '+';
  flex: none;
  font-family: var(--font-body);
  font-size: 1.2rem;
  line-height: 1;
  transition: transform var(--dur-fast) var(--ease-default);
}

.faq-item[open] summary::after { transform: rotate(45deg); }

.faq-item p {
  padding-bottom: var(--space-2);
  max-width: 38rem;
  font-size: var(--fs-small);
  color: var(--color-ink-soft);
}

/* ---------- Forms ---------- */
.form-grid {
  display: grid;
  gap: var(--space-3);
}

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

.field { display: flex; flex-direction: column; gap: 0.6em; }

.field label {
  font-size: var(--fs-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-muted);
}

.field input,
.field select,
.field textarea {
  background: transparent;
  border: none;
  border-bottom: 1px solid var(--color-border);
  padding-block: 0.7em;
  font-size: 1.05rem;
  transition: border-color var(--dur-fast);
}

.field textarea { resize: vertical; min-height: 7em; }

.field input:focus,
.field select:focus,
.field textarea:focus { border-color: var(--color-ink); outline: none; }

.field--error input,
.field--error textarea,
.field--error select { border-color: #A33B2E; }

.field__error {
  font-size: var(--fs-small);
  color: #A33B2E;
  display: none;
}

.checkbox-label {
  display: flex;
  align-items: flex-start;
  gap: 0.75em;
  cursor: pointer;
  font-size: var(--fs-small);
  color: var(--color-ink-soft);
  line-height: 1.5;
}

.checkbox-label input[type="checkbox"] {
  flex: none;
  width: 18px;
  height: 18px;
  margin-top: 0.15em;
  accent-color: var(--color-ink);
}

/* Beats the generic ".field label" rule (uppercase field-name styling)
   that would otherwise leak into a checkbox label nested in a .field. */
.field .checkbox-label {
  font-size: var(--fs-small);
  letter-spacing: normal;
  text-transform: none;
  color: var(--color-ink-soft);
}

.field--error .checkbox-label { color: #A33B2E; }

.field--error .field__error { display: block; }

.form-note {
  font-size: var(--fs-small);
  color: var(--color-muted);
}

.form-status {
  margin-top: var(--space-3);
  font-size: var(--fs-small);
  letter-spacing: var(--tracking-wide);
  display: none;
}

.form-status.is-visible { display: block; }
.form-status.is-success { color: #3F6B4A; }
.form-status.is-error { color: #A33B2E; }

/* Placeholder marker for content the client will fill in later */
.fill-marker {
  display: inline-block;
  padding: 0.15em 0.5em;
  background: repeating-linear-gradient(45deg, #F2ECE1, #F2ECE1 6px, #E7DFCD 6px, #E7DFCD 12px);
  border: 1px dashed var(--color-muted);
  font-size: 0.85em;
  color: var(--color-ink-soft);
}

/* ---------- Journal ---------- */
.journal-card { display: grid; gap: var(--space-2); }
.journal-card time {
  font-size: var(--fs-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-muted);
}

.notice {
  border: 1px dashed var(--color-border);
  padding: var(--space-3);
  font-size: var(--fs-small);
  color: var(--color-ink-soft);
}

/* ---------- Footer ---------- */
.site-footer {
  background: var(--color-dark);
  color: var(--color-dark-ink);
  padding-block: var(--space-6) var(--space-4);
}

.footer-grid {
  display: grid;
  gap: var(--space-5);
}

@media (min-width: 900px) { .footer-grid { grid-template-columns: 1.4fr 1fr 1fr; } }

.footer-grid h4 {
  font-family: var(--font-body);
  font-size: var(--fs-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-dark-muted);
  margin-bottom: var(--space-2);
  font-weight: 500;
}

.footer-grid ul { display: grid; gap: 0.6em; }

.footer-bottom {
  margin-top: var(--space-6);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-dark-border);
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--space-2);
  font-size: var(--fs-small);
  color: var(--color-dark-muted);
}

/* ---------- Reveal animation base states (GSAP toggles .is-visible) ---------- */
[data-reveal] { opacity: 0; transform: translateY(28px); }
[data-reveal].is-visible { opacity: 1; transform: none; }

[data-reveal-fade] { opacity: 0; }
[data-reveal-fade].is-visible { opacity: 1; }

.js-loaded [data-reveal],
.js-loaded [data-reveal-fade] {
  transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out);
}

/* ---------- Utilities ---------- */
.u-center { text-align: center; }
.u-visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
.u-mt-1 { margin-top: var(--space-1); }
.u-mt-2 { margin-top: var(--space-2); }
.u-mt-3 { margin-top: var(--space-3); }
.u-mt-4 { margin-top: var(--space-4); }
.u-mt-5 { margin-top: var(--space-5); }
.u-mb-3 { margin-bottom: var(--space-3); }
.u-max-prose { max-width: 42rem; }
.u-max-prose--center { max-width: 42rem; margin-inline: auto; }
.u-flex-between { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }

/* ---------- Join the socials: 5-image Instagram grid ---------- */
/* Mobile: flex-wrap so a ragged last row (5 items / 3 per row = 2 left over)
   centers itself instead of sitting left-aligned in fixed grid columns. */
.social-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 2px;
}
.social-grid a { flex: 0 0 calc((100% - 4px) / 3); }
@media (min-width: 640px) {
  .social-grid { display: grid; grid-template-columns: repeat(6, 1fr); }
}

.social-grid a {
  display: block;
  overflow: hidden;
}

.social-grid .ph-image { transition: transform var(--dur-slow) var(--ease-default); }
.social-grid a:hover .ph-image { transform: scale(1.05); }

/* Image breaks out of its section, overlapping upward into the section above */
.img-overlap-up {
  max-width: 75%;
  margin-inline: auto;
  margin-top: -16rem;
  position: relative;
  z-index: 2;
}
@media (max-width: 899px) {
  .img-overlap-up { max-width: 78%; margin-top: -6rem; }
}

/* ==========================================================================
   Tablet-only overrides (768–1023px — the range where the mobile hamburger
   nav is still used (.menu-toggle only hides at 1024px) but .grid--2 is
   already 2-column, so this project treats it as its own zone rather than
   lumping it into "mobile" or "desktop"). Everything in this block is
   explicitly scoped to that range; neither phone nor real desktop is
   touched. Per explicit user request 2026-09-08. */
@media (min-width: 768px) and (max-width: 1023px) {
  /* "Meine Handschrift" (home): text pulled up further per follow-up
     feedback (was -space-4/-32px, still read as too low), image overlap
     scaled back from the full desktop -16rem — that swallowed Vanessa's
     whole head into the section above. Note margin-top has to cover this
     section's own 128px padding-top (var(--space-8)) *before* any of it
     becomes visible overlap into the section above — a first attempt at
     -4rem (64px) looked like no overlap at all for exactly that reason.
     -12rem = 128px padding-top + ~64px actual overlap, i.e. roughly the
     top ~17% of the image at any tablet width (checked at 768px/1023px),
     just the wall/backdrop above her — per the user's own "her head
     should already be on the lower background" calibration, confirmed
     visually via screenshot afterward, not just computed. */
  .home-intro-grid__content { margin-top: calc(-1 * var(--space-6)); }
  html[data-page="home"] .img-overlap-up { max-width: 75%; margin-top: -12rem; }

  /* "Standort" (about): same idea, text up a bit less far than home's
     (explicit "etwas höher" vs home's "noch höher"), image sized up a
     touch and set to overlap by ~25% of its own (new, taller) rendered
     height — 25% of the checked 768/1023px heights is 88.7px/119.8px,
     plus the same 128px padding-top this section also has to clear first
     (see the home comment above). ~104px average overlap + 128px = -14.5rem. */
  .img-overlap-text { margin-top: calc(-1 * var(--space-5)); }
  html[data-page="about"] .img-overlap-up { max-width: 85%; margin-top: -14.5rem; }

  /* Both sections' gap to whatever follows read as too large once the
     image above is overlapping this dramatically — tighten it, reusing
     the same 72px/128px split already established (and user-approved) for
     the equivalent mobile case, rather than inventing new numbers. */
  .section:has(.img-overlap-up) { padding-bottom: var(--space-6); }
  .section:has(.img-overlap-up) + .section { padding-top: var(--space-8); }

  /* About "Vanessa und Robert" intro photo: was 1fr:4fr (image:text) same
     as here — reads far too small at tablet's narrower container. Flip
     toward the image. minmax(0,Nfr) rather than bare Nfr — plain fr tracks
     default to an implicit auto minimum (their content's min-content), so
     without this the text column's own min-content silently overrode the
     intended ratio (confirmed via getComputedStyle: bare 1.6fr/1fr rendered
     as ~1.13:1, not 1.6:1, until wrapped in minmax(0, …)). */
  .about-intro-grid { grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); }

  /* SEO location pages: the side-by-side attempt didn't work out (the
     shrunk text column wrapped the heading taller instead of getting a
     smaller font, so the enlarged image ended up cutting into it) — redo
     as a single stacked column instead, same order as mobile (image,
     then heading+text, then the trio, then the extra text) but with a
     noticeably bigger hero image than the standing 80% base rule. */
  .grid--2:has(.seo-hero-image) { grid-template-columns: 1fr; }
  .seo-hero-image { order: -1; max-width: 90%; margin-bottom: var(--space-4); }

  /* Same trio-to-neighbours tightening as the established mobile pattern
     (hero's default 72px bottom + trio's reduced 16px top = 88px, trio's
     reduced 24px bottom + extra-text's own 64px top = 88px) — applies
     here now for the same reason: the hero is stacked single-column too,
     so the two breakpoints should read the same. */
  .section--tight:has(.seo-trio) { padding-top: var(--space-2); padding-bottom: var(--space-3); }
}
/* Below 768px .grid--2 stacks to a single column (see .grid--2 media query),
   so the negative pull-up above drags the image into the text stacked right
   above it instead of the (now nonexistent) side-by-side column. Cancel the
   overlap entirely once stacked; the grid's own gap gives normal spacing. */
@media (max-width: 767px) {
  .img-overlap-up { margin-top: var(--space-3); margin-inline: auto; position: static; }
  /* This class is shared by about.html's Standort photo and index.html's
     Vanessa-intro photo, but the user wants each at a different size, so
     split by the page-level data-page attribute rather than the class alone. */
  html[data-page="home"] .img-overlap-up { max-width: 80%; }

  /* Standort (about), mobile: moved above its own text (order:-1 — the grid
     is still .grid--2's default single-column stack, DOM order is text
     first otherwise) and overlapping ~10% into the section above, per
     explicit user request 2026-09-08. -10rem = this section's own 128px
     padding-top + ~33px actual overlap (10% of the ~328px rendered image
     height at 390px width) — same "clear the padding-top first" math as
     the tablet version of this same image. */
  html[data-page="about"] .img-overlap-up {
    max-width: 75%;
    order: -1;
    margin-top: -10rem;
    margin-inline: auto;
    position: relative;
    z-index: 2;
  }
}

/* Text next to an overlapping image: anchor to the section's own top so it
   never runs past the image's bottom edge */
.img-overlap-text {
  align-self: start;
}
.u-hide-mobile { display: none; }
@media (min-width: 768px) { .u-hide-mobile { display: revert; } }
.skip-link {
  position: absolute;
  left: -999px;
  top: auto;
  background: var(--color-ink);
  color: var(--color-bg);
  padding: 1em 1.5em;
  z-index: 300;
}
.skip-link:focus { left: var(--space-2); top: var(--space-2); }

/* ---------- Reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  [data-reveal], [data-reveal-fade] {
    opacity: 1 !important;
    transform: none !important;
  }
  * { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
}

/* Inline-Code in den Rechtstexten (Cookie-Namen, Consent-Signale) — nur so
   viel Auszeichnung, dass ein technischer Name als solcher erkennbar ist. */
code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.875em;
  word-break: break-word;
}

/* ==========================================================================
   Lange deutsche Komposita in Überschriften
   Gefunden am 06.10.2026 beim Mobile-Test: „Datenschutzerklärung" ist als
   einzelnes Wort in der h1-Größe rund 497 px breit. Auf einem 375-px-Display
   gibt es keine Umbruchstelle, also wuchs das Dokument auf 517 px und die
   ganze Seite ließ sich horizontal verschieben – inklusive Header, Footer und
   Cookie-Banner, die dadurch über den sichtbaren Bereich hinausragten.
   Betraf alle Seiten mit langen Überschriften, unabhängig vom Tracking.
   ========================================================================== */

h1, h2, h3, h4 {
  /* Greift nur, wenn ein Wort tatsächlich nicht passt – sonst ohne Wirkung. */
  overflow-wrap: break-word;
}

/* In den Fließtext-Seiten zusätzlich echte Silbentrennung: bricht
   „Daten-schutz-erklärung" mit Trennstrich statt hart mitten im Wort.
   Funktioniert, weil jede Seite ein korrektes lang-Attribut trägt (de bzw. en). */
.u-max-prose h1,
.u-max-prose h2,
.u-max-prose h3 {
  -webkit-hyphens: auto;
  hyphens: auto;
}

/* Letzte Absicherung gegen horizontales Scrollen: Kein Element darf das
   Dokument breiter machen als den Viewport. Bewusst auf <body> und nicht auf
   <html>, damit position: sticky im Header weiter funktioniert. */
body {
  overflow-x: hidden;
}

/* ==========================================================================
   Consent: Banner (erste Ebene) & Einstellungsdialog
   --------------------------------------------------------------------------
   Editorial statt Cookie-Popup: eine ruhige cremefarbene Karte mit feiner
   Linie, Cormorant-Überschrift und viel Luft – dieselbe Bildsprache wie die
   Content-Sections, nur kompakter. Bewusst KEIN vollflächiger dunkler
   Streifen mehr: der wirkte wie ein zusätzlicher Website-Abschnitt und nahm
   auf dem Handy die halbe Höhe ein.

   Gleichwertigkeit (DSK-Orientierungshilfe): „Ablehnen“ und „Alle
   akzeptieren“ sind exakt dieselbe Komponente – gleiche Fläche, gleiche
   Kontur, gleiche Typo, gleicher Hover. Keines ist hervorgehoben.
   ========================================================================== */

/* ---------- gemeinsame Bausteine ---------- */

.consent-eyebrow {
  margin: 0;
  font-size: var(--fs-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-muted);
}

.consent-headline {
  margin: 0.45rem 0 0.6rem;
  font-family: var(--font-display);
  font-weight: 500;
  line-height: var(--lh-heading);
  letter-spacing: -0.01em;
  color: var(--color-ink);
  font-size: clamp(1.4rem, 1.1vw + 1.05rem, 1.75rem);
}

.consent-links {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 0.7rem;
  font-size: var(--fs-small);
  color: var(--color-muted);
}

.consent-link {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  text-decoration: underline;
  text-decoration-color: var(--color-border);
  text-underline-offset: 3px;
  transition: color var(--dur-base) var(--ease-default), text-decoration-color var(--dur-base) var(--ease-default);
}

.consent-link:hover,
.consent-link:focus-visible { color: var(--color-accent); text-decoration-color: currentColor; }

.consent-sep { color: var(--color-border); }

/* Beide Hauptaktionen: identische Komponente. */
.consent-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-body);
  font-size: var(--fs-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  line-height: 1;
  padding: 1.05em 1.6em;
  min-height: 3rem;             /* bequeme Touch-Fläche */
  border: 1px solid var(--color-ink);
  background: transparent;
  color: var(--color-ink);
  cursor: pointer;
  white-space: nowrap;
  transition: background-color var(--dur-base) var(--ease-default), color var(--dur-base) var(--ease-default);
}

.consent-btn:hover,
.consent-btn:focus-visible { background: var(--color-dark); border-color: var(--color-dark); color: var(--color-dark-ink); }

/* ---------- Erste Ebene ---------- */

.consent-banner {
  position: fixed;
  inset-inline: 0;
  bottom: clamp(0.75rem, 2.5vw, 1.75rem);
  z-index: 200;
  padding-inline: clamp(0.75rem, 4vw, 1.5rem);
  display: flex;
  justify-content: center;
  pointer-events: none;         /* nur die Karte selbst fängt Klicks */
}

.consent-banner__card {
  pointer-events: auto;
  width: 100%;
  max-width: 62rem;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  box-shadow: 0 1rem 2.75rem rgba(24, 22, 15, 0.13);
  padding: clamp(1.25rem, 2.2vw, 1.9rem) clamp(1.25rem, 2.4vw, 2.1rem);
  display: grid;
  grid-template-columns: 1fr 14rem;
  align-items: center;
  column-gap: clamp(1.75rem, 3vw, 3rem);
}

.consent-banner__text {
  margin: 0 0 0.75rem;
  font-size: var(--fs-small);
  line-height: var(--lh-body);
  color: var(--color-ink-soft);
  max-width: 54ch;
}

.consent-banner__actions {
  display: grid;
  gap: 0.6rem;
}

.consent-banner__actions .consent-btn { width: 100%; }

@media (max-width: 54rem) {
  .consent-banner { bottom: 0; padding-inline: 0; }
  .consent-banner__card {
    max-width: none;
    grid-template-columns: 1fr;
    row-gap: 0.85rem;
    border-inline: 0;
    border-bottom: 0;
    padding: 1rem var(--space-2) calc(1rem + env(safe-area-inset-bottom, 0px));
    box-shadow: 0 -0.5rem 2rem rgba(24, 22, 15, 0.14);
  }
  /* Auf dem Handy zählt jede Zeile: etwas kleinere Headline, engere Abstände.
     Der Text selbst bleibt unverändert – gekürzt wird die Gestaltung, nicht
     die Information. */
  .consent-headline { font-size: 1.25rem; margin: 0.3rem 0 0.5rem; }
  .consent-banner__text { margin-bottom: 0.55rem; line-height: 1.55; }
  .consent-banner__actions { gap: 0.5rem; }
}

/* ---------- Einstellungsdialog ---------- */

.consent-modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 300;
  background: rgba(24, 22, 15, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(0.75rem, 3vw, 2rem);
}

body.consent-modal-open { overflow: hidden; }

.consent-modal {
  --pad-x: clamp(1.25rem, 2.6vw, 2.25rem);
  --pad-y: clamp(1.5rem, 2.6vw, 2.25rem);
  width: min(34rem, 100%);
  max-height: min(42rem, 100%);
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  box-shadow: 0 1.5rem 4rem rgba(24, 22, 15, 0.22);
  padding: var(--pad-y) var(--pad-x);
}

.consent-modal:focus { outline: none; }

/* Kopf bleibt beim Scrollen durch die Kategorien erreichbar. */
.consent-modal__head {
  position: sticky;
  top: calc(var(--pad-y) * -1);
  margin: calc(var(--pad-y) * -1) calc(var(--pad-x) * -1) 0;
  padding: var(--pad-y) var(--pad-x) 0.9rem;
  background: var(--color-bg);
  z-index: 1;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
}

.consent-modal__title {
  margin: 0.4rem 0 0;
  font-family: var(--font-display);
  font-weight: 500;
  line-height: var(--lh-heading);
  letter-spacing: -0.01em;
  color: var(--color-ink);
  font-size: clamp(1.6rem, 1.4vw + 1.2rem, 2.1rem);
}

.consent-modal__close {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  margin: -0.5rem -0.6rem 0 0;
  background: none;
  border: 0;
  color: var(--color-muted);
  cursor: pointer;
  transition: color var(--dur-base) var(--ease-default);
}

.consent-modal__close:hover,
.consent-modal__close:focus-visible { color: var(--color-ink); }

.consent-modal__intro {
  margin: 1rem 0 0;
  font-size: var(--fs-small);
  line-height: var(--lh-body);
  color: var(--color-ink-soft);
  max-width: 48ch;
}

/* ---------- Kategorien ---------- */

.consent-cat {
  margin-top: 1.4rem;
  padding-top: 1.4rem;
  border-top: 1px solid var(--color-border);
}

.consent-cat__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1.25rem;
}

.consent-cat__name {
  display: block;
  font-family: var(--font-display);
  font-size: 1.2rem;
  font-weight: 600;
  line-height: 1.25;
  color: var(--color-ink);
}

.consent-cat__service {
  display: block;
  margin-top: 0.25rem;
  font-size: var(--fs-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-muted);
}

.consent-cat__desc {
  margin: 0.6rem 0 0;
  font-size: var(--fs-small);
  line-height: var(--lh-body);
  color: var(--color-ink-soft);
  max-width: 48ch;
}

.consent-cat__list {
  margin: 0.6rem 0 0;
  padding-left: 1.1em;
  list-style: disc;
  display: grid;
  gap: 0.3em;
  font-size: var(--fs-small);
  line-height: 1.55;
  color: var(--color-muted);
  max-width: 48ch;
}

.consent-cat__meta {
  margin: 0.55rem 0 0;
  font-size: var(--fs-small);
  line-height: 1.55;
  color: var(--color-muted);
}

/* „Immer aktiv“ statt eines Schalters, der ohnehin nicht bedienbar wäre. */
.consent-cat__always {
  flex: none;
  padding-top: 0.3rem;
  font-size: var(--fs-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-muted);
  white-space: nowrap;
}

/* ---------- Toggle ---------- */

.consent-toggle {
  appearance: none;
  -webkit-appearance: none;
  flex: none;
  position: relative;
  width: 3rem;
  height: 1.5rem;
  margin: 0.25rem 0 0;
  background: transparent;
  border: 1px solid var(--color-muted);
  border-radius: 999px;
  cursor: pointer;
  transition: border-color var(--dur-base) var(--ease-default), background-color var(--dur-base) var(--ease-default);
}

.consent-toggle::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: calc(1.5rem - 6px);
  height: calc(1.5rem - 6px);
  border-radius: 50%;
  background: var(--color-muted);
  transition: transform var(--dur-base) var(--ease-default), background-color var(--dur-base) var(--ease-default);
}

.consent-toggle:checked {
  border-color: var(--color-dark);
  background: var(--color-dark);
}

.consent-toggle:checked::after {
  transform: translateX(1.5rem);
  background: var(--color-dark-ink);
}

.consent-toggle:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 3px; }

@media (prefers-reduced-motion: reduce) {
  .consent-toggle, .consent-toggle::after { transition: none; }
}

/* ---------- Allgemeiner Hinweis + Aktionen ---------- */

.consent-detail {
  margin-top: 1.4rem;
  padding-top: 1.4rem;
  border-top: 1px solid var(--color-border);
}

.consent-detail__text {
  margin: 0 0 0.7rem;
  font-size: var(--fs-small);
  line-height: 1.6;
  color: var(--color-muted);
  max-width: 48ch;
}

.consent-modal__actions {
  margin-top: 1.4rem;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.7rem;
}

@media (max-width: 34rem) {
  .consent-modal {
    width: 100%;
    max-height: 100%;
    height: 100%;
    border: 0;
  }
  .consent-modal-overlay { padding: 0; }
  .consent-modal__actions { grid-template-columns: 1fr; }
  .consent-cat__head { gap: 0.9rem; }
}

/* ---------- Widerrufs-Einstieg im Footer ---------- */
/* Ein <button>, weil es kein Seitenaufruf ist, sondern das Modal öffnet. Er
   muss sich optisch exakt wie die beiden Rechtstext-Links daneben verhalten:
   gleiche Schrift, Größe, Laufweite, Farbe, Hover und Fokus. Deshalb erbt er
   .text-link und setzt hier nur die Button-Eigenheiten zurück. */
.consent-footer-link {
  font: inherit;
  font-size: var(--fs-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  background: none;
  border: 0;
  color: inherit;
  cursor: pointer;
  /* Gleiche Grundlinie wie die <a>-Geschwister: .text-link bringt bereits
     padding-bottom 0.3em mit, der Button darf keine eigene Polsterung dazulegen. */
  padding: 0 0 0.3em;
}

/* links.html hat keinen Footer – dort sitzt der Einstieg zwischen den großen
   Link-Buttons und übernimmt deren Optik. */
.consent-footer-link--btn { font-family: inherit; cursor: pointer; color: inherit; }

/* ==========================================================================
   Footer-Rechtsnavigation umbruchfähig
   --------------------------------------------------------------------------
   Die Zeile trägt im HTML ein inline `display:flex; gap:1.5rem`. Flex bricht
   standardmäßig NICHT um – mit zwei Links (Impressum, Datenschutz) ging das
   knapp auf, mit dem dritten („Cookie-Einstellungen") nicht mehr: Auf 320 px
   wurde die Zeile 363 px breit und spannte das Dokument auf. Weil fixierte
   Elemente sich am Dokument orientieren, wurde dadurch auch der Cookie-Banner
   zu breit und rechts abgeschnitten.
   ========================================================================== */

.footer-bottom > span:last-child {
  flex-wrap: wrap;
  row-gap: 0.6rem;
  justify-content: flex-end;
}

@media (max-width: 30rem) {
  /* Auf sehr schmalen Displays Copyright und Rechtsnavigation untereinander,
     beide linksbündig – nebeneinander bleibt sonst zu wenig Platz. */
  .footer-bottom { flex-direction: column; align-items: flex-start; }
  .footer-bottom > span:last-child { justify-content: flex-start; }
}

/* ==========================================================================
   Startseiten-Intro: Textspalte darf schrumpfen
   --------------------------------------------------------------------------
   Die Mobile-Variante nutzt `grid-template-columns: 1fr clamp(150px,46vw,210px)`.
   Ein `1fr`-Track hat eine automatische Mindestbreite (min-width:auto) und wird
   nie schmaler als sein längstes unteilbares Inhaltsstück. Auf 320 px brauchte
   die Überschrift „Vanessa Schönleben" 129,75 px, verfügbar waren nach Bild
   (150 px) und Spalte (24 px) aber nur 106 px – macht 303,75 px Spurbreite in
   einem 280 px breiten Container und damit 4 px Dokumentüberlauf.

   `minmax(0, 1fr)` hebt genau diese Mindestbreite auf. Oberhalb von ~360 px
   ändert sich nichts, weil der Track dort ohnehin breiter ist als sein
   Mindestinhalt – die Regel greift nur, wenn es sonst überlaufen würde.
   Gefunden im Abschluss-Audit am 06.10.2026.
   ========================================================================== */

@media (max-width: 767px) {
  .home-intro-grid {
    grid-template-columns: minmax(0, 1fr) clamp(150px, 46vw, 210px);
  }
}
