/* pwb-builder — premium editorial base. One typeface (Satoshi), OKLCH, fluid type,
   asymmetric layouts, CSS aurora, scroll reveals. Self-hosted, no framework, Hostpoint-pure. */

@font-face {
  font-family: "Satoshi";
  src: url("/assets/fonts/satoshi-variable.woff2") format("woff2-variations");
  font-weight: 300 900; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Satoshi";
  src: url("/assets/fonts/satoshi-variable-italic.woff2") format("woff2-variations");
  font-weight: 300 900; font-style: italic; font-display: swap;
}
/* Fraunces — editorial serif kept self-hosted as an OPTIONAL per-site look, but no longer the
   default: a wonky serif on every heading read as "typical AI". Default display = Satoshi (below).
   Unreferenced @font-face does not download, so this costs nothing until a site opts back in. */
@font-face {
  font-family: "Fraunces";
  src: url("/assets/fonts/fraunces-display.woff2") format("woff2-variations");
  font-weight: 100 900; font-style: normal; font-display: swap;
}

/* === BEGIN design-system pairing faces (generated: php _vendor/gen_faces.php) ===
   Self-hosted woff2 (latin), DSG-clean. Unreferenced faces do not download — only the
   pairing a site selects (layout.php sets --font-display/--font) is ever fetched. */
@font-face {
  font-family: "Cinzel";
  src: url("/assets/fonts/cinzel-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Cinzel";
  src: url("/assets/fonts/cinzel-600.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Cinzel";
  src: url("/assets/fonts/cinzel-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Cormorant Garamond";
  src: url("/assets/fonts/cormorant-garamond-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Cormorant Garamond";
  src: url("/assets/fonts/cormorant-garamond-600.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Cormorant Garamond";
  src: url("/assets/fonts/cormorant-garamond-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "EB Garamond";
  src: url("/assets/fonts/eb-garamond-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "EB Garamond";
  src: url("/assets/fonts/eb-garamond-600.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "EB Garamond";
  src: url("/assets/fonts/eb-garamond-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Figtree";
  src: url("/assets/fonts/figtree-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Figtree";
  src: url("/assets/fonts/figtree-600.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Figtree";
  src: url("/assets/fonts/figtree-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "General Sans";
  src: url("/assets/fonts/general-sans-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "General Sans";
  src: url("/assets/fonts/general-sans-600.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "General Sans";
  src: url("/assets/fonts/general-sans-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "IBM Plex Sans";
  src: url("/assets/fonts/ibm-plex-sans-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "IBM Plex Sans";
  src: url("/assets/fonts/ibm-plex-sans-600.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "IBM Plex Sans";
  src: url("/assets/fonts/ibm-plex-sans-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Inter";
  src: url("/assets/fonts/inter-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Inter";
  src: url("/assets/fonts/inter-600.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Inter";
  src: url("/assets/fonts/inter-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Josefin Sans";
  src: url("/assets/fonts/josefin-sans-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Josefin Sans";
  src: url("/assets/fonts/josefin-sans-600.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Josefin Sans";
  src: url("/assets/fonts/josefin-sans-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Karla";
  src: url("/assets/fonts/karla-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Karla";
  src: url("/assets/fonts/karla-600.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Karla";
  src: url("/assets/fonts/karla-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Lato";
  src: url("/assets/fonts/lato-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Lato";
  src: url("/assets/fonts/lato-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Lexend";
  src: url("/assets/fonts/lexend-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Lexend";
  src: url("/assets/fonts/lexend-600.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Lexend";
  src: url("/assets/fonts/lexend-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Libre Baskerville";
  src: url("/assets/fonts/libre-baskerville-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Libre Baskerville";
  src: url("/assets/fonts/libre-baskerville-600.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Libre Baskerville";
  src: url("/assets/fonts/libre-baskerville-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Lora";
  src: url("/assets/fonts/lora-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Lora";
  src: url("/assets/fonts/lora-600.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Lora";
  src: url("/assets/fonts/lora-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Noto Sans";
  src: url("/assets/fonts/noto-sans-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Noto Sans";
  src: url("/assets/fonts/noto-sans-600.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Noto Sans";
  src: url("/assets/fonts/noto-sans-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Nunito Sans";
  src: url("/assets/fonts/nunito-sans-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Nunito Sans";
  src: url("/assets/fonts/nunito-sans-600.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Nunito Sans";
  src: url("/assets/fonts/nunito-sans-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Open Sans";
  src: url("/assets/fonts/open-sans-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Open Sans";
  src: url("/assets/fonts/open-sans-600.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Open Sans";
  src: url("/assets/fonts/open-sans-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Playfair Display";
  src: url("/assets/fonts/playfair-display-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Playfair Display";
  src: url("/assets/fonts/playfair-display-600.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Playfair Display";
  src: url("/assets/fonts/playfair-display-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Playfair Display SC";
  src: url("/assets/fonts/playfair-display-sc-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Playfair Display SC";
  src: url("/assets/fonts/playfair-display-sc-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Poppins";
  src: url("/assets/fonts/poppins-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Poppins";
  src: url("/assets/fonts/poppins-600.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Poppins";
  src: url("/assets/fonts/poppins-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Raleway";
  src: url("/assets/fonts/raleway-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Raleway";
  src: url("/assets/fonts/raleway-600.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Raleway";
  src: url("/assets/fonts/raleway-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Source Sans 3";
  src: url("/assets/fonts/source-sans-3-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Source Sans 3";
  src: url("/assets/fonts/source-sans-3-600.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Source Sans 3";
  src: url("/assets/fonts/source-sans-3-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Varela Round";
  src: url("/assets/fonts/varela-round-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
/* === END design-system pairing faces === */

:root {
  /* Fluid type scale (utopia-style, 360 -> 1440) */
  --t-sm:   clamp(0.84rem, 0.81rem + 0.14vw, 0.92rem);
  --t-base: clamp(1rem, 0.95rem + 0.25vw, 1.15rem);
  --t-lg:   clamp(1.18rem, 1.08rem + 0.5vw, 1.45rem);
  --t-xl:   clamp(1.45rem, 1.25rem + 1vw, 2rem);
  --t-2xl:  clamp(1.9rem, 1.55rem + 1.7vw, 2.85rem);
  --t-3xl:  clamp(2.5rem, 1.9rem + 3vw, 4.4rem);
  --t-display: clamp(2.35rem, 1.5rem + 4.4vw, 5.6rem);

  /* Rhythm — height-aware so legacy section padding does not balloon on wide-but-short laptops (was 8vw) */
  --section: clamp(3rem, 6vh, 5.5rem);
  --maxw: 1240px;
  --gutter: clamp(1.4rem, 5vw, 2.5rem);
  --radius: 16px;

  /* Palette — default: slate-blue. Warm off-white paper, near-black warm ink, one accent. */
  --paper:   oklch(98.4% 0.006 95);
  --ink:     oklch(24% 0.012 260);
  --muted:   oklch(48% 0.014 260);
  --line:    oklch(89% 0.01 250);
  --surface: oklch(96.5% 0.008 95);
  --accent:      oklch(50% 0.11 245);
  --accent-deep: oklch(40% 0.1 250);
  --accent-soft: oklch(70% 0.13 250);
  --accent-ink:  oklch(98% 0.01 250);

  --font: "Satoshi", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  /* Display = the same premium grotesk (Satoshi), NOT a curvy serif. A wonky editorial serif on
     every heading read as "AI template"; clean heavy Satoshi at display size + tight tracking is
     premium and tonally neutral across industries. Self-hosted, no extra font download. */
  --font-display: "Satoshi", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

[data-palette="warm-clay"] {
  --paper: oklch(97.2% 0.019 64); --ink: oklch(23% 0.026 40); --muted: oklch(47% 0.03 42);
  --line: oklch(88% 0.022 55); --surface: oklch(94.8% 0.026 60);
  --accent: oklch(56% 0.13 38); --accent-deep: oklch(45% 0.12 35); --accent-soft: oklch(77% 0.11 48); --accent-ink: oklch(99% 0.01 60);
}
[data-palette="blush"] {
  --paper: oklch(97.4% 0.017 26); --ink: oklch(24% 0.03 355); --muted: oklch(50% 0.03 352);
  --line: oklch(89% 0.022 18); --surface: oklch(95% 0.024 20);
  --accent: oklch(60% 0.12 16); --accent-deep: oklch(49% 0.12 10); --accent-soft: oklch(81% 0.09 22); --accent-ink: oklch(99% 0.01 20);
}
[data-palette="sand"] {
  --paper: oklch(96.8% 0.022 85); --ink: oklch(25% 0.024 70); --muted: oklch(49% 0.028 70);
  --line: oklch(88% 0.024 80); --surface: oklch(94% 0.03 82);
  --accent: oklch(54% 0.1 70); --accent-deep: oklch(43% 0.1 65); --accent-soft: oklch(78% 0.09 80); --accent-ink: oklch(99% 0.01 80);
}
[data-palette="forest"] {
  --paper: oklch(98.4% 0.01 130); --ink: oklch(22% 0.022 160); --muted: oklch(46% 0.022 160);
  --line: oklch(89% 0.015 150); --surface: oklch(96.5% 0.012 130);
  --accent: oklch(50% 0.09 165); --accent-deep: oklch(39% 0.08 168); --accent-soft: oklch(72% 0.11 160); --accent-ink: oklch(99% 0.01 150);
}
[data-palette="noir"] {
  --paper: oklch(17% 0.008 265); --ink: oklch(95% 0.004 260); --muted: oklch(66% 0.01 260);
  --line: oklch(32% 0.01 265); --surface: oklch(22% 0.01 265);
  --accent: oklch(80% 0.11 80); --accent-deep: oklch(86% 0.09 85); --accent-soft: oklch(72% 0.12 75); --accent-ink: oklch(18% 0.02 80);
}

* { box-sizing: border-box; min-width: 0; }
/* overflow-x: clip (not hidden) — hidden makes html/body a scroll container, which silently breaks
   the sticky header (it scrolled away instead of sticking). clip still hides the hero/mesh bleed but
   keeps position:sticky working. Verified: header sticks at top:0, no horizontal scrollbar. */
html { -webkit-text-size-adjust: 100%; overflow-x: clip; }
body {
  margin: 0; background: var(--paper); color: var(--ink);
  font-family: var(--font); font-size: var(--t-base); line-height: 1.6; font-weight: 420;
  font-feature-settings: "kern" 1, "liga" 1, "calt" 1; text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased; overflow-x: clip;
}
img { max-width: 100%; height: auto; display: block; }
a { color: var(--accent-deep); text-underline-offset: 3px; }
::selection { background: var(--accent-soft); color: var(--accent-ink); }

/* P5 — GLOBALER FOKUS-STIL. Bis 2026-08-03 gab es in 1849 Zeilen CSS genau zwei Fokus-Regeln
   (Kontaktformular-Feld, Dropdown) und kein einziges :focus-visible. Jeder Link, jeder Knopf,
   jede Karte, jeder Nav-Eintrag bekam den Browser-Standardring — auf einer dunkeltuerkisen
   Cover-Flaeche praktisch unsichtbar. WCAG 2.2 AA (2.4.11/2.4.13) ist damit nicht erfuellbar.
   Markenfarbe als Ring, auf dunklen/Akzent-Flaechen invertiert, damit er dort ueberhaupt
   erscheint. :focus-visible statt :focus — Mausklicks bekommen keinen Ring. */
:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
  border-radius: 4px;
}
:is(.sx-tone-dark, .sx-tone-accent, .site-header[data-style="over"], .site-footer) :focus-visible {
  outline-color: #fff;
}

.block-inner { width: min(100% - calc(var(--gutter) * 2), var(--maxw)); margin-inline: auto; }
.block-inner.narrow { width: min(100% - calc(var(--gutter) * 2), 760px); }
.block { padding-block: var(--section); position: relative; }
/* Section rhythm: stacked blocks share one gap (prev block's bottom padding), not two.
   Symmetric padding-block would otherwise sum to ~18rem between sections. The hero keeps
   its own padding (it's :first-child, so the + selector never touches it). */
.block + .block { padding-block-start: 0; }

/* ---------- Header ---------- */
.site-header {
  position: sticky; top: 0; z-index: 30; width: 100%;
  background: color-mix(in oklch, var(--paper) 80%, transparent);
  backdrop-filter: saturate(1.15) blur(14px); -webkit-backdrop-filter: saturate(1.15) blur(14px);
  border-bottom: 1px solid color-mix(in oklch, var(--line) 65%, transparent);
}
.header-inner {
  display: flex; align-items: center; gap: 1rem 1.8rem; flex-wrap: wrap;
  width: min(100% - calc(var(--gutter) * 2), var(--maxw)); margin: 0 auto; padding-block: .9rem;
}
.brand { font-weight: 720; font-size: 1.28rem; letter-spacing: -0.02em; text-decoration: none; color: var(--ink); display: inline-flex; align-items: center; }
.brand-logo { height: clamp(52px, 6vw, 82px); width: auto; max-width: 260px; object-fit: contain; display: block; }
/* P5/C2 — Plakette. Nur wenn ein Logo mit deckend hellem Grund ausdruecklich in einen dunklen
   Header gestellt wird (partials/header.php setzt data-logo dann selbst). Ohne diese Regel steht
   dort ein weisses Rechteck ohne erkennbaren Grund; mit ihr ist es eine bewusst gesetzte Platte —
   dieselbe Loesung, die die Handfassung fuer ihren dunklen Fuss verwendet. */
/* P5 — Telefonnummer im Kopf (partials/header.php, nur bei goal=calls).
   Auf schmalen Fenstern bleibt das Hoerersymbol und der Text verschwindet: die Nummer ist dort
   ohnehin antippbar, und der Platz gehoert der Marke. min-height 44px = Touch-Zielgroesse. */
.header-tel { display: inline-flex; align-items: center; gap: .45rem; margin-left: auto;
  text-decoration: none; font-weight: 640; font-size: var(--t-sm); white-space: nowrap;
  color: inherit; min-height: 44px; padding-inline: .2rem; }
.header-tel-icon { width: 1.15em; height: 1.15em; flex: none; color: var(--accent); }
.site-header[data-style="over"] .header-tel-icon { color: currentColor; }
.header-tel:hover .header-tel-text { text-decoration: underline; text-underline-offset: 3px; }
@media (max-width: 60rem) { .header-tel-text { display: none; } }

.site-header[data-logo="plakette"] .brand-logo {
  background: #fff; padding: .3rem .55rem; border-radius: 8px;
  box-shadow: 0 6px 18px -10px rgba(0,0,0,.55);
}
.nav-burger { display: none; }   /* CSS-only mobile menu toggle; shown < 40rem (rules below) */
.site-nav { display: flex; gap: 1.7rem; margin-left: auto; flex-wrap: wrap; align-items: center; }
.site-nav a { position: relative; display: inline-flex; align-items: center; min-height: 44px; color: var(--muted); text-decoration: none; font-size: var(--t-sm); font-weight: 500; transition: color .18s ease; }
.site-nav a[aria-current="page"], .site-nav a:hover { color: var(--ink); }
/* nav micro-interaction: a hairline underline that grows from the left on hover / for the current page.
   Anchored to the link's vertical centre (not the 44px box bottom) so it stays tight under the text. */
.site-nav a::after { content: ""; position: absolute; left: .1em; right: .1em; top: 50%; margin-top: .6em; height: 1.5px;
  background: currentColor; border-radius: 2px; opacity: .5; pointer-events: none;
  transform: scaleX(0); transform-origin: left; transition: transform .24s cubic-bezier(.2,.8,.2,1); }
.site-nav a:hover::after, .site-nav a[aria-current="page"]::after { transform: scaleX(1); }
/* QW4: optional global header CTA (primary button shown in the header on every page). */
.site-header .header-cta { flex: 0 0 auto; margin-left: 1rem; padding: .55rem 1.1rem; min-height: 44px; display: inline-flex; align-items: center; }
@media (max-width: 40rem) { .site-header .header-cta { order: 7; margin-left: auto; } }
.locale-switcher { display: flex; gap: .15rem; font-size: 0.72rem; font-weight: 600; }
.locale-switcher a { display: inline-flex; align-items: center; justify-content: center; min-width: 44px; min-height: 44px; color: var(--muted); text-decoration: none; padding: .15rem .5rem; border-radius: 8px; letter-spacing: .03em; }
.locale-switcher a.is-current { color: var(--accent-deep); }
@media (max-width: 40rem) {
  /* Row 1 = brand + burger only. The nav is collapsed by default, so the pill can never grow tall
     enough to overlap the page content below it (the mobile critical). Opening the burger drops the
     nav as a full-width panel with proper >= 44px tap targets. CSS-only, no JS. */
  .header-inner { row-gap: .4rem; }
  .brand { flex: 1 1 auto; }
  .nav-burger {
    display: inline-flex; align-items: center; justify-content: center;
    width: 44px; height: 44px; margin-left: auto; flex: 0 0 auto;
    color: var(--ink); cursor: pointer; border-radius: 12px;
    background: color-mix(in oklch, currentColor 8%, transparent);
  }
  .nav-burger span, .nav-burger span::before, .nav-burger span::after {
    content: ""; display: block; width: 22px; height: 2px; background: currentColor; border-radius: 2px;
    transition: transform .2s ease, opacity .2s ease;
  }
  .nav-burger span { position: relative; }
  .nav-burger span::before { position: absolute; left: 0; top: -7px; }
  .nav-burger span::after  { position: absolute; left: 0; top: 7px; }
  .nav-toggle:checked ~ .nav-burger span { background: transparent; }
  .nav-toggle:checked ~ .nav-burger span::before { transform: translateY(7px) rotate(45deg); }
  .nav-toggle:checked ~ .nav-burger span::after  { transform: translateY(-7px) rotate(-45deg); }
  .site-nav { display: none; flex-basis: 100%; margin-left: 0; flex-direction: column; gap: .2rem; align-items: stretch; order: 9; }
  .nav-toggle:checked ~ .site-nav { display: flex; }
  .site-nav a { padding: .7rem .2rem; font-size: var(--t-base); }   /* >= 44px tap targets */
  .site-nav a::after { display: none; }   /* underline micro is a desktop-row affordance only */
  /* dropdown/mega im Mobile-Stack (2026-08-02): das Hover-Untermenue existiert auf Touch nicht,
     und der "Mehr"-Elternlink navigiert beim Tap sofort zur ersten Unterseite — die gruppierten
     Seiten waren ueber das Menue faktisch unerreichbar. Im offenen Menue stehen die Kinder darum
     IMMER sichtbar eingerueckt unter ihrem Elternpunkt (kein Hover, kein zweiter Tap noetig). */
  .site-nav .nav-group { display: block; }
  .site-nav .nav-group .nav-sub { position: static; opacity: 1; visibility: visible; transform: none;
    box-shadow: none; border: 0; background: transparent; padding: 0 0 .3rem 1rem; min-width: 0; }
  .site-nav .nav-group .nav-sub a { color: inherit; opacity: .8; }
  .locale-switcher { order: 8; margin-left: auto; }
  /* Aufgeklappte Pille (2026-08-02): border-radius 999px machte aus dem hohen offenen Menue einen
     Kreis-Blob, der die obersten/untersten Links anschnitt (Beweisbild lt-rsm-…-390-offen vor dem
     Fix). Offen traegt die Kapsel eine moderate Rundung, geschlossen bleibt sie eine Pille. */
  .site-header[data-style="pill"] .header-inner:has(.nav-toggle:checked),
  .site-header[data-style="over"] .header-inner:has(.nav-toggle:checked),
  .site-header[data-nav="sticky-over"] .header-inner:has(.nav-toggle:checked) { border-radius: 26px; }
}

/* ---------- Buttons ---------- */
.btn { display: inline-flex; align-items: center; gap: .5rem; font-weight: 600; text-decoration: none; border-radius: 999px; transition: transform .2s cubic-bezier(.2,.8,.2,1), box-shadow .2s ease, background .2s ease; }
/* P5 — Magnetik und Hover-Lift teilen sich nicht mehr dieselbe Eigenschaft.
   reveal.js schrieb bis 2026-08-03 `style.transform` direkt auf den Knopf und ueberschrieb damit
   `.btn-primary:hover{transform:translateY(-2px)}` — der gestaltete Hover-Lift erschien auf
   Zeigegeraeten NIE. Zusaetzlich startete jeder pointermove die 200-ms-transform-Transition neu,
   der Knopf hing dem Zeiger schwabbelnd hinterher. Jetzt traegt `translate` die Magnetik (eigene,
   kurze Kurve) und `transform` den Lift — beide komponieren, keiner ueberschreibt den anderen. */
.btn-primary { background: var(--accent-deep); color: var(--accent-ink); padding: .9rem 1.6rem; min-height: 44px;
  translate: var(--mag-x, 0) var(--mag-y, 0); transition: translate .09s linear; }
.btn-primary::after { content: "→"; transition: transform .2s ease; }
.btn-primary:hover { transform: translateY(-2px); box-shadow: 0 14px 30px -14px var(--accent); }
.btn-primary:hover::after { transform: translateX(3px); }

/* ---------- Hero (asymmetric + aurora) ---------- */
.block-hero {
  min-height: calc(100vh - 4.8rem);
  min-height: calc(100svh - 4.8rem);   /* fills the screen below the header, any display */
  display: grid; align-content: center;
  padding-block: clamp(1.5rem, 4vw, 3rem) clamp(2rem, 6vw, 4.5rem);
  overflow: clip;
}
.hero-textonly .hero-grid { grid-template-columns: 1fr; max-width: 780px; }
body.shot .block-hero { min-height: 540px; }   /* screenshot mode: normalize hero height for full-page capture */
.hero-grid { display: grid; gap: clamp(2rem, 5vw, 4rem); align-items: center; }
@media (min-width: 60rem) { .hero-grid { grid-template-columns: 62fr 38fr; } }
.eyebrow { text-transform: uppercase; letter-spacing: .16em; font-size: 0.74rem; font-weight: 640; color: var(--accent-deep); margin: 0 0 1.4rem; }
.hero-title { font-weight: 760; font-size: var(--t-display); line-height: 1.0; letter-spacing: -0.035em; margin: 0 0 1.4rem; text-wrap: balance; overflow-wrap: break-word; hyphens: auto; }
.hero-sub { font-size: var(--t-lg); color: var(--muted); margin: 0 0 2.2rem; max-width: 34ch; line-height: 1.5; }
.hero-media img { border-radius: var(--radius); aspect-ratio: 4/5; object-fit: cover; }

/* B1 — per-site hero LAYOUTS (split = default 62/38). Chosen at render time by industry + seed so two
   industries get structurally different heroes, not just different colours. */
.hero-stage .hero-grid { grid-template-columns: 1fr; justify-items: center; text-align: center; max-width: 1060px; margin-inline: auto; gap: clamp(1.8rem, 4vw, 3rem); }
.hero-stage .eyebrow { margin-bottom: 1rem; }
.hero-stage .hero-sub { max-width: 54ch; margin-inline: auto; }
.hero-stage .hero-media { width: 100%; }
.hero-stage .hero-media img, .hero-stage .hero-art { aspect-ratio: 16 / 9; width: 100%; }
.hero-editorial .hero-title { font-size: clamp(2.7rem, 6.2vw, 4.7rem); letter-spacing: -0.045em; }
.hero-editorial .hero-media img, .hero-editorial .hero-art { aspect-ratio: 3 / 4; }
@media (min-width: 60rem) {
  .hero-editorial .hero-grid { grid-template-columns: 46fr 54fr; column-gap: clamp(2rem, 5vw, 4.5rem); }
  .hero-editorial .hero-media { order: -1; }            /* media to the LEFT, copy on the right */
  .hero-editorial .hero-copy { border-left: 3px solid var(--accent); padding-left: clamp(1.1rem, 2.2vw, 2.2rem); }
}

/* Aurora background — CSS only, behind hero, drifting blobs. */
.block-hero::before {
  content: ""; position: absolute; inset: -20% -10% auto -10%; height: 130%; z-index: -1;
  pointer-events: none; opacity: .5; filter: blur(60px);
  background:
    radial-gradient(40% 50% at 22% 30%, var(--accent-soft), transparent 70%),
    radial-gradient(38% 48% at 78% 22%, var(--accent), transparent 72%),
    radial-gradient(45% 55% at 60% 70%, var(--accent-deep), transparent 75%);
}
.block-hero::before { display: none; }
body[data-bg^="aurora"] .block-hero::before { display: block; }
/* pointer-events:none ist Pflicht, nicht Kosmetik: das Canvas liegt per inset:0 ueber der ganzen
   Sektion und fing sonst jeden Klick auf darunterliegende Formularfelder ab (gemessen: auf 13
   kontakt-Seiten kam getippter Text nicht an). Die Cursor-Effekte haengen ihren pointermove-
   Listener auf die SEKTION, nicht auf das Canvas (backgrounds.js) — sie verlieren dadurch nichts.
   Abgesichert in tests/css_contract_test.php. */
.bg-canvas { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 0; display: block; pointer-events: none; }
/* any hero with a canvas background: keep content above it */
.block-hero > .block-inner { position: relative; z-index: 1; }
/* Per-section background (editor "Hintergrund pro Sektion", data-sec-bg): lift content above the
   bg-canvas / colour fill so a per-section effect or colour never covers the text. */
.block[data-sec-bg] { isolation: isolate; }
.block[data-sec-bg] > :not(.bg-canvas) { position: relative; z-index: 1; }


/* ------------------------------------------------------------------------------------------
   BEWEGUNGSSTUFE DER SITE — der CSS-Hook, dessen Fehlen weiter unten (Z. ~1613) bisher nur
   dokumentiert war: «the site-wide motion wish (theme.motionLevel, S1) has no CSS hook».
   Er wird gebraucht, weil nicht jede Bewegung im Canvas steckt (Reveals, Ornament-Float,
   Laufschriften) — die JS-Stufe erreicht die nicht.

   ZUR SPEZIFITAET: der Attributselektor `body[data-motion="lebendig"]` wiegt (0,2,0). Eine
   Media Query erhoeht die Spezifitaet NICHT. Die reduced-motion-Regel muss deshalb DIESELBE
   Selektorform tragen und danach stehen, sonst gewinnt «lebendig» gegen den Systemwunsch des
   Nutzers — und der Systemwunsch gewinnt immer. WordPress 6.6 hat sich an genau dieser
   Fehlerklasse verhoben. */
body                          { --pwb-motion: 1; }
body[data-motion="aus"]       { --pwb-motion: 0; }
body[data-motion="dezent"]    { --pwb-motion: 1; }
body[data-motion="lebendig"]  { --pwb-motion: 1; }
@media (prefers-reduced-motion: reduce) {
  body[data-motion]           { --pwb-motion: 0; }
}

/* ------------------------------------------------------------------------------------------
   «BEWEGUNG ANHALTEN» — WCAG 2.2.2 «Pause, Stop, Hide», Stufe A (07.08.)

   Zwei verschiedene Sachen, absichtlich nebeneinander:

   (1) `html.pwb-js` — OHNE JavaScript gibt es keine Bedienung, also darf ohne JavaScript auch
       nichts von selbst laufen. Betroffen sind genau die drei Animationen, die der SERVER
       fertig ausliefert: Laufschrift, Galerie-Band, Ken Burns auf dem ersten Bild. Canvas,
       Video und Bilderfolge starten ohnehin erst durch JS. `assets/bewegung.js` setzt die
       Klasse; bis dahin (wenige Millisekunden bei `defer`) steht es still — ein spaeterer
       Beginn, kein Sprung.

   (2) `html[data-bewegung="aus"]` — der Besucher hat angehalten. `bewegung.js` pausiert
       CSS-Animationen bereits generisch ueber die Web-Animations-API; diese Regeln sind die
       zweite Schicht fuer den Fall, dass eine Animation ERST NACH dem Anhalten anfaengt und
       zwischen `animationstart` und dem Pausieren ein Bild lang liefe. Eine pauschale Regel
       (`* { animation-play-state: paused }`) waere hier falsch: sie friert auch die
       Einblend-Animationen ein, und Inhalt mit `backwards`-Fuellung bliebe fuer immer
       unsichtbar. Deshalb nur die, die endlos laufen oder laenger als fuenf Sekunden dauern.

   ZUR SPEZIFITAET: `html[data-bewegung="aus"] X` wiegt eine Klasse mehr als `X` und gewinnt
   unabhaengig von der Reihenfolge — auch gegen `.block-hero::before`, das weiter unten steht. */
html:not(.pwb-js) .marquee-track,
html:not(.pwb-js) .gm-track,
html:not(.pwb-js) .sx-slide.is-on img,
html:not(.pwb-js) .sx-slide:nth-child(even).is-on img,
html:not(.pwb-js) .block-hero::before,
html:not(.pwb-js) .fx-gradient,
html:not(.pwb-js) .fx-shiny { animation: none; }

html[data-bewegung="aus"] .marquee-track,
html[data-bewegung="aus"] .gm-track,
html[data-bewegung="aus"] .fx-gradient,
html[data-bewegung="aus"] .fx-shiny,
html[data-bewegung="aus"] .sx-slide.is-on img,
html[data-bewegung="aus"] .sx-slide:nth-child(even).is-on img { animation-play-state: paused; }
html[data-bewegung="aus"] .block-hero::before { animation-play-state: paused; }
/* Angehalten mitten im Lauf heisst: die zweite Haelfte des Bandes steht hinter dem Beschnitt.
   Lesbar bleibt sie nur, wenn man dorthin scrollen kann. */
html[data-bewegung="aus"] .marquee,
html[data-bewegung="aus"] .gallery-marquee { overflow-x: auto; }

/* «ZUM INHALT SPRINGEN» — WCAG 2.4.1 «Bypass Blocks», Stufe A.
   Erst beim Fokus sichtbar, und das ist hier NICHT das verbotene Verstecken: die Bedienung
   richtet sich ausschliesslich an Tastatur und Screenreader, und genau dort ist sie das erste,
   was man erreicht. Fuer die Maus gaebe es nichts zu ueberspringen — sie klickt direkt hin.
   WCAG-Technik G1 sieht diese Form ausdruecklich vor.
   `transform` statt `display:none`: ein weggeblendetes Element ist NICHT fokussierbar, der
   Sprung waere damit unerreichbar. Das ist der haeufigste Fehler bei dieser Bedienung.
   Keine Ueberblendung: der Sprung soll sofort da sein, nicht heranfahren. */
.pwb-sprung {
  position: fixed; z-index: 100; top: 0; left: 0;
  transform: translateY(-130%);
  display: inline-block; padding: .72rem 1.15rem;
  background: var(--paper, #fff); color: var(--ink, #1b1f27);
  border: 1px solid var(--line, rgba(0, 0, 0, .16));
  border-radius: 0 0 12px 0;
  font-family: inherit; font-size: .85rem; font-weight: 620; line-height: 1;
  text-decoration: none; white-space: nowrap;
  box-shadow: 0 8px 24px -14px rgba(0, 0, 0, .55);
}
.pwb-sprung:focus { transform: none; outline: 2px solid currentColor; outline-offset: -5px; }
/* Das Sprungziel traegt tabindex="-1", damit der Fokus wirklich umzieht. Es ist damit NICHT in
   der Tabulator-Reihenfolge, also verlangt WCAG 2.4.7 hier keinen sichtbaren Ring — ein Rahmen
   um die halbe Seite waere nur Irritation. */
#main:focus { outline: none; }
@media print { .pwb-sprung { display: none; } }

/* Die Bedienung selbst. Unten LINKS, weil unten rechts das Feld der Owner-Widgets ist
   (Chatbot, Consent) — `$site['globalEmbed']` setzt dort seit jeher ab.
   Sie ist gedeckt gefuellt, nicht durchscheinend: ueber einem dunklen Vollbild-Hero waere eine
   milchige Pille genau dort unlesbar, wo sie am dringendsten gebraucht wird. --paper/--ink
   tragen die Kontrastgarantie aus partials/layout.php (>= 7:1) in jeder Palette mit.
   Die sichtbare Beschriftung IST der zugaengliche Name (WCAG 2.5.3), das Zeichen daneben ist
   `aria-hidden`. 40px hoch — ueber der 24px-Mindestgroesse aus WCAG 2.5.8. */
.pwb-bewegung {
  position: fixed; z-index: 80;
  left: max(.9rem, env(safe-area-inset-left, 0px));
  bottom: max(.9rem, env(safe-area-inset-bottom, 0px));
  display: inline-flex; align-items: center; gap: .42rem;
  min-height: 40px; padding: .5rem .78rem;
  border: 1px solid var(--line, rgba(0, 0, 0, .16));
  border-radius: 999px;
  background: var(--paper, #fff);
  color: var(--ink, #1b1f27);
  font-family: inherit; font-size: .78rem; font-weight: 620; line-height: 1;
  letter-spacing: -.005em; white-space: nowrap; cursor: pointer;
  box-shadow: 0 8px 24px -14px rgba(0, 0, 0, .55);
}
.pwb-bewegung:hover { border-color: color-mix(in oklch, var(--ink, #1b1f27) 34%, var(--line, #ccc)); }
/* currentColor, nicht --accent: der Akzent ist markenabhaengig und auf hellen Paletten zu
   schwach fuer einen Fokusring. --ink traegt dieselbe Kontrastgarantie wie die Schrift. */
.pwb-bewegung:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }
.pwb-bewegung svg { flex: 0 0 auto; display: block; }
@media print { .pwb-bewegung { display: none; } }

/* "mesh" — own ambient background: soft light fields that breathe slowly and
   drift gently toward the cursor. Pure CSS gradients, positions driven by JS vars. */
body[data-bg="mesh"] .block-hero {
  background:
    radial-gradient(34rem 34rem at calc(28% + var(--mx, 0px)) calc(34% + var(--my, 0px)), color-mix(in oklch, var(--accent-soft) 40%, transparent), transparent 60%),
    radial-gradient(30rem 30rem at calc(76% + var(--mx2, 0px)) calc(26% + var(--my2, 0px)), color-mix(in oklch, var(--accent) 32%, transparent), transparent 60%),
    radial-gradient(42rem 42rem at calc(55% + var(--mx, 0px)) calc(84% + var(--my, 0px)), color-mix(in oklch, var(--accent-deep) 28%, transparent), transparent 64%),
    var(--paper);
}
body[data-bg="mesh"] .block-hero > .block-inner { position: relative; z-index: 1; }

/* "field" — own interactive Canvas dot grid (see field.js). */
body[data-bg="field"] .block-hero { background: radial-gradient(70% 90% at 50% 28%, var(--surface), var(--paper)); }
body[data-bg="field"] .block-hero > .block-inner { position: relative; z-index: 1; }
@media (prefers-reduced-motion: no-preference) {
  .block-hero::before { animation: aurora 24s ease-in-out infinite alternate; }
}
@keyframes aurora {
  0%   { transform: translate3d(-3%, -2%, 0) scale(1.05) rotate(-2deg); }
  50%  { transform: translate3d(4%, 3%, 0) scale(1.15) rotate(3deg); }
  100% { transform: translate3d(-2%, 5%, 0) scale(1.08) rotate(-1deg); }
}

/* ---------- Headings (sections) ---------- */
/* Display headings use --font-display (Satoshi by default — clean premium grotesk, not a curvy
   serif). font-optical-sizing:auto is harmless when the face has no opsz axis. */
.hero-title, .statement-quote,
.block-text :is(h1, h2), .block-cta :is(h1, h2), .block-contact :is(h1, h2), .features-heading,
.block-imagetext :is(h1, h2), .block-embed :is(h1, h2) {
  font-family: var(--font-display);
  font-optical-sizing: auto;
}
.block-text :is(h1, h2), .block-cta :is(h1, h2), .block-contact :is(h1, h2), .features-heading {
  font-weight: 700; font-size: var(--t-2xl); line-height: 1.1; letter-spacing: -0.02em; text-wrap: balance; margin: 0 0 1.2rem;
}

/* ---------- Text block ---------- */
.block-text.is-center { text-align: center; }
.block-text p { font-size: var(--t-lg); color: color-mix(in oklch, var(--ink) 88%, var(--muted)); max-width: 62ch; margin: 0 0 1.2em; }
.block-text.is-center p { margin-inline: auto; }
/* editorial drop-cap on the opening paragraph of a left-aligned text block (display face, brand accent).
   It's large display text, so the >=3:1 large-text bar applies even where accent-deep is a light hue. */
.block-text:not(.is-center) > .block-inner > p:first-of-type::first-letter {
  float: left; font-family: var(--font-display); font-weight: 700;
  font-size: 3.5em; line-height: .74; margin: .04em .14em 0 0; color: var(--accent-deep);
}

/* ---------- Features — editorial numbered list, no boxes ---------- */
.features-heading { margin-bottom: clamp(1.5rem, 4vw, 3rem); max-width: 20ch; }
.features-grid { list-style: none; padding: 0; margin: 0; display: grid; gap: 0; }
.feature-card {
  display: grid; grid-template-columns: auto 1fr; gap: clamp(1rem, 3vw, 2.4rem);
  align-items: start; padding: clamp(1.4rem, 3vw, 2.2rem) 0; border-top: 1px solid var(--line);
}
.feature-card:last-child { border-bottom: 1px solid var(--line); }
.feature-icon { display: inline-flex; width: 1.9rem; height: 1.9rem; color: var(--accent-deep); margin-top: .15rem; }
.feature-icon svg { width: 100%; height: 100%; }
.feature-card h3 { margin: 0 0 .35rem; font-size: var(--t-xl); font-weight: 640; letter-spacing: -0.015em; }
.feature-card p { margin: 0; color: var(--muted); font-size: var(--t-base); max-width: 48ch; }
@media (min-width: 48rem) {
  .feature-card { grid-template-columns: 3rem 1fr; align-items: baseline; }
}

/* ---------- CTA ---------- */
.block-cta { }
.block-cta .block-inner { background: var(--ink); color: var(--paper); border-radius: calc(var(--radius) * 1.4); padding: clamp(2.5rem, 6vw, 4.5rem); text-align: center; }
.block-cta h2 { color: var(--paper); }
.cta-text { color: color-mix(in oklch, var(--paper) 80%, transparent); margin: .4rem auto 1.8rem; max-width: 46ch; font-size: var(--t-lg); }
/* CTA sits on the dark ink panel: an inverse paper/ink button always inverts WITH the panel
   (incl. noir, where the panel is light) -> guaranteed >=15:1 text contrast + maximum pop. */
.block-cta .btn-primary { background: var(--paper); color: var(--ink); }

/* ---------- Contact ---------- */
.block-contact p { color: var(--muted); font-size: var(--t-lg); }
.contact-list { list-style: none; padding: 0; margin: 1.6rem 0 0; display: grid; gap: .4rem; font-size: var(--t-lg); }
.contact-list a { display: inline-flex; align-items: flex-end; min-height: 44px; color: var(--ink); text-decoration: none; border-bottom: 1px solid var(--line); padding-bottom: 2px; }
.contact-list a:hover { border-color: var(--accent); }
/* Öffnungszeiten: eine Zeile pro Tag/Gruppe — ohne diese Regel klebten die Spans inline
   zusammen («…19:00Sa 08:00…», Go-Live-Audit 09.08.2026). */
.contact-hours span { display: block; }
.contact-grid { display: grid; gap: clamp(1.6rem, 4vw, 3rem); margin-top: clamp(1.5rem, 3vw, 2.4rem); }
@media (min-width: 48rem) { .contact-grid { grid-template-columns: 0.9fr 1.1fr; align-items: start; } .contact-grid .contact-list { margin-top: 0; } }
.contact-form { display: grid; gap: .85rem; }
.contact-form label { display: grid; gap: .35rem; font-size: var(--t-sm); color: var(--muted); font-weight: 500; }
.contact-form input, .contact-form textarea { padding: .72rem .9rem; border: 1px solid var(--line); border-radius: 11px; background: color-mix(in oklch, var(--paper) 60%, var(--surface)); color: var(--ink); font: inherit; }
.contact-form input:focus, .contact-form textarea:focus { outline: 2px solid color-mix(in oklch, var(--accent) 45%, transparent); outline-offset: 1px; border-color: var(--accent); }
.contact-form textarea { resize: vertical; min-height: 7rem; }
.contact-form .btn-primary { justify-self: start; margin-top: .2rem; }
.hp { position: absolute !important; left: -9999px; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.form-ok { background: color-mix(in oklch, var(--accent) 12%, var(--surface)); border: 1px solid color-mix(in oklch, var(--accent) 30%, var(--line)); color: var(--accent-deep); padding: .75rem 1rem; border-radius: 11px; }

/* ---------- Footer ---------- */
.site-footer { width: min(100% - calc(var(--gutter) * 2), var(--maxw)); margin: 2rem auto 2.5rem; color: var(--muted); font-size: var(--t-sm); border-top: 1px solid var(--line); padding-top: 1.5rem; }
/* social brand-icon links (footer + contact) */
.social-links { list-style: none; margin: 0; padding: 0; display: flex; gap: .55rem; flex-wrap: wrap; }
.social-links a {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; border-radius: 999px; color: var(--ink);
  border: 1px solid var(--line); background: color-mix(in oklab, var(--card, #fff) 70%, transparent);
  transition: color .18s ease, border-color .18s ease, transform .18s ease, background .18s ease;
}
.social-links a:hover { color: var(--accent-ink, #fff); background: var(--accent); border-color: var(--accent); transform: translateY(-2px); }
.social-links svg { display: block; }
.footer-social { margin: 0 0 1rem; }
.contact-social { margin: 1.1rem 0 0; }
@media (prefers-reduced-motion: reduce) { .social-links a { transition: color .18s ease, background .18s ease; } .social-links a:hover { transform: none; } }
.footer-legal { margin: .4rem 0 0; display: flex; gap: .5rem; flex-wrap: wrap; }
.footer-legal a { display: inline-flex; align-items: center; min-height: 44px; color: var(--muted); text-decoration: none; }
.footer-legal a:hover { color: var(--ink); text-decoration: underline; }

/* ---------- Legal document (Impressum / Datenschutz) ---------- */
.block-legaldoc .block-inner { max-width: 70ch; }
.legaldoc h1 { font-size: var(--t-2xl); letter-spacing: -0.02em; margin: 0 0 1.2rem; }
.legaldoc h2 { font-size: var(--t-lg); margin: 2rem 0 .5rem; }
.legaldoc p { color: color-mix(in oklch, var(--ink) 86%, var(--muted)); line-height: 1.6; margin: 0 0 1rem; }
.legaldoc .ld-strong { font-weight: 640; color: var(--ink); }
.legaldoc a { color: var(--accent-deep, var(--ink)); }
.legaldoc .ld-stand { font-size: var(--t-sm); color: var(--muted); margin-top: 1.6rem; }
.legaldoc-todo { background: #fff8ec; border: 1px solid #f0d9a8; color: #6b5417; border-radius: 10px; padding: .7rem 1rem; font-size: .9rem; margin-bottom: 1.2rem; }

/* ---------- Hero art (custom SVG when no photo) ---------- */
.hero-media { position: relative; }
.hero-art { width: 100%; height: auto; aspect-ratio: 4 / 5; border-radius: var(--radius); display: block; box-shadow: 0 40px 80px -50px color-mix(in oklch, var(--ink) 60%, transparent); }
@media (max-width: 60rem) { .hero-art { aspect-ratio: 16 / 11; } }

/* ---------- Film grain — subtle editorial texture ---------- */
body::after {
  content: ""; position: fixed; inset: 0; pointer-events: none; z-index: 9000; opacity: .04; mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='150' height='150'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ---------- Image treatment ---------- */
/* Full colour by default (beauty/gastro/most SMEs sell the colour of their work). */
.media-img { width: 100%; height: 100%; object-fit: cover; border-radius: var(--radius); transition: filter .6s cubic-bezier(.2,.8,.2,1), transform .6s cubic-bezier(.2,.8,.2,1); }
/* Editorial grayscale (architecture/tech/legal) only when the theme opts in. */
[data-images="editorial"] .media-img { filter: grayscale(1) contrast(1.04); }
[data-images="editorial"] .media-img:hover { filter: grayscale(0) contrast(1); }
[data-palette="noir"] body::after, body[data-palette="noir"]::after { mix-blend-mode: screen; opacity: .06; }

/* ---------- Image + Text ---------- */
.it-grid { display: grid; gap: clamp(1.6rem, 4vw, 3.5rem); align-items: center; }
@media (min-width: 60rem) {
  /* editorial asymmetry: the tall image takes the narrower column, text the wider one. Flip-safe —
     the columns swap with the flip so the media stays the narrower side on either hand. */
  .it-grid { grid-template-columns: 5fr 7fr; }
  .block-imagetext.flip .it-grid { grid-template-columns: 7fr 5fr; }
  .block-imagetext.flip .it-media { order: 2; }
  /* no image → single column, never an empty media panel */
  .block-imagetext.no-media .it-grid { grid-template-columns: 1fr; }
}
.it-media { overflow: clip; border-radius: var(--radius); }
.it-media img { aspect-ratio: 4 / 5; }
.it-media:hover .media-img { transform: scale(1.03); }
.block-imagetext h2 { font-weight: 680; font-size: var(--t-2xl); line-height: 1.1; letter-spacing: -0.02em; margin: 0 0 1rem; text-wrap: balance; }
.it-body p { color: var(--muted); font-size: var(--t-lg); max-width: 46ch; }
/* Stacked variant (editor "Gestapelt — Titel über Bild"): title above a full-width image, text below. */
.block-imagetext.is-stack > .block-inner { max-width: 920px; }
.block-imagetext.is-stack .it-media { margin-top: 1.1rem; }
.block-imagetext.is-stack .it-media img { aspect-ratio: auto; width: 100%; height: auto; }
.block-imagetext.is-stack .it-body { margin-top: 1.1rem; }
.block-imagetext.is-stack .it-body p { max-width: 70ch; }

/* ---------- Single image ---------- */
.single-image { margin: 0; }
.single-image img { width: 100%; border-radius: var(--radius); display: block; }
.single-image figcaption { margin-top: .6rem; font-size: var(--t-sm); color: var(--muted); }

/* ---------- Gallery ---------- */
.gallery-grid { display: grid; gap: clamp(.8rem, 2vw, 1.4rem); grid-template-columns: 1fr; }
@media (min-width: 48rem) { .gallery-grid { grid-template-columns: 1fr 1fr; } .gallery-grid .g-wide { grid-column: 1 / -1; } }
.g-item { margin: 0; overflow: clip; border-radius: var(--radius); cursor: zoom-in; }
.g-item img { aspect-ratio: 3 / 4; }
.g-item.g-wide img { aspect-ratio: 16 / 9; }
.g-item:hover .media-img { transform: scale(1.03); }
.g-item figcaption { margin-top: .6rem; font-size: var(--t-sm); color: var(--muted); }
.g-item[hidden] { display: none; }

/* Masonry funnel — CSS columns (grid masonry not yet baseline 2026) */
.gallery-grid.is-masonry { display: block; column-count: 2; column-gap: clamp(.8rem, 2vw, 1.4rem); }
@media (min-width: 48rem) { .gallery-grid.is-masonry { column-count: 3; } }
@media (min-width: 75rem) { .gallery-grid.is-masonry { column-count: 4; } }
.is-masonry .g-item { break-inside: avoid; margin-bottom: clamp(.8rem, 2vw, 1.4rem); }
.is-masonry .g-item img { aspect-ratio: auto; height: auto; }

/* Carousel — scroll-snap track + buttons */
.gallery-carousel { position: relative; }
.gc-track { display: flex; gap: clamp(.8rem, 2vw, 1.4rem); overflow-x: auto; scroll-snap-type: x mandatory; scroll-behavior: smooth; scrollbar-width: none; padding-bottom: .4rem; }
.gc-track::-webkit-scrollbar { display: none; }
.gc-track .g-item { flex: 0 0 clamp(220px, 42%, 360px); scroll-snap-align: start; }
.gc-track .g-item img { aspect-ratio: 4 / 5; }
.gc-nav { position: absolute; top: 50%; transform: translateY(-50%); z-index: 2; width: 44px; height: 44px; border-radius: 50%;
  border: 1px solid var(--line); background: color-mix(in oklch, var(--paper, #fff) 88%, transparent); backdrop-filter: blur(4px);
  cursor: pointer; font-size: 1.5rem; line-height: 1; display: grid; place-items: center; color: var(--ink); }
.gc-prev { left: -6px; } .gc-next { right: -6px; }
.gc-nav:disabled { opacity: .35; cursor: default; }
@media (max-width: 40rem) { .gc-track .g-item { flex-basis: 78%; } }

/* Marquee — continuous CSS loop (track items are duplicated for seamlessness) */
.gallery-marquee { overflow: hidden; }
.gm-track { display: flex; gap: clamp(.8rem, 2vw, 1.4rem); width: max-content; animation: gmScroll 42s linear infinite; }
.gallery-marquee:hover .gm-track { animation-play-state: paused; }
.gm-track .g-item { flex: 0 0 auto; width: clamp(200px, 26vw, 300px); margin: 0; }
.gm-track .g-item img { aspect-ratio: 4 / 5; }
@keyframes gmScroll { to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) { .gm-track { animation: none; } .gallery-marquee { overflow-x: auto; } .gm-track .is-clone { display: none; } }

/* List — image + caption rows */
.gallery-list { display: grid; gap: clamp(1rem, 3vw, 1.8rem); }
.gallery-list .g-item { display: grid; gap: .8rem; }
@media (min-width: 40rem) { .gallery-list .g-item { grid-template-columns: clamp(220px, 32%, 340px) 1fr; align-items: center; column-gap: 1.6rem; } }
.gallery-list .g-item figcaption { margin: 0; font-size: var(--t-base); color: color-mix(in oklch, var(--ink) 84%, var(--muted)); }

/* Filter chips */
.gal-filter { display: flex; flex-wrap: wrap; gap: .5rem; margin-bottom: clamp(1rem, 3vw, 1.8rem); }
.gal-chip { display: inline-flex; align-items: center; min-height: 44px; border: 1px solid color-mix(in oklch, var(--ink) 18%, transparent); background: transparent; color: var(--muted);
  border-radius: 999px; padding: .42rem 1rem; font-size: var(--t-sm); font-weight: 500; cursor: pointer;
  transition: color .18s ease, background-color .18s ease, border-color .18s ease; }
.gal-chip:hover { color: var(--ink); }
.gal-chip.is-active { background: var(--accent, var(--ink)); border-color: var(--accent, var(--ink)); color: var(--accent-ink, #fff); }

/* Lightbox */
.pwb-lb { position: fixed; inset: 0; z-index: 90; display: none; align-items: center; justify-content: center;
  /* warm, soft scrim (not a harsh black box) so the photo just lifts off the page.
     Anchored to a fixed warm-dark — NOT --ink — so the lightbox always dims the page,
     including on the noir palette where --ink is near-white. */
  background: color-mix(in oklch, oklch(22% 0.02 40) 62%, transparent); backdrop-filter: blur(10px) saturate(1.1);
  padding: clamp(1rem, 4vw, 3rem); }
.pwb-lb.is-open { display: flex; animation: lbIn .22s ease; }
.pwb-lb.no-anim { animation: none; }
.pwb-lb.no-anim .lb-stage img { animation: none; }
@keyframes lbIn { from { opacity: 0; } }
.lb-stage { margin: 0; max-width: min(100%, 1100px); display: grid; gap: .8rem; justify-items: center; }
.lb-stage img { max-width: 100%; max-height: 82svh; width: auto; height: auto; border-radius: calc(var(--radius) * 1.2); display: block;
  box-shadow: 0 40px 120px -40px color-mix(in oklch, oklch(12% 0.02 40) 72%, transparent); animation: lbZoom .32s cubic-bezier(.2,.8,.2,1); }
@keyframes lbZoom { from { opacity: 0; transform: scale(.93); } }
.lb-stage figcaption { color: #e9ecf3; font-size: var(--t-sm); text-align: center; }
.lb-x, .lb-nav { position: absolute; border: 0; background: rgba(255,255,255,.1); color: #fff; cursor: pointer;
  border-radius: 50%; width: 48px; height: 48px; font-size: 1.7rem; line-height: 1; display: grid; place-items: center;
  transition: background-color .15s ease; }
.lb-x:hover, .lb-nav:hover { background: rgba(255,255,255,.24); }
.lb-x { top: clamp(.8rem, 3vw, 1.6rem); right: clamp(.8rem, 3vw, 1.6rem); }
.lb-prev { left: clamp(.4rem, 2vw, 1.4rem); top: 50%; transform: translateY(-50%); }
.lb-next { right: clamp(.4rem, 2vw, 1.4rem); top: 50%; transform: translateY(-50%); }
@media (max-width: 36rem) { .lb-nav { bottom: 1rem; top: auto; transform: none; } .lb-prev { left: 1rem; } .lb-next { right: 1rem; } }
@media (prefers-reduced-motion: reduce) { .pwb-lb { animation: none; } }

/* ---------- Statement (editorial pull-quote) ---------- */
.block-statement .block-inner { max-width: 980px; }
.statement-quote { position: relative; font-weight: 620; font-size: var(--t-3xl); line-height: 1.13; letter-spacing: -0.025em; margin: 0; text-wrap: balance; }
/* editorial pull-quote: an oversized accent quote glyph as a watermark behind the text (z-index 0,
   the quote text sits above it). Replaces the small inline quote marks that statement.php no longer emits. */
.statement-quote::before {
  content: "\201C"; position: absolute; left: -.04em; top: -.46em; z-index: 0;
  font-family: var(--font-display); font-weight: 700; font-size: 4.6em; line-height: 1;
  /* mixed toward paper so it stays visible on the dark ink beat even when the brand accent is dark
     (e.g. a navy accent vanished on the near-black panel); keeps the brand hue. */
  color: color-mix(in oklch, var(--accent) 58%, var(--paper)); opacity: .32; pointer-events: none;
}
.statement-quote > span { position: relative; z-index: 1; }
.statement-by { margin: 1.5rem 0 0; color: var(--muted); font-size: var(--t-base); }
.statement-author { color: var(--ink); font-weight: 600; }

/* ---------- Stats ---------- */
.stats-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(1.5rem, 4vw, 3rem); margin: 0; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); padding-block: clamp(1.8rem, 4vw, 2.8rem); }
@media (max-width: 40rem) { .stats-row { grid-template-columns: 1fr; gap: 1.4rem; } }
.stat-value { font-size: var(--t-3xl); font-weight: 720; letter-spacing: -0.03em; line-height: 1; font-variant-numeric: tabular-nums; }
.stat-label { margin: .5rem 0 0; color: var(--muted); font-size: var(--t-base); }

.btn-primary { will-change: transform; }

/* ---------- Scroll reveal (progressive enhancement) ---------- */
.reveal-on .block { opacity: 0; transform: translateY(16px); transition: opacity .7s ease, transform .7s cubic-bezier(.2,.8,.2,1); }
.reveal-on .block.in-view { opacity: 1; transform: none; }
/* Hero is above the fold — never hide it (LCP + no flash). */
.reveal-on main > .block:first-child { opacity: 1; transform: none; transition: none; }

/* ---------- Scroll choreography ----------
   Two progressive enhancements, both off under reduced-motion (this whole block is
   no-preference gated; reveal.js also omits .reveal-on under reduce / no-JS, so children
   below stay fully visible without it — never a stuck opacity:0). */
@media (prefers-reduced-motion: no-preference) {
  /* (a) Per-child stagger: grid children rise in sequence once their block enters view.
     Keyed off the same .reveal-on/.in-view that reveal.js already drives. Driven by a
     keyframe ANIMATION (not transition) so we never clobber a card's own `transition`.
     fill-mode:backwards holds the hidden `from` during the stagger delay, releasing
     afterwards (opacity:1 comes from the cascade, not from fill).
     .bento-grid is deliberately EXCLUDED: its cards own a transform-based cursor-tilt
     (card-fx / fx.js) and a transform keyframe here would fight that interaction — bento
     keeps its block-level reveal + tilt instead. features-grid/steps-list have no such
     transform interaction, so the rise is safe there. */
  /* P5/E2 — .sx-cards und .sx-stats sind seit 2026-08-03 mit dabei. Bis dahin galt der
     Stagger NUR fuer die Legacy-Container .features-grid/.steps-list; das neue sx-System
     hatte also WENIGER Choreografie als das, was es ersetzt hat: eine ganze Bildschirmseite
     mit acht Karten fadete als EIN Klotz ein. .sx-card traegt zwar eine eigene
     transform-Transition (Hover-Lift), aber keine dauerhafte JS-Interaktion wie .bento-card
     (card-fx/fx.js) — die Animation laeuft einmal ab und gibt den Transform danach wieder
     frei, der Hover funktioniert unveraendert. Genau deshalb bleibt .bento-grid weiter aussen
     vor. */
  .reveal-on :is(.features-grid, .steps-list, .sx-cards, .sx-stats) > * { opacity: 0; }
  .reveal-on .in-view :is(.features-grid, .steps-list, .sx-cards, .sx-stats) > * {
    opacity: 1; animation: staggerRise .6s cubic-bezier(.2,.8,.2,1) backwards;
  }
  .reveal-on .in-view :is(.features-grid, .steps-list, .sx-cards, .sx-stats) > :nth-child(2) { animation-delay: .05s; }
  .reveal-on .in-view :is(.features-grid, .steps-list, .sx-cards, .sx-stats) > :nth-child(3) { animation-delay: .1s; }
  .reveal-on .in-view :is(.features-grid, .steps-list, .sx-cards, .sx-stats) > :nth-child(4) { animation-delay: .15s; }
  .reveal-on .in-view :is(.features-grid, .steps-list, .sx-cards, .sx-stats) > :nth-child(5) { animation-delay: .2s; }
  .reveal-on .in-view :is(.features-grid, .steps-list, .sx-cards, .sx-stats) > :nth-child(6) { animation-delay: .25s; }
  .reveal-on .in-view :is(.features-grid, .steps-list, .sx-cards, .sx-stats) > :nth-child(7) { animation-delay: .3s; }
  .reveal-on .in-view :is(.features-grid, .steps-list, .sx-cards, .sx-stats) > :nth-child(n+8) { animation-delay: .35s; }

  /* (b) Hero-media parallax — native scroll-driven, gated by @supports (Chrome/Edge; others
     simply keep the static position). translateY drift stays inside the hero's centred
     whitespace, so overflow:clip never shows a gap. animation-timeline longhands avoid the
     `animation`-shorthand reset-to-auto gotcha. */
  @supports (animation-timeline: scroll()) {
    .block-hero .hero-media {
      animation-name: heroParallax;
      animation-timing-function: linear;
      animation-fill-mode: both;
      animation-duration: auto;
      animation-timeline: scroll(root block);
      animation-range: 0 100vh;
    }
  }
}
@keyframes staggerRise { from { opacity: 0; transform: translateY(18px); } to { opacity: 1; transform: none; } }
@keyframes heroParallax { from { transform: translateY(0); } to { transform: translateY(34px); } }

/* ---------- Text effects ---------- */
.fx-gradient { background: linear-gradient(100deg, var(--accent-deep), var(--accent), var(--accent-soft), var(--accent)); background-size: 250% 100%; -webkit-background-clip: text; background-clip: text; color: transparent; animation: fxgrad 8s linear infinite; }
@keyframes fxgrad { to { background-position: 250% 0; } }
.fx-shiny { background: linear-gradient(100deg, var(--ink) 42%, var(--accent-soft) 50%, var(--ink) 58%); background-size: 250% 100%; -webkit-background-clip: text; background-clip: text; color: transparent; animation: fxshiny 5s linear infinite; }
@keyframes fxshiny { to { background-position: -250% 0; } }
/* Der Systemwunsch «weniger Bewegung» erreichte diese beiden bis 07.08. NICHT: `fx.js` steigt
   bei reduced-motion aus, aber die Klasse steht im Markup (blocks/hero.php, props.fx) und die
   Animation steckt im CSS. Betrifft im Bestand 3 Sites — primeweb-gmbh (shiny), studio-grauwerk
   und trattoria-sole (gradient). Aufgefallen bei der Frage, ob die Anhalten-Bedienung bei
   reduced-motion ueberhaupt noch etwas anzuhalten haette. Sie hatte.
   `animation: none` friert die Position ein, der Verlauf bleibt sichtbar — der Text
   verschwindet also nicht (background-clip: text malt weiter). */
@media (prefers-reduced-motion: reduce) { .fx-gradient, .fx-shiny { animation: none; } }
.fx-word { display: inline-block; opacity: 0; transform: translateY(.45em); transition: opacity .6s ease, transform .6s cubic-bezier(.2,.8,.2,1); }
.fx-reveal.in .fx-word { opacity: 1; transform: none; }

/* ---------- Bento ---------- */
.block-bento .features-heading { max-width: 22ch; }
.bento-grid { display: grid; gap: clamp(.8rem,2vw,1.2rem); grid-template-columns: 1fr; margin-top: clamp(1.5rem,3vw,2.5rem); }
@media (min-width: 48rem) { .bento-grid { grid-template-columns: repeat(3, 1fr); } .bento-card.is-big { grid-column: span 2; grid-row: span 2; } }
.bento-card { position: relative; display: flex; flex-direction: column; overflow: hidden;
  background: linear-gradient(158deg, var(--surface), color-mix(in oklch, var(--paper) 60%, var(--surface)));
  border: 1px solid var(--line); border-radius: calc(var(--radius) * 1.15); padding: clamp(1.4rem,2.6vw,2.1rem);
  box-shadow: 0 1px 2px color-mix(in oklch, var(--ink) 6%, transparent);
  transition: transform .35s cubic-bezier(.2,.8,.2,1), box-shadow .35s ease, border-color .35s ease; }
.bento-card:hover { transform: translateY(-4px); border-color: color-mix(in oklch, var(--accent) 38%, var(--line));
  box-shadow: 0 24px 44px -26px color-mix(in oklch, var(--accent) 55%, #000); }
.feature-icon { display: inline-grid; place-items: center; width: 46px; height: 46px; border-radius: 13px;
  background: color-mix(in oklch, var(--accent) 13%, transparent); color: var(--accent-deep); margin-bottom: 1rem; }
.feature-icon svg { width: 22px; height: 22px; }
.bento-card h3 { margin: 0 0 .35rem; font-size: var(--t-lg); font-weight: 660; letter-spacing: -.012em; }
.bento-card p { margin: 0; color: var(--muted); line-height: 1.55; }
.bento-card.is-big h3 { font-size: var(--t-2xl); margin-top: .1rem; }
.bento-card.is-big p { font-size: var(--t-lg); max-width: 34ch; }
/* fill the large tile so it never reads as empty */
.bento-card.is-big::after { content: ""; position: absolute; right: -18%; bottom: -32%; width: 65%; height: 78%;
  background: radial-gradient(circle at 70% 70%, color-mix(in oklch, var(--accent) 24%, transparent), transparent 68%); pointer-events: none; }
.card-fx::before { content: ""; position: absolute; inset: 0; border-radius: inherit; opacity: 0; transition: opacity .3s ease; pointer-events: none; z-index: 0;
  background: radial-gradient(15rem 15rem at var(--mx,50%) var(--my,50%), color-mix(in oklch, var(--accent) 22%, transparent), transparent 60%); }
.card-fx:hover::before { opacity: 1; }
.bento-card > * { position: relative; z-index: 1; }
.bento-card.is-link { text-decoration: none; color: inherit; }
.bento-more { display: inline-block; margin-top: auto; padding-top: 1rem; color: var(--accent-deep); font-weight: 640; font-size: .92rem; }
.bento-card.is-link:hover .bento-more { text-decoration: underline; }

/* ---------- Marquee ---------- */
.block-marquee { padding-block: clamp(2rem,4vw,3rem); }
.marquee { overflow: hidden; -webkit-mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent); mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent); }
.marquee-track { display: inline-flex; gap: clamp(2rem,5vw,4rem); white-space: nowrap; animation: marquee 30s linear infinite; will-change: transform; }
.marquee:hover .marquee-track { animation-play-state: paused; }
@keyframes marquee { to { transform: translateX(-50%); } }
.marquee-item { font-weight: 640; font-size: clamp(1.1rem,2.5vw,1.6rem); color: var(--muted); letter-spacing: -.01em; }
/* Dieselbe Luecke wie oben bei .fx-*: die Laufschrift lief auch bei reduced-motion weiter.
   `overflow-x: auto` gehoert dazu, sonst waere die zweite Haelfte des Bandes hinter dem
   Beschnitt unerreichbar — genau die Loesung, die `.gallery-marquee` weiter oben schon hat. */
@media (prefers-reduced-motion: reduce) { .marquee-track { animation: none; } .marquee { overflow-x: auto; } }

/* ---------- Steps ---------- */
.steps-list { list-style: none; padding: 0; margin: clamp(1.5rem,3vw,2.5rem) 0 0; }
.step { display: grid; grid-template-columns: auto 1fr; gap: clamp(1rem,3vw,2.4rem); align-items: baseline; padding: clamp(1.3rem,3vw,2rem) 0; border-top: 1px solid var(--line); }
.step:last-child { border-bottom: 1px solid var(--line); }
.step-no { font-size: var(--t-xl); font-weight: 720; color: var(--accent-2, var(--accent-deep)); font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
.step h3 { margin: 0 0 .3rem; font-size: var(--t-lg); font-weight: 640; }
.step p { margin: 0; color: var(--muted); max-width: 52ch; }

/* ---------- Accordion ---------- */
.block-accordion .block-inner { max-width: 760px; }
.acc-list { display: grid; gap: .6rem; margin-top: clamp(1rem, 3vw, 1.8rem); }
.acc-item { border: 1px solid var(--line); border-radius: var(--radius); overflow: clip; background: color-mix(in oklch, var(--paper, #fff) 88%, transparent); }
.acc-q { cursor: pointer; padding: 1rem 1.2rem; font-weight: 620; font-size: var(--t-base); list-style: none; display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.acc-q::-webkit-details-marker { display: none; }
.acc-q::after { content: "+"; font-weight: 400; font-size: 1.4em; line-height: 1; color: var(--accent-deep, var(--muted)); transition: transform .2s ease; }
.acc-item[open] .acc-q::after { content: "\2013"; }
.acc-a { padding: 0 1.2rem 1.2rem; color: color-mix(in oklch, var(--ink) 82%, var(--muted)); line-height: 1.6; }
@media (prefers-reduced-motion: no-preference) { .acc-item[open] .acc-a { animation: accIn .25s ease; } }
@keyframes accIn { from { opacity: 0; transform: translateY(-4px); } }

/* ---------- Embed ---------- */
.block-embed h2 { font-weight: 680; font-size: var(--t-2xl); letter-spacing: -.02em; line-height: 1.1; margin: 0; }
.embed-wrap { margin-top: clamp(1.2rem,3vw,2rem); }
/* aspect-ratio reserves space (kills CLS) + a min-height floor keeps booking/app widgets usable at
   our narrow container width; maps are width-proportional and get a clean 16:9 via .is-map below. */
.embed-wrap iframe { width: 100%; aspect-ratio: 16 / 10; min-height: 520px; border: 0; border-radius: var(--radius); display: block; }
.embed-wrap img, .embed-wrap video { max-width: 100%; border-radius: var(--radius); }
.embed-frame { margin-top: clamp(1.2rem,3vw,2rem); border-radius: var(--radius); overflow: clip; background: color-mix(in oklch, var(--ink) 5%, transparent); }
.embed-frame iframe { width: 100%; aspect-ratio: 16 / 10; min-height: 560px; border: 0; display: block; }
.block-embed.is-map .embed-frame iframe { aspect-ratio: 16 / 9; min-height: 0; height: auto; }
.embed-err { margin-top: 1rem; color: var(--danger, #b3261e); font-size: var(--t-sm); }
/* Premium location card — shown for Google Maps when the value isn't an embeddable URL
   (address or share-link), since Google refuses to frame those. Always renders, never blank. */
.embed-map { margin-top: clamp(1.2rem,3vw,2rem); display: grid; justify-items: center; gap: .7rem;
  text-align: center; padding: clamp(1.6rem,4vw,2.6rem); border-radius: var(--radius);
  border: 1px solid var(--line); background: color-mix(in oklch, var(--ink) 4%, transparent); }
.embed-map-pin { font-size: 2rem; line-height: 1; }
.embed-map-label { margin: 0; font-weight: 600; color: var(--ink); }

/* C5 — DSG consent gate: click-to-load card for third-party iframes/scripts (Calendly, Shore, Maps
   embed, …). The real embed sits inert in a <template>; nothing loads until consent. The card reserves
   the embed's space (same aspect/min-height) so swapping in the iframe causes no layout shift. */
.embed-consent { margin-top: clamp(1.2rem,3vw,2rem); border-radius: var(--radius); overflow: clip;
  border: 1px solid var(--line); background: color-mix(in oklch, var(--ink) 4%, transparent);
  display: grid; place-items: center; aspect-ratio: 16 / 10; min-height: 560px; padding: clamp(1.6rem,4vw,2.6rem); }
.embed-consent.is-map { aspect-ratio: 16 / 9; min-height: 0; }
.embed-consent-card { display: grid; justify-items: center; gap: .7rem; text-align: center; max-width: 44ch; }
.embed-consent-icon { font-size: 2rem; line-height: 1; }
.embed-consent-label { margin: 0; font-weight: 600; color: var(--ink); font-size: var(--t-lg); }
.embed-consent-note { margin: 0; color: var(--muted); font-size: var(--t-sm); line-height: 1.5; }
.embed-consent .btn-primary { margin-top: .3rem; }

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .001ms !important; transition-duration: .001ms !important; }
  .reveal-on .block { opacity: 1; transform: none; }
}

/* ---------- A2: card photos, person cards, timeline year, trust bar, rich footer ---------- */

/* bento card with a photo instead of the icon glyph */
.bento-media { display: block; margin-bottom: 1rem; }
.bento-media img { width: 100%; aspect-ratio: 16 / 10; object-fit: cover; border-radius: 11px; display: block; }
.bento-card.is-big .bento-media img { aspect-ratio: 16 / 9; }

/* features as person cards (photo + role) */
.feature-card.is-person { grid-template-columns: auto 1fr; align-items: start; }
.feature-photo { display: block; }
.feature-photo img { width: 84px; height: 84px; object-fit: cover; border-radius: 16px; display: block; }
@media (min-width: 48rem) { .feature-card.is-person { grid-template-columns: 110px 1fr; align-items: start; } .feature-photo img { width: 110px; height: 110px; } }
.feature-role { margin: 0 0 .4rem; color: var(--accent-deep); font-size: var(--t-sm); font-weight: 600; letter-spacing: .01em; }

/* steps as a year-labelled milestone timeline */
.step.is-year .step-no { font-size: var(--t-lg); font-weight: 700; }

/* trust bar */
.block-trust { padding-block: clamp(1.4rem, 3vw, 2.2rem); }
.trust-inner { display: flex; flex-wrap: wrap; align-items: center; gap: clamp(1rem, 3vw, 2.4rem); }
.trust-rating { display: inline-flex; align-items: center; gap: .6rem; }
.trust-stars { color: var(--accent); font-size: 1.05rem; letter-spacing: .06em; }
.trust-stars .star { color: color-mix(in oklch, var(--ink) 18%, transparent); }
.trust-stars .star.on { color: var(--accent); }
.trust-score { color: var(--muted); font-size: var(--t-sm); }
.trust-score strong { color: var(--ink); font-weight: 700; }
.trust-badges { list-style: none; display: flex; flex-wrap: wrap; gap: .5rem; margin: 0; padding: 0; }
.trust-badges li { font-size: var(--t-sm); font-weight: 600; color: var(--ink); padding: .32rem .8rem; border: 1px solid var(--line); border-radius: 999px; background: color-mix(in oklch, var(--accent) 7%, transparent); }

/* rich multi-column footer */
.footer-cols { display: grid; grid-template-columns: 1fr; gap: clamp(1.6rem, 4vw, 3rem); }
@media (min-width: 40rem) { .footer-cols { grid-template-columns: 1.6fr 1fr 1.2fr; } }
.footer-name { margin: 0 0 .8rem; font-weight: 700; font-size: var(--t-lg); color: var(--ink); letter-spacing: -.01em; }
.footer-h { margin: 0 0 .7rem; font-size: var(--t-xs, .78rem); text-transform: uppercase; letter-spacing: .08em; color: var(--muted); font-weight: 700; }
.footer-nav ul, .footer-contact ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .45rem; }
.footer-nav a, .footer-contact a { color: var(--ink); text-decoration: none; }
.footer-nav a:hover, .footer-contact a:hover { color: var(--accent-deep); text-decoration: underline; }
.footer-contact li { color: var(--muted); }
.footer-base { display: flex; flex-wrap: wrap; justify-content: space-between; gap: .6rem; margin-top: clamp(1.6rem, 4vw, 2.6rem); padding-top: 1.2rem; border-top: 1px solid var(--line); }
.footer-base p { margin: 0; }

/* ============================================================
   PREMIUM DEPTH & FRAMER-STYLE MEDIA — Optik-Track A (2026-06-19)
   Tames the candy ambient wash into a whisper, and gives every media box
   layered depth + a hairline frame ring -> premium "framer" feel instead of
   flat boxes on a childish pastel gradient. Appended last so it wins at equal
   specificity. Shadow drops use a fixed dark (read on light paper); the ring is
   ink-derived so it adapts (dark hairline on light, light hairline on dark).
   ============================================================ */
:root {
  --ring: color-mix(in oklch, var(--ink) 14%, transparent);
  --shadow-card:  0 1px 2px -1px oklch(20% 0.02 260 / .18), 0 10px 26px -18px oklch(16% 0.02 260 / .34);
  --shadow-media: 0 0 0 1px var(--ring),
                  0 2px 6px -2px oklch(20% 0.02 260 / .22),
                  0 26px 52px -30px oklch(15% 0.02 260 / .44),
                  0 64px 100px -70px oklch(12% 0.02 260 / .36);
}
/* restrained ambient: a whisper of brand colour at the edges, never a pastel wash */
body[data-bg="mesh"] .block-hero {
  background:
    radial-gradient(46rem 40rem at calc(12% + var(--mx, 0px)) calc(2% + var(--my, 0px)), color-mix(in oklch, var(--accent-soft) 15%, transparent), transparent 60%),
    radial-gradient(44rem 38rem at calc(94% + var(--mx2, 0px)) calc(-4% + var(--my2, 0px)), color-mix(in oklch, var(--accent) 11%, transparent), transparent 62%),
    radial-gradient(64rem 52rem at calc(62% + var(--mx, 0px)) calc(120% + var(--my, 0px)), color-mix(in oklch, var(--accent-deep) 8%, transparent), transparent 66%),
    var(--paper);
}
/* framer-style image boxes: rounded + layered depth + hairline ring */
.it-media, .hero-media img, .hero-art, .single-image img { box-shadow: var(--shadow-media); }
.bento-media img { box-shadow: var(--shadow-card); border-radius: 12px; }
.g-item { box-shadow: var(--shadow-card); }
/* cards: resting depth, stronger lift on hover */
.bento-card { box-shadow: var(--shadow-card); }
.bento-card:hover { box-shadow: var(--shadow-media); }

/* PREMIUM NEUTRAL CANVAS — the #1 "childish" tell was a saturated pastel PAGE wash.
   Keep each palette's accent + ink (colour lives in CTAs/details/imagery), but pull the
   PAPER/SURFACE back to a near-white with only a whisper of hue. Appended last -> wins. */
[data-palette="blush"]     { --paper: oklch(98.6% 0.004 20);  --surface: oklch(97% 0.006 24); --line: oklch(90% 0.008 20); }
[data-palette="warm-clay"] { --paper: oklch(98.6% 0.005 70);  --surface: oklch(97% 0.007 64); --line: oklch(90% 0.009 60); }
[data-palette="sand"]      { --paper: oklch(98.6% 0.006 90);  --surface: oklch(97% 0.008 85); --line: oklch(90% 0.01 82); }
[data-palette="forest"]    { --paper: oklch(98.7% 0.004 150); --surface: oklch(97.2% 0.006 150); --line: oklch(90% 0.008 150); }
/* slate-blue (default) paper is already near-neutral (chroma .006) — left as is. noir stays dark. */

/* ============================================================
   PREMIUM MENU HEADER — Optik-Track 2 (2026-06-19)
   'pill'  = floating glass capsule (new default for every site)
   'over'  = transparent glass over a full-bleed hero image (light text)
   'solid' = legacy translucent bar (kept as fallback)
   Appended last -> overrides the base .site-header at higher specificity.
   ============================================================ */
.site-header[data-style="pill"] {
  background: transparent; border-bottom: 0; -webkit-backdrop-filter: none; backdrop-filter: none;
  padding-top: clamp(.55rem, 1.4vw, 1rem); transition: padding-top .25s ease;
}
.site-header[data-style="pill"] .header-inner {
  padding: .5rem .8rem .5rem 1.5rem; border-radius: 999px;
  background: color-mix(in oklch, var(--paper) 30%, white);   /* brighter than the page so it lifts */
  -webkit-backdrop-filter: saturate(1.4) blur(16px); backdrop-filter: saturate(1.4) blur(16px);
  border: 1px solid color-mix(in oklch, var(--line) 80%, transparent);
  box-shadow: 0 1px 2px -1px rgba(18,20,38,.10), 0 12px 28px -14px rgba(18,20,38,.22), 0 28px 60px -40px rgba(18,20,38,.18);
  transition: padding .25s ease, box-shadow .25s ease, background .25s ease;
}
.site-header[data-style="pill"] .brand-logo { transition: height .25s ease; }
/* scrolled state — toggled by the header scroll listener in partials/header.php: compact the pill
   (less padding + a smaller logo) and deepen the lift, so it reads as a tight floating bar on scroll. */
.site-header[data-style="pill"].is-scrolled { padding-top: clamp(.3rem, .8vw, .6rem); }
.site-header[data-style="pill"].is-scrolled .header-inner { padding-block: .32rem; box-shadow: 0 2px 6px -2px rgba(18,20,38,.16), 0 18px 40px -18px rgba(18,20,38,.34); }
.site-header[data-style="pill"].is-scrolled .brand-logo { height: clamp(40px, 4.6vw, 58px); }
/* solid (legacy bar) scrolled state — deepen the hairline into a soft drop so it separates from content */
.site-header[data-style="solid"] { transition: box-shadow .25s ease, background .25s ease; }
.site-header[data-style="solid"].is-scrolled { box-shadow: 0 10px 30px -22px rgba(18,20,38,.5); background: color-mix(in oklch, var(--paper) 90%, transparent); }
/* transparent glass over a full-bleed hero image — light text for legibility on the photo */
.site-header[data-style="over"] {
  position: absolute; inset: 0 0 auto 0; background: transparent; border-bottom: 0;
  -webkit-backdrop-filter: none; backdrop-filter: none; padding-top: clamp(.55rem, 1.4vw, 1rem);
}
.site-header[data-style="over"] .header-inner {
  padding: .5rem .8rem .5rem 1.5rem; border-radius: 999px;
  background: color-mix(in oklch, black 22%, transparent);
  -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
  border: 1px solid color-mix(in oklch, white 24%, transparent);
  box-shadow: 0 18px 40px -24px rgba(0,0,0,.5);
}
.site-header[data-style="over"] .brand { color: #fff; }
.site-header[data-style="over"] .nav-burger { color: #fff; }
.site-header[data-style="over"] .site-nav a { color: rgba(255,255,255,.82); }
.site-header[data-style="over"] .site-nav a[aria-current="page"],
.site-header[data-style="over"] .site-nav a:hover { color: #fff; }
.site-header[data-style="over"] .locale-switcher a { color: rgba(255,255,255,.7); }
.site-header[data-style="over"] .locale-switcher a.is-current { color: #fff; }

/* OWNER-DIREKTIVE 09.08.2026 — Header als Overlay über dem Startbild.
   Beginnt die Seite mit einem Vollbild-Cover, nimmt der Header KEINEN eigenen Streifen mehr ein:
   das Cover läuft bis zur Oberkante durch, die Pille schwebt darüber (wie beim 'over'-Skin, der
   das für dunkle Cover schon immer tat). Gesetzt von partials/header.php via [data-overlay]. */
.site-header[data-overlay="1"] {
  position: absolute; inset: 0 0 auto 0;
  background: transparent; border-bottom: 0;
  -webkit-backdrop-filter: none; backdrop-filter: none;
}

/* Faint neutral PAGE so white pills / cards / framer-media boxes lift off it (the Pawwell move).
   Light palettes only — noir/dark canvases keep their dark page. */
body:not([data-palette="noir"]) { background: color-mix(in oklch, var(--paper) 93%, var(--ink)); }
.site-header[data-style="pill"] .header-inner { border-color: color-mix(in oklch, var(--line) 92%, transparent); }
/* the hero's own paper base should match the brighter card-white, not the grey page */
body:not([data-palette="noir"]) .block-hero { background-color: var(--paper); }

/* ============================================================
   NAV LAYOUTS — P2 (2026-07-26): [data-nav] = STRUCTURE, orthogonal to [data-style] = SKIN.
   Reuses .site-nav / .nav-burger / #pwb-navtoggle. Appended last -> wins on specificity.
   12 layouts: classic dropdown hamburger hybrid sidebar-left slidein-right onepager
   circular mega overlay sticky-over split.
   ============================================================ */
html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
.sx[id], .block[id] { scroll-margin-top: 6rem; }               /* onepager anchor clearance under sticky header */
.nav-scrim, .nav-close { display: none; }
.nav-caret { display: inline-block; width: .46em; height: .46em; border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor; transform: rotate(45deg) translateY(-.12em); margin-left: .15em; opacity: .6; }

/* burger bars (shared) — the mobile block defines these inside a media query; repeat here unscoped so
   PANEL layouts show a working burger on desktop too. Same values -> no conflict when both apply. */
.nav-burger span, .nav-burger span::before, .nav-burger span::after {
  content: ""; display: block; width: 22px; height: 2px; background: currentColor; border-radius: 2px;
  transition: transform .2s ease, opacity .2s ease; }
.nav-burger span { position: relative; }
.nav-burger span::before { position: absolute; left: 0; top: -7px; }
.nav-burger span::after  { position: absolute; left: 0; top: 7px; }
.nav-toggle:checked ~ .nav-burger span,
.nav-toggle:checked ~ .site-nav .nav-close { /* (X handled per-context below) */ }

/* ---- dropdown: hover submenu on items that carry children ---- */
.nav-group { position: relative; display: inline-flex; align-items: center; }
.nav-group .nav-top { display: inline-flex; align-items: center; }
.nav-sub { position: absolute; top: 100%; left: 0; min-width: 12rem; display: grid; gap: .1rem; padding: .5rem;
  background: color-mix(in oklch, var(--paper) 18%, white); border: 1px solid color-mix(in oklch, var(--line) 85%, transparent);
  border-radius: 14px; box-shadow: 0 20px 44px -24px rgba(18,20,38,.42); opacity: 0; visibility: hidden;
  transform: translateY(6px); transition: opacity .18s ease, transform .18s ease, visibility .18s; z-index: 40; }
.nav-sub a { padding: .5rem .75rem; border-radius: 8px; white-space: nowrap; color: var(--muted); }
/* Submenue-Links lesen gegen das HELLE PANEL, nie gegen den Header-Skin (2026-08-02): der
   over-Skin faerbte .site-nav a weiss (0-2-1 schlaegt .nav-sub a 0-1-1) — auf dem weissen
   Dropdown/Mega-Panel waren die Eintraege unsichtbar. Gleiche Lehre wie app.css Panel-Block. */
.site-header .site-nav .nav-sub a { color: var(--muted); }
.site-header .site-nav .nav-sub a:hover,
.site-header .site-nav .nav-sub a[aria-current="page"] { color: var(--ink); }
.nav-sub a:hover { background: color-mix(in oklch, var(--accent) 12%, transparent); color: var(--ink); }
.nav-sub a::after { display: none; }
.nav-group:hover .nav-sub, .nav-group:focus-within .nav-sub { opacity: 1; visibility: visible; transform: none; }

/* ---- mega: full-bleed columns panel ---- */
.site-header[data-nav="mega"] .nav-group { position: static; }
.site-header[data-nav="mega"] .nav-sub { left: 0; right: 0; width: min(100% - 2rem, var(--maxw)); margin-inline: auto;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); gap: .2rem 1.4rem; padding: 1.4rem 1.6rem; }

/* ---- PANEL layouts: burger opens an off-canvas / overlay panel on EVERY viewport ---- */
.site-header[data-nav="hamburger"] .nav-burger,
.site-header[data-nav="overlay"] .nav-burger,
.site-header[data-nav="sidebar-left"] .nav-burger,
.site-header[data-nav="slidein-right"] .nav-burger,
.site-header[data-nav="circular"] .nav-burger {
  display: inline-flex; align-items: center; justify-content: center; width: 46px; height: 46px; margin-left: auto;
  flex: 0 0 auto; cursor: pointer; border-radius: 12px; color: var(--ink);
  background: color-mix(in oklch, currentColor 8%, transparent); }
/* CRITICAL: backdrop-filter on the header / pill creates a containing block, which would TRAP the
   position:fixed panels inside the header instead of covering the viewport. Neutralise it for panel
   layouts so the off-canvas drawers + fullscreen overlay escape to the viewport. */
.site-header[data-nav="hamburger"], .site-header[data-nav="hamburger"] .header-inner,
.site-header[data-nav="overlay"], .site-header[data-nav="overlay"] .header-inner,
.site-header[data-nav="sidebar-left"], .site-header[data-nav="sidebar-left"] .header-inner,
.site-header[data-nav="slidein-right"], .site-header[data-nav="slidein-right"] .header-inner,
.site-header[data-nav="circular"], .site-header[data-nav="circular"] .header-inner {
  -webkit-backdrop-filter: none !important; backdrop-filter: none !important;
}
/* panel base (off-canvas drawers + overlay share this) */
.site-header[data-nav="sidebar-left"] .site-nav,
.site-header[data-nav="slidein-right"] .site-nav,
.site-header[data-nav="overlay"] .site-nav,
.site-header[data-nav="circular"] .site-nav {
  position: fixed; z-index: 60; margin: 0; display: flex; flex-direction: column; flex-wrap: nowrap;
  gap: .3rem; align-items: stretch; justify-content: flex-start;
  background: color-mix(in oklch, var(--paper) 8%, white);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  visibility: hidden; transition: transform .32s cubic-bezier(.4,0,.2,1), opacity .28s ease, visibility .32s; }
.site-header[data-nav="hamburger"] .site-nav {
  position: absolute; top: calc(100% + .4rem); right: max(1rem, calc((100% - var(--maxw)) / 2)); z-index: 60; margin: 0;
  display: flex; flex-direction: column; gap: .1rem; width: min(78vw, 260px); padding: .6rem;
  background: color-mix(in oklch, var(--paper) 14%, white); border: 1px solid color-mix(in oklch, var(--line) 85%, transparent);
  border-radius: 16px; box-shadow: 0 24px 50px -28px rgba(18,20,38,.5);
  opacity: 0; visibility: hidden; transform: translateY(-8px) scale(.98); transform-origin: top right;
  transition: opacity .2s ease, transform .2s ease, visibility .2s; }
.site-header[data-nav="hamburger"] .site-nav a { padding: .6rem .8rem; border-radius: 10px; color: var(--muted); }
.site-header[data-nav="hamburger"] .site-nav a:hover,
.site-header[data-nav="hamburger"] .site-nav a[aria-current="page"] { background: color-mix(in oklch, var(--accent) 10%, transparent); color: var(--ink); }
.site-header[data-nav="hamburger"] .site-nav a::after { display: none; }

/* left drawer / right drawer */
.site-header[data-nav="sidebar-left"] .site-nav { inset: 0 auto 0 0; width: min(82vw, 300px); padding: 5rem 1.4rem 1.4rem;
  transform: translateX(-100%); box-shadow: 24px 0 60px -30px rgba(18,20,38,.5); }
.site-header[data-nav="slidein-right"] .site-nav { inset: 0 0 0 auto; width: min(82vw, 320px); padding: 5rem 1.4rem 1.4rem;
  transform: translateX(100%); box-shadow: -24px 0 60px -30px rgba(18,20,38,.5); }
.site-header[data-nav="sidebar-left"] .site-nav a,
.site-header[data-nav="slidein-right"] .site-nav a { padding: .7rem .4rem; font-size: var(--t-lg); color: var(--muted); }
.site-header[data-nav="sidebar-left"] .site-nav a:hover,
.site-header[data-nav="slidein-right"] .site-nav a:hover { color: var(--ink); }
.site-header[data-nav="sidebar-left"] .site-nav a::after,
.site-header[data-nav="slidein-right"] .site-nav a::after { display: none; }

/* panel links must read against the PANEL background, not the header skin — the 'over' skin paints nav
   links (incl. the higher-specificity aria-current rule) white, which vanished on a white panel. */
.site-header[data-nav="overlay"] .site-nav a, .site-header[data-nav="overlay"] .site-nav a[aria-current="page"],
.site-header[data-nav="sidebar-left"] .site-nav a, .site-header[data-nav="sidebar-left"] .site-nav a[aria-current="page"],
.site-header[data-nav="slidein-right"] .site-nav a, .site-header[data-nav="slidein-right"] .site-nav a[aria-current="page"],
.site-header[data-nav="hamburger"] .site-nav a, .site-header[data-nav="hamburger"] .site-nav a[aria-current="page"],
.site-header[data-nav="circular"] .site-nav a, .site-header[data-nav="circular"] .site-nav a[aria-current="page"] {
  color: var(--ink); }
/* fullscreen overlay — grid place-content is a bulletproof centre (flex column centring fought the
   base .site-nav's align/wrap rules). */
.site-header[data-nav="overlay"] .site-nav { inset: 0; padding: 2rem; display: grid; grid-auto-flow: row;
  place-content: center; justify-items: center; gap: 1rem;
  background: color-mix(in oklch, var(--paper) 4%, white); opacity: 0; transform: none; }
.site-header[data-nav="overlay"] .site-nav a { font-family: var(--font-display); font-size: clamp(1.8rem, 1.2rem + 3vw, 3.2rem);
  font-weight: 640; letter-spacing: -.02em; color: var(--ink); padding: .2rem .6rem; }
.site-header[data-nav="overlay"] .site-nav a::after { display: none; }

/* circular / radial: items fan around the centre */
.site-header[data-nav="circular"] .site-nav { inset: 0; padding: 0; align-items: center; justify-content: center; opacity: 0;
  background: radial-gradient(circle at 50% 50%, color-mix(in oklch, var(--paper) 6%, white) 0%, color-mix(in oklch, var(--paper) 2%, white) 100%); }
.site-header[data-nav="circular"] .site-nav a { position: absolute; left: 50%; top: 50%; color: var(--ink);
  font-weight: 640; font-size: var(--t-lg); transform: translate(-50%, -50%) rotate(0) translateY(0) scale(.4); opacity: 0;
  transition: transform .38s cubic-bezier(.2,.9,.3,1.2), opacity .3s ease; }
.site-header[data-nav="circular"] .site-nav a::after { display: none; }
/* Winkel kommen seit 2026-08-02 INLINE aus header.php (--ang = 360/n * i): feste nth-child-Winkel
   in 60-Grad-Schritten ergaben bei 4 Nav-Punkten einen rechtslastigen Halbkreis statt eines
   Kreises (gemessen navproof nd-consulting@1366), und Punkt 7+ fiel auf 0deg zurueck und
   ueberlappte Punkt 1. Der Radius skaliert mit dem Viewport statt fix 9rem zu kleben. */
.site-header[data-nav="circular"] .site-nav { --nav-r: clamp(7rem, 24vmin, 13rem); }
/* OFFEN-Zustand NUR fuer circular. Bis 2026-08-02 war dieser Selektor ungescoped und traf JEDES
   geoeffnete Menue jeder Site: ohne --ang schob er jeden Link um die halbe Eigenbreite nach links
   und Radius+halbe Hoehe nach oben — das Mobile-Menue von "classic" lag komplett ausserhalb des
   Viewports (gemessen navproof rsm-swissexpress@390: matrix(1,0,0,1,-153,-168)). */
.site-header[data-nav="circular"] .nav-toggle:checked ~ .site-nav a { transform: translate(-50%, -50%) rotate(var(--ang, 0deg)) translateY(calc(-1 * var(--nav-r, 9rem))) rotate(calc(-1 * var(--ang, 0deg))) scale(1); opacity: 1; }

/* open state (drawers + overlay + circular): reveal + slide/fade in */
.nav-toggle:checked ~ .site-nav[data-panel] { visibility: visible; transform: translateX(0); opacity: 1; }
.site-header[data-nav="hamburger"] .nav-toggle:checked ~ .site-nav { opacity: 1; visibility: visible; transform: none; }
/* scrim (dim + click-to-close) for drawers/overlay/circular */
.site-header[data-nav="sidebar-left"] .nav-scrim,
.site-header[data-nav="slidein-right"] .nav-scrim,
.site-header[data-nav="overlay"] .nav-scrim,
.site-header[data-nav="circular"] .nav-scrim { display: block; position: fixed; inset: 0; z-index: 55;
  background: rgba(10,12,20,.42); opacity: 0; visibility: hidden; transition: opacity .28s ease, visibility .28s; cursor: pointer; }
.nav-toggle:checked ~ .nav-scrim { opacity: 1; visibility: visible; }
/* close button inside panels */
.site-nav[data-panel] .nav-close { display: inline-flex; align-items: center; justify-content: center;
  position: absolute; top: 1rem; right: 1.2rem; width: 44px; height: 44px; font-size: 2rem; line-height: 1;
  color: var(--ink); cursor: pointer; border-radius: 10px; z-index: 2; }
/* burger -> X while open (panel layouts, all viewports) */
.site-header[data-nav="hamburger"] .nav-toggle:checked ~ .nav-burger span,
.site-header[data-nav="overlay"] .nav-toggle:checked ~ .nav-burger span,
.site-header[data-nav="sidebar-left"] .nav-toggle:checked ~ .nav-burger span,
.site-header[data-nav="slidein-right"] .nav-toggle:checked ~ .nav-burger span,
.site-header[data-nav="circular"] .nav-toggle:checked ~ .nav-burger span { background: transparent; }
.site-header[data-nav="hamburger"] .nav-toggle:checked ~ .nav-burger span::before,
.site-header[data-nav="overlay"] .nav-toggle:checked ~ .nav-burger span::before,
.site-header[data-nav="sidebar-left"] .nav-toggle:checked ~ .nav-burger span::before,
.site-header[data-nav="slidein-right"] .nav-toggle:checked ~ .nav-burger span::before,
.site-header[data-nav="circular"] .nav-toggle:checked ~ .nav-burger span::before { transform: translateY(7px) rotate(45deg); }
.site-header[data-nav="hamburger"] .nav-toggle:checked ~ .nav-burger span::after,
.site-header[data-nav="overlay"] .nav-toggle:checked ~ .nav-burger span::after,
.site-header[data-nav="sidebar-left"] .nav-toggle:checked ~ .nav-burger span::after,
.site-header[data-nav="slidein-right"] .nav-toggle:checked ~ .nav-burger span::after,
.site-header[data-nav="circular"] .nav-toggle:checked ~ .nav-burger span::after { transform: translateY(-7px) rotate(-45deg); }

/* ---- hybrid: a few inline links + burger reveals the full list ---- */
@media (min-width: 40.01rem) {
  .site-header[data-nav="hybrid"] .nav-burger { display: inline-flex; align-items: center; justify-content: center;
    width: 44px; height: 44px; cursor: pointer; border-radius: 12px; color: var(--ink); order: 8;
    background: color-mix(in oklch, currentColor 8%, transparent); }
  .site-header[data-nav="hybrid"] .site-nav > a:nth-of-type(n+4),
  .site-header[data-nav="hybrid"] .site-nav > .nav-group:nth-of-type(n+4) { display: none; }
  .site-header[data-nav="hybrid"] .nav-toggle:checked ~ .site-nav { position: absolute; top: calc(100% + .4rem);
    right: max(1rem, calc((100% - var(--maxw)) / 2)); flex-direction: column; width: min(78vw, 260px); padding: .6rem;
    background: color-mix(in oklch, var(--paper) 14%, white); border: 1px solid color-mix(in oklch, var(--line) 85%, transparent);
    border-radius: 16px; box-shadow: 0 24px 50px -28px rgba(18,20,38,.5); z-index: 60; }
  .site-header[data-nav="hybrid"] .nav-toggle:checked ~ .site-nav > a,
  .site-header[data-nav="hybrid"] .nav-toggle:checked ~ .site-nav > .nav-group { display: flex; }
}

/* ---- split: two nav halves flanking a centred brand ---- */
@media (min-width: 40.01rem) {
  .site-header[data-nav="split"] .header-inner { justify-content: center; }
  .site-header[data-nav="split"] .nav-side { flex: 1 1 0; display: flex; margin: 0; gap: 1.7rem; }
  .site-header[data-nav="split"] .nav-left { justify-content: flex-end; order: 1; }
  .site-header[data-nav="split"] .brand { order: 2; margin-inline: clamp(1.5rem, 4vw, 3.5rem); flex: 0 0 auto; }
  .site-header[data-nav="split"] .nav-right { justify-content: flex-start; order: 3; }
  .site-header[data-nav="split"] .nav-burger { display: none; }
}
@media (max-width: 40rem) {
  .site-header[data-nav="split"] .nav-side { display: none; flex-basis: 100%; flex-direction: column; }
  .site-header[data-nav="split"] .nav-toggle:checked ~ .nav-side { display: flex; }
  .site-header[data-nav="split"] .nav-burger { display: inline-flex; }
}

/* ---- onepager: centred anchor bar ---- */
@media (min-width: 40.01rem) {
  .site-header[data-nav="onepager"] .site-nav { margin-inline: auto; gap: 2rem; }
}

/* ---- sticky-over: force the transparent-over-hero treatment regardless of skin ---- */
.site-header[data-nav="sticky-over"] { position: absolute; inset: 0 0 auto 0; background: transparent; border-bottom: 0;
  -webkit-backdrop-filter: none; backdrop-filter: none; }
.site-header[data-nav="sticky-over"] .header-inner { background: color-mix(in oklch, black 20%, transparent);
  -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px); border: 1px solid color-mix(in oklch, white 22%, transparent);
  border-radius: 999px; padding: .5rem .8rem .5rem 1.5rem; box-shadow: 0 18px 40px -24px rgba(0,0,0,.5); }
.site-header[data-nav="sticky-over"] .brand,
.site-header[data-nav="sticky-over"] .nav-burger { color: #fff; }
.site-header[data-nav="sticky-over"] .site-nav a { color: rgba(255,255,255,.84); }
.site-header[data-nav="sticky-over"] .site-nav a:hover,
.site-header[data-nav="sticky-over"] .site-nav a[aria-current="page"] { color: #fff; }

/* body scroll-lock when a panel is open (progressive enhancement adds .nav-open via nav.js;
   CSS-only fallback still works, just without the lock). */
body.nav-open { overflow: hidden; }

/* 'static' background mode (wizard): a calm, NON-animated depth glow (no canvas), premium + quiet */
body[data-bg="static"] .block-hero {
  background:
    radial-gradient(58rem 42rem at 50% -12%, color-mix(in oklch, var(--accent-soft) 13%, transparent), transparent 60%),
    var(--paper);
}

/* ---------- Full-bleed photo hero (bgType=image): image fills the hero, dark scrim, light copy ---------- */
.block-hero.hero-bgimage { position: relative; overflow: clip; color: #fff; isolation: isolate; background-color: #0a0c14; }
.hero-bgimage .hero-bg-media { position: absolute; inset: 0; z-index: -2; display: block; }
.hero-bgimage .hero-bg-media img { width: 100%; height: 100%; object-fit: cover; border-radius: 0; box-shadow: none; }
.hero-bgimage::after { content: ""; position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(180deg, rgba(8,10,18,.58) 0%, rgba(8,10,18,.30) 38%, rgba(8,10,18,.66) 100%); }
.hero-bgimage .hero-copy { max-width: 780px; padding-top: clamp(4.5rem, 10vw, 7.5rem); }
.hero-bgimage .hero-title { color: #fff; }
.hero-bgimage .eyebrow { color: rgba(255,255,255,.86); }
.hero-bgimage .hero-sub { color: rgba(255,255,255,.9); max-width: 46ch; }

/* ============================================================
   SECTION RHYTHM + HONEST PAPER — Optik-Track C (2026-06-20)
   Premium vertical cadence on EVERY page below the hero, via neutral
   tonal bands + hairlines (NO accent on content sections — anti-slop).
   Tier class injected by SiteRenderer::renderBlock from block order.
   Appended LAST so it wins over the 977 grey-canvas + the hero washes.
   ============================================================ */
/* Raised-band tone: derived from paper+ink, palette-aware. Light: pull paper toward ink
   (recede). Noir: pull toward the LIGHT ink so the band LIFTS off the dark page. */
/* Light palettes: the 977 canvas already pulls the PAGE ~7% toward ink (grey), so the raised
   band lifts by being the brighter paper-white panel (the card-pop move) — clearly visible,
   never a muddy near-grey. Noir lifts the other way (toward the light ink). */
:root                 { --band: var(--paper);
  /* shared filmic grain (fractal noise) for the ink tier — keeps a dark band from reading as flat paint */
  --grain-tex: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='gt'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23gt)'/%3E%3C/svg%3E"); }
[data-palette="noir"] { --band: color-mix(in oklch, var(--paper) 90%, var(--ink)); }
/* Global depth dial (theme.rhythm -> data-rhythm). 'soft' is the default. */
body[data-rhythm="strong"]                      { --band: color-mix(in oklch, var(--paper) 60%, white); }
body[data-rhythm="strong"][data-palette="noir"] { --band: color-mix(in oklch, var(--paper) 84%, var(--ink)); }
body[data-rhythm="off"]                         { --band: var(--paper); }

/* Bands get symmetric padding so content breathes inside the coloured stripe (overriding the
   global `.block + .block{padding-block-start:0}`); the section right after a band gets its top
   padding back too, so nothing touches a hairline. */
.block.tier-raised, .block.tier-ink { padding-block: var(--section); }
.block.tier-raised + .block, .block.tier-ink + .block { padding-block-start: var(--section); }

/* Raised band: full-bleed neutral stripe with hairline edges + a faint ink dot-grid. The dot texture
   also makes the band VISIBLE on near-white (custom-paper) sites, where the tonal lift alone is a
   whisper. Paper/ink-derived (never accent), in the background behind content. */
.block.tier-raised {
  background-color: var(--band);
  background-image: radial-gradient(color-mix(in oklch, var(--ink) 7%, transparent) 1px, transparent 1.7px);
  background-size: 22px 22px;
  border-block: 1px solid color-mix(in oklch, var(--line) 60%, transparent);
}
.block.tier-raised + .block.tier-raised { border-top: 0; }   /* collapse a shared hairline */
/* ONE editorial accent band per page (tagged once by SiteRenderer): a deliberate brand moment — a
   faint accent wash + accent hairlines + accent dots. Text stays --ink, so contrast equals the body
   baseline (WCAG-safe); the single sanctioned accent on a content section. */
.block.tier-raised.tier-accent {
  background-color: color-mix(in oklch, var(--band) 88%, var(--accent));
  background-image: radial-gradient(color-mix(in oklch, var(--accent) 14%, transparent) 1px, transparent 1.7px);
  border-block: 1px solid color-mix(in oklch, var(--accent) 32%, var(--line));
}

/* Once-per-page NEUTRAL ink heavy beat (statement). Reuses the proven dark inversion,
   full-bleed. NEUTRAL ink — never accent. Does NOT touch the real CTA block. */
.block.tier-ink {
  background-color: var(--ink);
  background-image: var(--grain-tex), radial-gradient(color-mix(in oklch, var(--paper) 6%, transparent) 1px, transparent 1.7px);
  background-size: 160px 160px, 26px 26px;
  background-blend-mode: soft-light, normal;   /* grain whispers over the ink; dots add a faint grid */
  color: color-mix(in oklch, var(--paper) 86%, var(--ink));
  border-block: 1px solid color-mix(in oklch, var(--ink) 82%, var(--line));
}
.block.tier-ink :is(h2, h3, .statement-quote) { color: var(--paper); }
.block.tier-ink .statement-by     { color: color-mix(in oklch, var(--paper) 72%, transparent); }
.block.tier-ink .statement-author { color: var(--paper); }
body[data-palette="noir"] .block.tier-ink {                 /* dark page: flip to a lighter panel */
  background: color-mix(in oklch, var(--paper) 14%, var(--surface));
  color: var(--ink); border-color: var(--line);
}
body[data-palette="noir"] .block.tier-ink :is(h2,h3,.statement-quote) { color: var(--ink); }

/* OFF dial: no band paint, no hairlines, original single-gap rhythm -> a literally flat page. */
body[data-rhythm="off"] .block.tier-raised { background: var(--paper); border-block: 0; }
body[data-rhythm="off"] .block.tier-ink { background: var(--paper); color: var(--ink); border-block: 0; }
body[data-rhythm="off"] .block.tier-ink :is(h2,h3,.statement-quote) { color: var(--ink); }
body[data-rhythm="off"] .block.tier-raised, body[data-rhythm="off"] .block.tier-ink,
body[data-rhythm="off"] .block.tier-raised + .block, body[data-rhythm="off"] .block.tier-ink + .block { padding-block-start: 0; }

/* ============================================================
   EXPLICIT-PAPER GUARD — honour a chosen colour EXACTLY (incl. #ffffff).
   layout.php stamps data-paper="custom" when theme.paper is set. Bypass the deliberate grey
   canvas, reduce the band tint to a whisper, suppress the hero accent washes, de-tint the
   trust pills. Palette-only sites (flag absent) stay byte-identical to today.

   Wiederhergestellt 2026-08-01: die acht Regeln waren in 54bd07b geloescht worden, der
   Kommentarkopf blieb stehen und verschmolz mit dem B3-Kopf darunter. Folge: data-paper wurde
   weiter gestempelt, aber von niemandem gelesen — eine gewaehlte Papierfarbe #ffffff wurde als
   232,232,232 gerendert (gemessen auf e-gold-ch, primeweb-ch, sen-knife-com).
   ============================================================ */
body[data-paper="custom"]:not([data-palette="noir"]) { background: var(--paper); }
body[data-paper="custom"]                       { --band: var(--paper); }
body[data-paper="custom"][data-rhythm="strong"] { --band: color-mix(in oklch, var(--paper) 95%, var(--ink)); }
body[data-paper="custom"][data-rhythm="off"]    { --band: var(--paper); }
body[data-paper="custom"] .block-hero { background-color: var(--paper); }
body[data-paper="custom"][data-bg="mesh"]   .block-hero,
body[data-paper="custom"][data-bg="static"] .block-hero { background: var(--paper); }
body[data-paper="custom"] .trust-badges li { background: color-mix(in oklch, var(--paper) 94%, var(--ink)); }

/* ============================================================
   B3 — per-industry DESIGN MOODS (render-time, body-level token overrides).
   Orthogonal to palette/fonts: re-textures rhythm, corner radius, shadow depth and
   display scale so two industries FEEL different, not just differently coloured.
   Mood is chosen in layout.php (pwb_mood) from the industry. ============================ */
body[data-mood="editorial"] {            /* law / finance / medical — refined, sharp, generous whitespace */
  --radius: 5px;
  --section: clamp(3.5rem, 7vh, 6.5rem);
  --maxw: 1160px;
  --shadow-media: 0 0 0 1px var(--ring), 0 2px 10px -6px oklch(20% 0.02 260 / .16), 0 40px 72px -54px oklch(15% 0.02 260 / .30);
  --shadow-card:  0 1px 2px -1px oklch(20% 0.02 260 / .12), 0 8px 22px -18px oklch(16% 0.02 260 / .22);
}
body[data-mood="soft"] {                 /* beauty / wellness / gastro — rounded, warm, dreamy depth */
  --radius: 24px;
  --section: clamp(3rem, 6vh, 5.5rem);
  --shadow-media: 0 0 0 1px var(--ring), 0 8px 26px -10px oklch(45% 0.04 25 / .22), 0 44px 84px -42px oklch(38% 0.04 25 / .30);
  --shadow-card:  0 2px 8px -2px oklch(45% 0.03 25 / .16), 0 20px 44px -24px oklch(38% 0.03 25 / .28);
}
body[data-mood="grounded"] {             /* trades / handwerk — solid, sturdy, defined edges */
  --radius: 9px;
  --section: clamp(3rem, 6vh, 5.5rem);
  --shadow-media: 0 0 0 1px var(--ring), 0 3px 9px -3px oklch(28% 0.02 70 / .26), 0 30px 56px -34px oklch(18% 0.02 70 / .38);
}
body[data-mood="modern"] {               /* tech / agency / default — crisp, snappy, bigger display */
  --radius: 13px;
  --section: clamp(2.75rem, 5.5vh, 5rem);
  --t-display: clamp(2.5rem, 1.5rem + 5vw, 6rem);
}

/* ============================================================
   B4 — premium DEPTH in cards & sections, flavoured by the B3 mood (render-time, every site).
   Complements the existing polish (bento tilt/hover, statement watermark, text drop-cap) — does not
   duplicate it. ============================================================ */

/* editorial: precision depth — a crisp accent top-rule on service cards + a drop-cap on the service copy */
body[data-mood="editorial"] .bento-card { border-top: 2px solid color-mix(in oklch, var(--accent) 58%, var(--line)); }
body[data-mood="editorial"] .it-body > p:first-of-type::first-letter {
  float: left; font-family: var(--font-display); font-weight: 700; font-size: 3.1em; line-height: .72;
  margin: .05em .14em 0 0; color: var(--accent-deep);
}

/* soft: warm, tinted card surfaces for an inviting, dreamy depth */
body[data-mood="soft"] .bento-card  { background: color-mix(in oklch, var(--paper) 93%, var(--accent-soft)); }
body[data-mood="soft"] .feature-card { background: color-mix(in oklch, var(--paper) 95%, var(--accent-soft)); }

/* grounded: solid, defined edges */
body[data-mood="grounded"] .bento-card { border-width: 1.6px; }

/* CTA — premium gradient depth (ink -> a whisper of brand accent), never a flat block, on every site */
.block-cta .block-inner { background: linear-gradient(135deg, var(--ink) 0%, color-mix(in oklch, var(--ink) 86%, var(--accent)) 100%); position: relative; overflow: clip; }

/* ============================================================================
   SX — PREMIUM SECTION SYSTEM (P1 rebuild)
   Fewer, stronger, image-bearing sections. Each `.sx` owns a full-bleed surface:
   a tragendes Hintergrundbild (.sx-media) under a legibility scrim (.sx-scrim),
   with optional per-section motion (.bg-canvas from backgrounds.js) between them.
   Decoupled from the legacy `.block` rhythm (different root class) — legacy sites
   are unaffected. Tokens reused: --ink, --paper, --accent-*, --font-display, --maxw, --section.
   ============================================================================ */
.sx {
  position: relative; isolation: isolate; overflow: clip;
  display: grid; align-content: start;
  /* P5 — DER RHYTHMUS KOMMT AUS DEM INHALT, NICHT AUS DER FENSTERHOEHE.
     Bis 2026-08-03 stand hier `min-height:100svh` fuer JEDE Sektion. Gemessen an
     rsm-swissexpress-gmbh-aaa: sieben von neun Bloecken exakt 900px hoch, einer davon mit
     95 Zeichen Inhalt. Das war nicht eine Regel mit Nebenwirkungen — es war die Wurzel:
     weil eine Sektion nicht wachsen DURFTE, musste danach alles andere schrumpfen
     (Kartenbild auf 15vh, Kartentext auf 130 Zeichen, jeder Absatz auf 40 Woerter im
     Prompt). Die Handfassung derselben Seite spannt 120px bis 3400px in EINER Seite.
     Die volle Bildschirmhoehe ist jetzt eine Eigenschaft, die eine Sektion ANFORDERT
     (sx-size-cover / -tall), keine, die ihr aufgezwungen wird.
     padding-block ist wieder breitengetrieben: auf einem kurzen Laptop soll die Sektion
     nicht enger atmen, sie soll einfach so hoch sein wie ihr Inhalt. */
  padding-block: clamp(3.5rem, 2rem + 6vw, 7rem);
  background: var(--paper); color: var(--ink);
}
.sx-inner {
  position: relative; z-index: 3;
  width: min(100% - calc(var(--gutter) * 2), var(--maxw)); margin-inline: auto;
}
.sx-inner--narrow { width: min(100% - calc(var(--gutter) * 2), 820px); }

/* layers: media (0) -> scrim (1) -> motion canvas (2) -> content (3) */
.sx-media { position: absolute; inset: 0; z-index: 0; }
.sx-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.sx-scrim { position: absolute; inset: 0; z-index: 1; pointer-events: none; }
.sx > .bg-canvas { z-index: 2; }
.sx:not(.sx-has-media) .sx-scrim { display: none; }

/* S2c — free-standing library motif (a cut-out Spline still). A LAYER, not content:
   position:absolute keeps it out of the flow, so it can never add a pixel to the section height
   (P4c screen budget). z-index 2 puts it above the scrim (it must not be veiled) and below
   .sx-inner. Sized in vh as well as vw so it shrinks with everything else on a short laptop
   instead of fighting the one-screen rule.
   Deliberately NOT animated: the site-wide motion wish (theme.motionLevel, S1) has no CSS hook, so a
   global float would also run on a site that explicitly asked for calm. A baked Spline still is a
   still. */
.sx-ornament { position: absolute; z-index: 2; pointer-events: none; user-select: none; }
.sx-ornament img { display: block; width: 100%; height: auto; }

@media (min-width: 55rem) {
  .sx-ornament { top: 50%; right: clamp(1rem, 4vw, 4.5rem); translate: 0 -50%; width: min(32vw, 44vh); }
  /* the text column yields room instead of the object overlapping it — rem, never ch (a ch cap on
     an element whose font differs from the container is the P4c bug this repo already paid for) */
  .sx-has-ornament .sx-eyebrow,
  .sx-has-ornament .sx-title,
  .sx-has-ornament .sx-lead { max-width: min(100%, 36rem); }
}
@media (max-width: 55rem) {
  /* Phones: ABOVE the content, not below it. Measured on farbwerk-amrein at 390x844 — anchored to the
     section's BOTTOM the object fell 45px past the fold, because a pill header sits in the flow and
     pushes a 100svh section down by exactly that much. The area above the eyebrow is empty on every
     mobile hero (the content is grid-centred), so the object is visible there without any assumption
     about the header. Still absolute -> still 0px of section height. */
  .sx-ornament { left: 50%; top: clamp(1rem, 5vh, 3.5rem); bottom: auto; translate: -50% 0;
    width: min(46vw, 17vh); }
}

/* vertical scale — VIER Groessen, und nur die oberen zwei fordern Bildschirmhoehe an.
   `cover`  Auftritt: der Hero besitzt den Bildschirm, Inhalt mittig.
   `tall`   Praesenz: darf den Bildschirm fuellen, muss aber nicht — waechst mit dem Inhalt.
   `band`   Fluss: so hoch wie noetig. Der Normalfall (feature, index, story).
   `strip`  Streifen: Zahlen-/Vertrauensbaender, bewusst gedrungen (Referenz: 71px hoch).
   align-content folgt derselben Logik: zentriert wird nur, was eine Buehne ist. Ein
   Fliess-Abschnitt setzt oben an, sonst entsteht beim Scrollen wieder Kachel-Optik. */
.sx-size-cover { min-height: 100svh; align-content: center; --sx-pt: clamp(6rem, 12vh, 8rem);
                 padding-block: var(--sx-pt) clamp(3rem, 6vh, 5rem); }
.sx-size-tall  { min-height: 62svh;  align-content: center; --sx-pt: clamp(4rem, 2rem + 6vw, 7rem);
                 padding-block: var(--sx-pt) clamp(4rem, 2rem + 6vw, 7rem); }
.sx-size-band  { --sx-pt: clamp(3.5rem, 2rem + 6vw, 7rem);
                 padding-block: var(--sx-pt) clamp(3.5rem, 2rem + 6vw, 7rem); }
.sx-size-strip { align-content: center; --sx-pt: clamp(1.4rem, .8rem + 2vw, 2.6rem);
                 padding-block: var(--sx-pt) clamp(1.4rem, .8rem + 2vw, 2.6rem); }

/* STRUKTUR-GARANTIE Z1 (2026-08-31) — der Vertrag, den es nie gab: drei Regeln nehmen den
   Header aus dem Layoutfluss (data-style="over" / data-overlay / data-nav="sticky-over");
   die erste Sektion darunter haelt seine REALE Hoehe frei, nicht eine geratene Konstante.
   Vorher stand hier woertlich «extra top clears the position:absolute site-header» an einem
   clamp — gemessen sprang der Header mit Logo auf 116 px, die Reserve blieb bei 96 px (15 px
   Titel hinter der Navigation, 72 Bestandsseiten). --header-h setzt das Inline-Skript in
   partials/header.php (ResizeObserver, nur im Ruhezustand gemessen); ohne JS greift der
   Fallback 6rem + 1rem Luft. max() nimmt die groessere der beiden Reserven; die Spezifitaet
   (0,4,1) schlaegt bewusst auch den mobilen cover-clamp weiter unten. .block deckt die
   Legacy-Sites ab, deren Owner-gewaehlter 'over'-Skin ebenfalls absolut sitzt. */
.site-header[data-style="over"] ~ main > :is(.sx, .block):first-child,
.site-header[data-overlay="1"] ~ main > :is(.sx, .block):first-child,
.site-header[data-nav="sticky-over"] ~ main > :is(.sx, .block):first-child {
  padding-top: max(var(--sx-pt, 6rem), calc(var(--header-h, 6rem) + 1rem));
}

/* alignment */
.sx-align-center { text-align: center; }
.sx-align-center .sx-lead, .sx-align-center .sx-copy { margin-inline: auto; }

/* ---- tones: text colour, solid surface, and the scrim veil over a photo -------------------
   The scrim always carries a FLAT floor so white/dark body text clears WCAG 4.5:1 over ANY
   photo (worst case: a fully white/black image) — a pure directional gradient could not. The
   directional layer on top only ADDS depth, never lightens below the floor. */
.sx-tone-paper  { background: var(--paper); color: var(--ink); }
.sx-tone-light  { background: color-mix(in oklab, var(--paper) 88%, var(--ink)); color: var(--ink); }
.sx-tone-dark   { background: #0b0c10; color: #fff; --ink: #fff; --muted: rgba(255,255,255,.82); }
.sx-tone-accent { background: var(--accent-deep); color: var(--accent-ink);
                  --ink: var(--accent-ink); --muted: color-mix(in oklab, var(--accent-ink) 80%, var(--accent-deep)); }

.sx-tone-dark.sx-has-media .sx-scrim {
  background:
    linear-gradient(90deg, rgba(8,9,12,.55), rgba(8,9,12,0) 68%),
    linear-gradient(0deg,  rgba(8,9,12,.72), rgba(8,9,12,.72));
}
.sx-align-center.sx-tone-dark.sx-has-media .sx-scrim {
  background:
    radial-gradient(120% 90% at 50% 50%, rgba(8,9,12,.45), rgba(8,9,12,0) 70%),
    linear-gradient(0deg, rgba(8,9,12,.72), rgba(8,9,12,.72));
}
/* GERICHTETER SCHLEIER STATT VOLLFLAECHEN-WAESCHE (21.08.2026).
   Bis dahin lag ueber hellen Foto-Sektionen ein Schleier auf der GANZEN Flaeche (light: 80 %
   Weiss + 60 %-Verlauf, paper: 50 % + 60 %) — Lesbarkeit wurde durch Weisswaschen des ganzen
   Fotos erkauft. Genau das haben zwei echte Kunden benannt: «ALLE BILDER NICHT KLAR, DA SIE
   EINE VORHANG HABEN — DIESEN ENTFERNEN» (allgemeinmedizin, 10.08.) und der Owner («blasser
   Vordergrund, alles andere als premium», 20.08.). Gemessen: 61 Foto-Sektionen auf 14 Sites.
   Jetzt schuetzt der Schleier die TEXTZONE — dort sogar staerker als vorher (.94 statt .8) —
   und laesst das Foto auf der freien Seite ungewaschen stehen. WCAG-Garantie bleibt damit
   erhalten, das Bild wird wieder Bild. */
.sx-tone-light.sx-has-media .sx-scrim {
  background:
    linear-gradient(90deg, rgba(255,255,255,.94) 0%, rgba(255,255,255,.86) 42%,
                           rgba(255,255,255,.55) 62%, rgba(255,255,255,0) 82%);
}
.sx-align-center.sx-tone-light.sx-has-media .sx-scrim {
  background:
    radial-gradient(120% 100% at 50% 55%, rgba(255,255,255,.92) 0%,
                    rgba(255,255,255,.6) 52%, rgba(255,255,255,0) 78%);
}
/* paper + Vollflaechenmedium: seit 2026-08-02 mit Schleier (vorher fehlte er als einzigem Ton
   und dunkler Text lag nackt auf dem Foto — 84 % der Buchstabenflaeche unter dem Minimum bei
   390 px). In der Papierfarbe der Palette statt in Weiss, damit eine warme Palette keinen
   kalten Fleck bekommt; seit 21.08. gerichtet statt vollflaechig (siehe oben). */
.sx-tone-paper.sx-has-media .sx-scrim {
  background:
    linear-gradient(90deg, color-mix(in oklab, var(--paper) 94%, transparent) 0%,
                           color-mix(in oklab, var(--paper) 86%, transparent) 42%,
                           color-mix(in oklab, var(--paper) 55%, transparent) 62%,
                           transparent 82%);
}
.sx-align-center.sx-tone-paper.sx-has-media .sx-scrim {
  background:
    radial-gradient(120% 100% at 50% 55%, color-mix(in oklab, var(--paper) 92%, transparent) 0%,
                    color-mix(in oklab, var(--paper) 60%, transparent) 52%, transparent 78%);
}
/* Schmale Viewports: der Text laeuft ueber die volle Breite — der seitliche Verlauf liesse ihn
   rechts ungeschuetzt auf dem Foto stehen. Vertikal statt horizontal: oben (Textzone) dicht,
   unten bleibt das Foto sichtbar. */
@media (max-width: 760px) {
  .sx-tone-light.sx-has-media .sx-scrim,
  .sx-align-center.sx-tone-light.sx-has-media .sx-scrim {
    background: linear-gradient(180deg, rgba(255,255,255,.93) 0%, rgba(255,255,255,.85) 55%,
                                rgba(255,255,255,.4) 100%);
  }
  .sx-tone-paper.sx-has-media .sx-scrim,
  .sx-align-center.sx-tone-paper.sx-has-media .sx-scrim {
    background: linear-gradient(180deg, color-mix(in oklab, var(--paper) 93%, transparent) 0%,
                                color-mix(in oklab, var(--paper) 85%, transparent) 55%,
                                color-mix(in oklab, var(--paper) 40%, transparent) 100%);
  }
}
.sx-tone-accent.sx-has-media .sx-scrim {
  background: color-mix(in oklab, var(--accent-deep) 88%, transparent);
}

/* Ein Cover ohne Medium ist eine GESTALTETE Flaeche, nie eine nackte (2026-08-02).
   Befund atelier-lindenhof: bgType "solid" + tone dark + kein Bild/Effekt/Ornament ergab eine
   #0b0c10-Vollwand als ersten Bildschirm — auf der Seite eines Grafikdesign-Studios. "Einfarbig,
   maximale Ruhe" heisst ruhig gestaltet, nicht leer. Darum bekommt jedes medienlose Cover (und
   jedes Schluss-Finale) Tiefe aus der eigenen Markenfarbe: ein grosser weicher Akzent-Schein
   oben links, ein schwaecherer Gegenpol unten rechts, darunter ein leiser Vertikalverlauf.
   Liegt doch ein Medium, eine Illustration oder ein Effekt darueber, stoert die Basis nicht —
   sie ist dann schlicht der bessere Untergrund. */
.sx-cover.sx-tone-dark:not(.sx-has-media),
.sx-finale.sx-tone-dark:not(.sx-has-media) {
  background:
    radial-gradient(90rem 62rem at 12% -12%, color-mix(in oklab, var(--accent-deep) 38%, transparent), transparent 62%),
    radial-gradient(74rem 52rem at 106% 118%, color-mix(in oklab, var(--accent) 16%, transparent), transparent 56%),
    linear-gradient(180deg, #12141b, #0b0c10 58%);
}
.sx-cover.sx-tone-accent:not(.sx-has-media),
.sx-finale.sx-tone-accent:not(.sx-has-media) {
  background:
    radial-gradient(90rem 62rem at 12% -12%, color-mix(in oklab, var(--accent-soft, #fff) 20%, transparent), transparent 62%),
    radial-gradient(74rem 52rem at 106% 118%, color-mix(in oklab, black 26%, transparent), transparent 56%),
    var(--accent-deep);
}
/* bgMode "gradient": der GEWAEHLTE Markenverlauf ist die Gestaltung. Die Pflicht-Regeln oben
   (Spezifitaet 0-3-0) hatten .sx-bg-gradient (0-1-0) kaskadisch ueberschrieben — uebrig blieb
   eine flache accent-deep-Wand, "Farbverlauf" war visuell identisch mit "Einfarbig" (gemessen
   navproof nd-consulting: computed background OHNE linear-gradient). Hier gewinnt der Verlauf
   (0-4-0) und behaelt die Tiefe der Pflicht-Regeln als Schein-Layer darueber. Lehre: eine
   bg-Flaeche auf dark/accent-Covern braucht IMMER eine Kombi-Regel gegen die Gestaltungspflicht. */
.sx-cover.sx-tone-accent.sx-bg-gradient:not(.sx-has-media),
.sx-finale.sx-tone-accent.sx-bg-gradient:not(.sx-has-media) {
  background:
    radial-gradient(90rem 62rem at 12% -12%, color-mix(in oklab, var(--accent-soft, #fff) 24%, transparent), transparent 62%),
    radial-gradient(74rem 52rem at 106% 118%, color-mix(in oklab, black 30%, transparent), transparent 56%),
    linear-gradient(150deg, var(--accent-deep) 0%, var(--accent) 52%,
      color-mix(in oklab, var(--accent) 42%, var(--accent-soft)) 100%);
}
.sx-cover.sx-tone-dark.sx-bg-gradient:not(.sx-has-media),
.sx-finale.sx-tone-dark.sx-bg-gradient:not(.sx-has-media) {
  background:
    radial-gradient(90rem 62rem at 12% -12%, color-mix(in oklab, var(--accent-deep) 42%, transparent), transparent 62%),
    linear-gradient(150deg, #171922 0%, #101218 46%, color-mix(in oklab, var(--accent-deep) 55%, #0b0c10) 100%);
}

/* ---- typography inside sx --------------------------------------------------- */
.sx-eyebrow {
  text-transform: uppercase; letter-spacing: .18em; font-size: .76rem; font-weight: 650;
  color: var(--accent); margin: 0 0 1.1rem;
  display: flex; align-items: center; gap: .6rem;
}
/* Der Balken ist der sichtbare Traeger der ZWEITEN Markenfarbe (--accent-2, gesetzt in
   partials/layout.php). Er faerbt keinen Text ein: bliebe die zweite Farbe kontrastschwach,
   waere hier nur ein Strich blass — kein unlesbares Wort. */
.sx-eyebrow::before {
  content: ""; flex: none; width: 1.6rem; height: 2px; border-radius: 2px;
  background: var(--accent-2, currentColor);
}
.sx-eyebrow:empty { display: none; }
.sx-tone-dark .sx-eyebrow::before, .sx-tone-accent .sx-eyebrow::before { background: currentColor; opacity: .7; }
/* Mittig gesetzte Augenbrauen (cover/finale) zentrieren mitsamt Balken.
   Nur .sx-align-center — ein zweiter, ausgedachter Klassenname stand hier zuerst mit drin und
   haette nie gegriffen. */
.sx-align-center .sx-eyebrow { justify-content: center; }
.sx-tone-dark .sx-eyebrow, .sx-tone-accent .sx-eyebrow { color: currentColor; opacity: .82; }
/* P5 — die Schriftgroesse haengt wieder an der BREITE, nicht an der Fensterhoehe.
   Der vh-Deckel (min(--t-3xl, 7.4vh)) war eine Notbremse gegen den 100svh-Zwang: eine
   Ueberschrift durfte die Sektion nicht sprengen, also wurde sie auf kurzen Laptops
   kleiner. Jetzt darf die Sektion wachsen — der Deckel gilt nur noch dort, wo die
   Bildschirmhoehe wirklich bindend ist (cover, tall). */
.sx-title {
  font-family: var(--font-display); font-weight: 760; font-size: var(--t-3xl);
  line-height: 1.03; letter-spacing: -.03em; margin: 0 0 1.1rem;
  text-wrap: balance; overflow-wrap: break-word; hyphens: auto;
}
.sx-size-tall  .sx-title { font-size: min(var(--t-3xl), 8.6vh); }
.sx-size-cover .sx-title { font-size: min(var(--t-display), 12vh); line-height: 1.0; letter-spacing: -.038em; }
.sx-lead { font-size: var(--t-lg); line-height: 1.5; max-width: 48ch; color: var(--muted); margin: 0 0 1.6rem; }
.sx-copy { font-size: var(--t-base); line-height: 1.7; max-width: 62ch; color: var(--muted); }
.sx-copy > p { margin: 0 0 1rem; } .sx-copy > p:last-child { margin-bottom: 0; }
.sx-cta { margin-top: .4rem; }
/* on an image-bearing accent/dark section the primary button must not vanish -> a light chip */
.sx-tone-dark .btn-primary { background: #fff; color: #0b0c10; }
.sx-tone-accent .btn-primary { background: var(--accent-ink); color: var(--accent-deep); }

/* reveal-on-scroll hook (wired in reveal.js at P1c): hidden ONLY while JS is active + not yet
   in view, so no-JS / reduced-motion always shows content. Motion respects the global guard. */
html.reveal-on .sx:not(.in-view) .sx-inner { opacity: 0; transform: translateY(16px); }
.sx-inner { transition: opacity .52s cubic-bezier(.2,.75,.25,1), transform .52s cubic-bezier(.2,.75,.25,1); }

/* P5/E3 — DER HERO WIRD NIE VERSTECKT.
   Fuer den Legacy-Zweig existiert dieser Schutz seit je (app.css oben, `.reveal-on main >
   .block:first-child`, Kommentar «Hero is above the fold — never hide it»). Beim Umbau auf
   `.sx` wurde er nie nachgezogen: reveal.js beobachtet `.block, .sx` gleichberechtigt, die
   CSS-Ausnahme kannte nur `.block`. Folge: auf JEDER Seite stand die H1 — das LCP-Element —
   auf opacity:0, bis der IntersectionObserver asynchron zurueckkam. Leerer erster Frame.
   Die Sektion selbst bleibt sichtbar; der Auftritt wird darunter bewusst gestaffelt. */
html.reveal-on main > .sx:first-child .sx-inner { opacity: 1; transform: none; transition: none; }

@media (prefers-reduced-motion: reduce) {
  html.reveal-on .sx:not(.in-view) .sx-inner { opacity: 1; transform: none; }
  .sx-inner { transition: none; }
}

/* P5/E3b — der Hero-Auftritt ist choreografiert statt zufaellig: Eyebrow, Titel, Lead, Knopf
   kommen nacheinander. Eine ANIMATION (nicht transition), damit sie ohne .in-view laeuft —
   der Hero wartet auf nichts. `backwards` haelt den verborgenen Zustand waehrend der
   Verzoegerung; danach uebernimmt wieder die Kaskade. Nur auf der ERSTEN Sektion der Seite. */
@media (prefers-reduced-motion: no-preference) {
  html.reveal-on main > .sx:first-child :is(.sx-eyebrow, .sx-title, .sx-lead, .sx-copy, .sx-cta) {
    animation: sxHeroIn .62s cubic-bezier(.2,.8,.2,1) backwards;
  }
  html.reveal-on main > .sx:first-child .sx-title { animation-delay: .07s; }
  html.reveal-on main > .sx:first-child :is(.sx-lead, .sx-copy) { animation-delay: .14s; }
  html.reveal-on main > .sx:first-child .sx-cta { animation-delay: .21s; }
}
@keyframes sxHeroIn { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }

@media (max-width: 40rem) {
  /* full-height sections on mobile too (owner: each section = one screen); svh keeps it stable
     under the mobile URL bar. Slightly less top padding — the mobile header is shorter.
     --sx-pt wandert mit, damit die Struktur-Garantie (max() mit --header-h) auch hier den
     mobilen Wert als Untergrenze nimmt, nicht den Desktop-clamp. */
  .sx-size-cover { --sx-pt: clamp(4.5rem, 10vh, 6rem);
                   padding-block: var(--sx-pt) clamp(2.5rem, 5vh, 3.5rem); }
}

/* ---- feature: editorial split (side image + copy) --------------------------- */
.sx-split { display: grid; gap: clamp(1.8rem, 4vw, 3.6rem); align-items: center; }
.sx-split-media img { width: 100%; height: 100%; object-fit: cover; border-radius: var(--radius);
  aspect-ratio: 4 / 3; box-shadow: 0 40px 80px -50px color-mix(in oklab, var(--ink) 60%, transparent); }
.sx-split .sx-copy { margin-top: .2rem; }
.sx-split .sx-cta { margin-top: 1.2rem; }

/* P5/D3 — die Aufzaehlung einer freien Sektion (Einzugsgebiet, Leistungsumfang, Preislogik).
   Zweispaltig ab Tablet, weil solche Listen typisch 4-8 kurze Punkte haben und einspaltig
   eine unnoetig lange Kolonne ergeben. Der Marker ist ein Akzentquadrat statt eines Punktes —
   die Handfassung macht genau das (orange Quadrate), und es ist einer der billigsten Wege,
   Text als GESTALTET statt als Standardausgabe lesbar zu machen. */
.sx-list { list-style: none; margin: 1.2rem 0 0; padding: 0; display: grid; gap: .6rem .0rem; }
.sx-list > li { display: grid; grid-template-columns: auto 1fr; gap: .6rem; align-items: start;
  font-size: var(--t-base); line-height: 1.55; color: var(--muted); }
/* Der Haken (ein <symbol>, per <use> geteilt — blocks/feature.php). em statt rem, damit er mit
   der Zeile waechst; margin-top rueckt ihn auf die optische Mittellinie der ersten Zeile. */
.sx-list-icon { width: 1.05em; height: 1.05em; flex: none; margin-top: .22em; color: var(--accent-2, var(--accent)); }
.sx-tone-dark .sx-list-icon, .sx-tone-accent .sx-list-icon { color: currentColor; }
@media (min-width: 48rem) {
  .sx-list { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: clamp(1.2rem, 3vw, 2.4rem); }
  /* Eine Liste mit drei oder weniger Punkten bleibt einspaltig — zwei Spalten mit einem
     einsamen Punkt in der zweiten sehen nach Umbruchfehler aus. */
  .sx-list:has(> li:nth-child(-n+3):last-child) { grid-template-columns: minmax(0, 1fr); }
}

/* Die FAQ traegt jetzt die sx-Huelle (blocks/accordion.php). Ihre Ueberschrift ist eine
   sx-title in schmaler Spalte; die .acc-*-Regeln weiter oben gelten unveraendert. */
.sx-accordion .acc-list { margin-top: clamp(1.2rem, 3vh, 2rem); }
@media (min-width: 55rem) {
  .sx-split { grid-template-columns: 52fr 48fr; }
  .sx-split.is-flip .sx-split-media { order: 2; }
  .sx-split.is-solo { grid-template-columns: min(100%, 760px); }
}
/* P5 — eine bildlose Sektion MIT Aufzaehlung stellt Prosa und Liste nebeneinander.
   Gerendert an der Preis-Sektion der neu gebauten RSM-Site: die Lesespalte ist auf 760px
   gedeckelt (richtig fuer Lesbarkeit), wodurch bei 1366px die rechte Haelfte des Containers
   leer blieb — 480px tote Flaeche neben einem Text, der etwas belegt. Die Handfassung loest
   dieselbe Stelle genau so: Argument links, Checkliste rechts (css/blocks.css .flt__group).
   Ueberschrift und Handlungsaufforderung spannen weiter ueber beide Spalten. */
@media (min-width: 62rem) {
  .sx-split.is-solo:has(.sx-list) { grid-template-columns: min(100%, 1060px); }
  .sx-split.is-solo .sx-split-copy:has(.sx-list) {
    display: grid; grid-template-columns: 1.05fr .95fr;
    column-gap: clamp(2rem, 4vw, 3.6rem); align-items: start;
  }
  .sx-split.is-solo .sx-split-copy:has(.sx-list) > :is(.sx-eyebrow, .sx-title, .sx-cta) { grid-column: 1 / -1; }
  .sx-split.is-solo .sx-split-copy:has(.sx-list) > .sx-copy { grid-column: 1; }
  .sx-split.is-solo .sx-split-copy:has(.sx-list) > .sx-list {
    grid-column: 2; grid-template-columns: minmax(0, 1fr); margin-top: .35rem;
  }
}

/* ---- index: service cards --------------------------------------------------- */
/* height-aware spacing so a multi-card grid stays close to one screen on short laptops
   (was 4vw margin + 2.2vw gap, which ignored viewport height and pushed the grid over 100vh) */
/* P4c: 60ch measured the SMALL container font (~696px), which forced the display-sized title into
   4-5 lines — the same ch-trap already documented for .sx-quote below. The reading measure belongs
   on .sx-lead (48ch), not on the headline column. */
.sx-index-head { max-width: min(100%, 62rem); margin-bottom: clamp(1.1rem, 2.5vh, 2rem); }
.sx-index-head .sx-lead { margin-bottom: 0; }
/* P5 — das Kartenbild darf wieder ein Bild sein.
   Vorher: is-rows-2 -> 15vh, is-rows-3 -> 10vh. Auf einem 900px-Fenster sind das 135px bzw. 90px
   Bildhoehe bei ~290px Kartenbreite — aus einem 3:2-Foto blieb ein Querstreifen, und die
   Leistungssektion zeigte acht Bilder, von denen keines etwas zeigte. Die Staffelung war reine
   Folge des 100svh-Zwangs (mehr Reihen mussten in dieselbe Hoehe passen). Der Zwang ist weg,
   also darf die Sektion mit der Reihenzahl wachsen. Die Klassen bleiben — sie steuern jetzt
   eine milde Abstufung in rem/vh statt einer Quetschung, und sie tragen weiter den
   srcset-sizes-Hinweis aus blocks/index.php. */
.sx-cards { display: grid; gap: clamp(.9rem, 1.8vh, 1.5rem); grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
  --sx-card-media: clamp(10rem, 24vh, 16rem); }
.sx-cards.is-cols-1 { grid-template-columns: min(100%, 30rem); }
.sx-cards.is-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.sx-cards.is-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.sx-cards.is-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.sx-cards.is-rows-2 { --sx-card-media: clamp(9rem, 21vh, 14rem); }
.sx-cards.is-rows-3 { --sx-card-media: clamp(8rem, 19vh, 12rem); }
.sx-card { display: flex; flex-direction: column; overflow: clip; border-radius: var(--radius);
  background: color-mix(in oklab, var(--paper) 92%, var(--ink)); color: var(--ink);
  border: 1px solid color-mix(in oklab, var(--paper) 82%, var(--ink)); text-decoration: none;
  transition: transform .3s cubic-bezier(.2,.8,.2,1), box-shadow .3s ease; }
.sx-tone-dark .sx-card { background: rgba(255,255,255,.06); border-color: rgba(255,255,255,.14); color: #fff; }
a.sx-card:hover { transform: translateY(-4px); box-shadow: 0 26px 50px -30px color-mix(in oklab, var(--ink) 55%, transparent); }
.sx-card-media { display: block; aspect-ratio: 3 / 2; max-height: var(--sx-card-media); overflow: clip; }
.sx-card-media img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .5s ease; }
a.sx-card:hover .sx-card-media img { transform: scale(1.05); }
/* flex:1 — der Koerper fuellt die Karte aus, damit der Fuss unten andocken kann. Ohne das
   endet er direkt unter dem Text und die Preise fluchten nicht. */
.sx-card-body { display: flex; flex-direction: column; gap: .35rem; padding: clamp(.9rem, 1.8vh, 1.35rem); flex: 1; }
.sx-card-head { display: flex; align-items: baseline; gap: .5rem; }
.sx-card-foot { margin-top: auto; padding-top: .55rem; display: flex; flex-direction: column; gap: .1rem; }
/* S2b library icon (data/spline, Lucide). currentColor is the whole point — the glyph takes the
   brand accent, and #fff on a dark section.
   P5: Das Icon sitzt jetzt IMMER neben dem Titel, nicht mehr nur auf fotolosen Karten ueber ihm.
   Vorher galt «Foto ODER Icon», und weil generierte Karten praktisch immer ein Foto bekommen,
   wurde kein einziges der gebackenen Icons je gerendert (gemessen: 14 vorhanden, 0 sichtbar).
   Neben den Worten liest es sich als Beschriftung, darueber als Dekoration — dieselbe
   Entscheidung, die schon fuer die Handy-Ansicht getroffen wurde, jetzt ueberall. */
.sx-card-icon { display: block; width: clamp(1.05rem, 1.9vh, 1.4rem); height: clamp(1.05rem, 1.9vh, 1.4rem);
  color: var(--accent); flex: none; }
.sx-card-icon svg { width: 100%; height: 100%; display: block; }
.sx-tone-dark .sx-card-icon { color: #fff; }
/* Icon und Titel teilen sich eine Zeile (.sx-card-head im Markup) — das frueher noetige
   :has()-Raster entfaellt damit, und der Fuss kann unten andocken. */
.sx-card-title { font-family: var(--font-display); font-weight: 680; font-size: var(--t-lg); line-height: 1.15; letter-spacing: -.01em; }
.sx-card-text { font-size: var(--t-sm); line-height: 1.6; color: var(--muted); }
/* P5/D1 — der Preisanker. Er steht bewusst NACH dem Teaser und vor dem "Mehr →": erst was es
   ist, dann was es kostet, dann der Weg. Tabellenziffern, damit Preise untereinander fluchten. */
.sx-card-price { font-weight: 700; font-size: var(--t-base); color: var(--ink);
  font-variant-numeric: tabular-nums; margin-top: .15rem; }
.sx-tone-dark .sx-card-price { color: #fff; }
/* P5 — die eine Asymmetrie, die das System besitzt. `big` wurde bisher gesetzt (Generator) und
   beim bento->index-Umbau wieder weggeworfen, und `.sx-cards` kannte ohnehin nur gleich breite
   Spalten. Ergebnis war ein Schachbrett aus identischen Kacheln auf jeder generierten Seite. */
@media (min-width: 55rem) {
  .sx-cards.is-cols-3 .sx-card.is-big,
  .sx-cards.is-cols-4 .sx-card.is-big { grid-column: span 2; grid-row: span 2; }
  .sx-cards .sx-card.is-big .sx-card-media { max-height: none; height: 100%; aspect-ratio: auto; }
  .sx-cards .sx-card.is-big { display: grid; grid-template-rows: 1fr auto; }
  .sx-cards .sx-card.is-big .sx-card-title { font-size: var(--t-xl); }
}
.sx-card-more { font-size: var(--t-sm); font-weight: 640; color: var(--accent); margin-top: .1rem; }
.sx-tone-dark .sx-card-more { color: #fff; }
/* mobile: the explicit column counts MUST collapse (4 columns at 390px = 90px cards). Attribute
   selector keeps the same specificity as .sx-cards.is-cols-N and wins by source order. */
@media (max-width: 55rem) {
  .sx-cards[class*="is-cols-"] { grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
    --sx-card-media: 19vh; }   /* owner rule: on phones compactness beats generous imagery */
  /* Das Kartenicon steht seit P5 IMMER neben dem Titel (.sx-card-head im Markup), auch auf dem
     Desktop — die frueher hier noetige :has()-Sonderregel fuer Handys ist damit ueberfluessig.
     Die urspruengliche Messung gilt weiter und war der Grund fuer die Entscheidung: bei 390x844
     kostete eine eigene Icon-Zeile 26 px pro Karte und schob ein gestapeltes Raster von 131 %
     auf 140 % vh. Neben den Worten kostet es nichts — und liest sich als Beschriftung statt als
     Dekoration. Hier bleibt nur die Groesse. */
  .sx-card-icon { width: clamp(.95rem, 1.7vh, 1.15rem); height: clamp(.95rem, 1.7vh, 1.15rem); }
}

/* ---- proof: stats + voice --------------------------------------------------- */
.sx-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 9rem), 1fr));
  gap: clamp(1.1rem, 2.5vh, 2.2rem); justify-items: center; margin-inline: auto; max-width: 1000px; }
.sx-stat { display: flex; flex-direction: column; gap: .3rem; }
.sx-stat-value { font-family: var(--font-display); font-weight: 780; font-size: clamp(2.4rem, 1.6rem + 3vw, 4rem);
  line-height: 1; letter-spacing: -.03em; }
.sx-stat-label { font-size: var(--t-sm); letter-spacing: .04em; color: var(--muted); }
/* rem (not ch): ch here measures the SMALL container font, not the large quote font -> too narrow. */
.sx-quote { margin: clamp(1.4rem, 3vh, 2.6rem) auto 0; max-width: 54rem; border: 0; padding: 0; }
/* P4c: same height-aware cap as .sx-title — a 242-character quote at 45.6px filled 8 lines (452px)
   and pushed the proof section to 118% vh. Long quotes additionally step down one notch (is-long,
   set by the block when the text exceeds the one-screen budget) instead of being truncated. */
.sx-quote p { font-family: var(--font-display); font-weight: 600; font-size: min(var(--t-2xl), 5.2vh); line-height: 1.24;
  letter-spacing: -.02em; margin: 0; text-wrap: balance; }
.sx-quote.is-long p { font-size: min(var(--t-xl), 4vh); line-height: 1.35; }
.sx-quote p::before { content: "\201C"; } .sx-quote p::after { content: "\201D"; }
.sx-quote footer { margin-top: 1rem; font-size: var(--t-sm); color: var(--muted); }
.sx-quote-author { font-weight: 640; color: currentColor; }

/* ============================================================================
   P3 — BACKGROUND SYSTEM (12 waehlbare Typen)
   New media layers (video, slides, art) live INSIDE .sx-media (z 0), the scrim
   (z 1) and motion canvas (z 2) stack above them unchanged. Modes (gradient,
   pattern, split) are surface treatments on the .sx root itself.
   ============================================================================ */

/* -- video loop: sits over the poster img, under the scrim; JS may remove it -- */
.sx-video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; pointer-events: none; }

/* -- slideshow: stacked slides, only .is-on is visible; Ken Burns on the active image -- */
.sx-slides { position: absolute; inset: 0; display: block; }
.sx-slide { position: absolute; inset: 0; opacity: 0; transition: opacity 1.2s ease; }
.sx-slide.is-on { opacity: 1; }
.sx-slide img { width: 100%; height: 100%; object-fit: cover; display: block; }
@keyframes sx-kenburns-a { from { transform: scale(1); } to { transform: scale(1.07) translate3d(1.2%, -.8%, 0); } }
@keyframes sx-kenburns-b { from { transform: scale(1.07) translate3d(-1%, .6%, 0); } to { transform: scale(1); } }
.sx-slide.is-on img { animation: sx-kenburns-a 7.4s ease-out both; }
.sx-slide:nth-child(even).is-on img { animation-name: sx-kenburns-b; }

/* dots are injected by sxmedia.js (no-JS pages get none); above content, small tap targets kept
   generous via padding-box */
.sx-dots { position: absolute; left: 50%; bottom: clamp(.9rem, 2.4vh, 1.5rem); transform: translateX(-50%);
  z-index: 4; display: flex; gap: .55rem; }
.sx-dots button { width: 10px; height: 10px; padding: 0; border: 1px solid rgba(255,255,255,.9); border-radius: 50%;
  background: rgba(0,0,0,.25); cursor: pointer; }
.sx-dots button[aria-current] { background: #fff; }

/* -- strong veil ("Bild mit Overlay"): a heavier flat floor, tone-independent -- */
.sx.sx-scrim-strong.sx-has-media .sx-scrim {
  background: linear-gradient(180deg, rgba(6,8,12,.46), rgba(6,8,12,.66));
}

/* -- gradient surface: brand-driven, overrides the tone surface (accent tone supplies text colours) -- */
.sx-bg-gradient {
  background: linear-gradient(150deg, var(--accent-deep) 0%, var(--accent) 56%,
    color-mix(in oklab, var(--accent) 55%, var(--accent-soft)) 100%);
}

/* -- pattern surfaces: accent-tinted geometry on the tone surface (background-image only,
      so the tone's background-color keeps carrying the floor) -- */
.sx-bg-pattern-grid {
  background-image:
    linear-gradient(color-mix(in oklab, var(--accent) 12%, transparent) 1px, transparent 1px),
    linear-gradient(90deg, color-mix(in oklab, var(--accent) 12%, transparent) 1px, transparent 1px);
  background-size: 44px 44px;
}
.sx-bg-pattern-dots {
  background-image: radial-gradient(color-mix(in oklab, var(--accent) 24%, transparent) 1.5px, transparent 1.6px);
  background-size: 26px 26px;
}
.sx-bg-pattern-lines {
  background-image: repeating-linear-gradient(45deg,
    color-mix(in oklab, var(--accent) 10%, transparent) 0 1px, transparent 1px 16px);
}

/* -- split: media fills the right half, text owns the solid left half; no scrim needed -- */
.sx-bg-split .sx-media { left: 50%; }
.sx-bg-split .sx-scrim { display: none; }
.sx-bg-split .sx-inner { display: grid; grid-template-columns: minmax(0, 46%) 1fr; }
.sx-bg-split .sx-inner > * { grid-column: 1; }

/* -- illustration: palette-adaptive inline SVG as the full-bleed backdrop -- */
.sx-art { position: absolute; inset: 0; display: block; }
.sx-art svg { width: 100%; height: 100%; display: block; }

@media (max-width: 40rem) {
  /* split stacks on mobile: image band on top, copy below */
  .sx-bg-split .sx-media { left: 0; bottom: auto; height: 36svh; }
  .sx-bg-split .sx-inner { grid-template-columns: 1fr; padding-top: 38svh; }
  .sx-dots button { width: 12px; height: 12px; }
}

@media (prefers-reduced-motion: reduce) {
  .sx-slide { transition: none; }
  .sx-slide.is-on img, .sx-slide:nth-child(even).is-on img { animation: none; }
}
