/* base.css — technisches Grundgerüst für alle /redesign-Vorschauen.
   Themes überschreiben NUR die :root-Variablen + eigene Sektions-Styles. */
:root {
  --bg: #ffffff;
  --bg-alt: #f4f4f5;
  --text: #18181b;
  --text-dim: #52525b;
  --accent: #2563eb;
  --accent-contrast: #ffffff;
  --display: system-ui, 'Segoe UI', Roboto, sans-serif;
  --body: system-ui, 'Segoe UI', Roboto, sans-serif;
  --radius: 1rem;
  --pad: clamp(1rem, 4vw, 2.5rem);
}

* { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  font-family: var(--body);
  background: var(--bg);
  color: var(--text);
  line-height: 1.6;
  overflow-x: hidden;
}
img { max-width: 100%; height: auto; display: block; }

/* ---------- A11y ---------- */
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--accent); color: var(--accent-contrast);
  padding: .75rem 1.25rem; font-weight: 700;
}
.skip-link:focus { left: 0; }
a:focus-visible, button:focus-visible {
  outline: 3px solid var(--accent); outline-offset: 3px; border-radius: 2px;
}
.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

/* ---------- Layout ---------- */
.container { max-width: 72rem; margin-inline: auto; padding-inline: var(--pad); }
.section { padding-block: clamp(3.5rem, 8vw, 6.5rem); }
.section--alt { background: var(--bg-alt); }
.section-kicker {
  font-weight: 800; letter-spacing: .2em; text-transform: uppercase;
  font-size: .8rem; margin-bottom: .5rem; color: var(--accent);
}
.section-title { font-family: var(--display); font-size: clamp(2.2rem, 6vw, 4rem); line-height: 1.05; margin-bottom: 1rem; }

.grid { display: grid; gap: 1.5rem; }
@media (min-width: 700px) {
  .grid--2 { grid-template-columns: 1fr 1fr; }
  .grid--3 { grid-template-columns: repeat(3, 1fr); }
}

/* ---------- Header ---------- */
.site-header {
  position: sticky; top: 0; z-index: 50;
  background: color-mix(in srgb, var(--bg) 85%, transparent);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid color-mix(in srgb, var(--text) 12%, transparent);
}
.header-inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; padding-block: .6rem; flex-wrap: wrap;
}
.logo {
  font-family: var(--display); font-size: 1.5rem; color: var(--text);
  text-decoration: none; display: inline-flex; align-items: center;
  gap: .5rem; min-height: 44px; font-weight: 800;
}
.site-nav ul { display: flex; list-style: none; gap: .25rem; flex-wrap: wrap; }
.site-nav a {
  display: inline-flex; align-items: center; min-height: 44px;
  padding: .35rem .8rem; color: var(--text-dim); text-decoration: none;
  font-weight: 600; font-size: .95rem; border-radius: 999px;
  transition: color .2s, background .2s;
}
.site-nav a:hover { color: var(--text); background: color-mix(in srgb, var(--text) 8%, transparent); }
.site-nav a.nav-cta { background: var(--accent); color: var(--accent-contrast); }

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 48px; padding: .8rem 2rem; border-radius: 999px;
  font-weight: 800; font-size: 1.05rem; text-decoration: none;
  transition: transform .15s, box-shadow .15s, background .2s;
}
.btn--primary { background: var(--accent); color: var(--accent-contrast); }
.btn--primary:hover { transform: translateY(-2px); }
.btn--ghost { border: 2px solid color-mix(in srgb, var(--text) 40%, transparent); color: var(--text); }
.btn--ghost:hover { border-color: var(--text); }

/* ---------- Bild-Hilfen ---------- */
.img-cover { width: 100%; height: 100%; object-fit: cover; }
.img-card { border-radius: var(--radius); overflow: hidden; }

/* ---------- Footer ---------- */
.site-footer {
  background: var(--bg-alt);
  border-top: 1px solid color-mix(in srgb, var(--text) 12%, transparent);
  padding-block: 3rem 5.5rem;
}
.footer-grid { display: grid; gap: 2rem; grid-template-columns: 1fr; }
@media (min-width: 700px) { .footer-grid { grid-template-columns: repeat(3, 1fr); } }
.site-footer h2 { font-size: 1.05rem; margin-bottom: .6rem; text-transform: uppercase; letter-spacing: .1em; }
.site-footer p, .site-footer li { color: var(--text-dim); font-size: .95rem; }
.site-footer ul { list-style: none; }
.site-footer a { color: var(--text); text-underline-offset: 3px; }
.footer-legal {
  margin-top: 2.5rem; padding-top: 1.5rem;
  border-top: 1px solid color-mix(in srgb, var(--text) 10%, transparent);
  font-size: .85rem; color: var(--text-dim);
  display: flex; gap: 1.5rem; flex-wrap: wrap;
}

/* ---------- Entwurfs-Badge (Pflicht) ---------- */
.draft-badge {
  position: fixed; right: .9rem; bottom: .9rem; z-index: 60;
  background: color-mix(in srgb, var(--text) 92%, transparent);
  color: var(--bg); border-radius: 999px; padding: .45rem 1rem;
  font-size: .78rem; font-weight: 600; pointer-events: none;
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
}

/* ---------- Scroll-Reveal ----------
   Sichtbar per Default (progressive enhancement): erst wenn base.js läuft,
   setzt es .js-reveal und blendet aus. Bleibt JS aus, verschwindet nichts. */
.reveal.js-reveal { opacity: 0; transform: translateY(24px); transition: opacity .6s ease, transform .6s ease; }
.reveal.js-reveal.is-visible { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { .reveal.js-reveal { opacity: 1; transform: none; transition: none; } }

/* ---------- Scroll-gekoppeltes Text-Aufhellen ----------
   Für kurze Statement-Absätze (nicht für lange Fließtexte). Startet als
   normaler, voll sichtbarer Text (progressive enhancement) — erst base.js
   zerlegt ihn in Wörter und dimmt sie, danach hellen sie proportional zum
   Scrollfortschritt durch den Block auf. .sh-ready markiert "JS ist bereit",
   ohne das bleibt der Absatz einfach normaler Text. */
.scroll-highlight.sh-ready .sh-w {
  color: color-mix(in srgb, var(--text) 25%, transparent);
  transition: color .15s linear;
}
.scroll-highlight.sh-ready .sh-w.is-lit { color: var(--text); }
@media (prefers-reduced-motion: reduce) {
  .scroll-highlight.sh-ready .sh-w { color: var(--text); transition: none; }
}

/* ---------- Swipebare Abschnitte (mobil) ----------
   Handy: horizontales Scroll-Snap-Karussell, kein JS nötig für die Bedienung.
   Aktive Karte wird zentriert und ist dabei bündig mit dem normalen Seitenrand
   (--swipe-peek = derselbe Rand wie .container/--pad) statt an einem eigenen
   Prozent-Abstand zu kleben. Springt pro Wisch genau eine Karte weiter:
   scroll-snap-stop:always verhindert, dass ein schneller Wisch mehrere Karten
   überspringt und "weiterrotiert". ::before/::after sind so breit, dass die
   erste/letzte Karte trotzdem bündig zentriert einrasten kann.
   Ab 720px: normales Grid, keine Karten mehr abgeschnitten. Für Cards, Referenzen,
   Zitate, Logo-/Badge-Reihen – überall, wo auf dem Handy mehr "Bewegung" gut tut. */
.swipe {
  --swipe-peek: var(--pad);
  display: flex; gap: 1rem; overflow-x: auto; scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch; padding-block: .25rem;
  scrollbar-width: none; margin-inline: calc(var(--swipe-peek) * -1);
}
.swipe::-webkit-scrollbar { display: none; }
.swipe > * {
  scroll-snap-align: center;
  scroll-snap-stop: always;
  flex: 0 0 calc(100% - 2 * var(--swipe-peek));
  max-width: 22rem;
}
.swipe::before,
.swipe::after { content: ""; flex: 0 0 calc(var(--swipe-peek) - 1rem); }
.swipe::before { scroll-snap-align: start; }
@media (min-width: 720px) {
  .swipe {
    overflow-x: visible; display: grid;
    grid-template-columns: repeat(var(--swipe-cols, 3), 1fr);
    gap: 1.5rem; margin-inline: 0; padding-inline: 0;
  }
  .swipe > * { flex: initial; max-width: none; scroll-snap-align: none; scroll-snap-stop: normal; }
  .swipe::before, .swipe::after { content: none; }
}
/* .swipe bricht per negativem Margin bis zum Seitenrand aus — das setzt
   voraus, dass es DIREKT im .container sitzt. In einer schmaleren Spalte
   (.split-row__body) ist der Seitenrand nicht der Viewport-Rand, der
   Ausbruch erzeugt dort echten horizontalen Überlauf. Deshalb dort neutral. */
.split-row__body .swipe { margin-inline: 0; }
.split-row__body .swipe::before, .split-row__body .swipe::after { content: none; }

.swipe-dots { display: flex; justify-content: center; gap: .4rem; margin-top: 1rem; }
.swipe-dots span {
  width: 6px; height: 6px; border-radius: 50%;
  background: color-mix(in srgb, var(--text) 25%, transparent);
  transition: background .2s, transform .2s;
}
.swipe-dots span.is-active { background: var(--accent); transform: scale(1.4); }
@media (min-width: 720px) { .swipe-dots { display: none; } }
@media (prefers-reduced-motion: reduce) { .swipe { scroll-behavior: auto; } }

/* ---------- Magnetische Buttons ----------
   Button folgt der Maus minimal (max. ~8px), federt beim Verlassen zurück.
   Ohne JS/bei reduced-motion: ganz normaler Button, keine Bewegung. */
.btn--magnetic { transition: transform .25s cubic-bezier(.2,.8,.2,1); }

/* ---------- Zahlen zählen hoch ----------
   <strong class="count-up" data-target="150" data-suffix="+">0</strong>
   Startwert im Markup ist "0" (oder Endwert für No-JS/Crawler) — base.js zählt
   erst hoch, wenn der Block ins Bild scrollt. font-variant-numeric hält die
   Ziffernbreite stabil, damit der Text beim Zählen nicht "zittert". */
.count-up { font-variant-numeric: tabular-nums; }

/* ---------- Sticky Schritt-Erklärung ----------
   Links ein sticky-fixiertes Bild/Icon, rechts mehrere Schritte, die beim
   Vorbeiscrollen einzeln hervorgehoben werden (nicht alle gleichzeitig grau).
   Nur für echte Abläufe mit Reihenfolge (z. B. "So arbeite ich"), keine
   künstliche Nummerierung für Aussagen ohne echte Sequenz. */
.steps-scroll { display: grid; gap: 3rem; }
@media (min-width: 900px) {
  .steps-scroll { grid-template-columns: 1fr 1fr; align-items: start; gap: 4rem; }
  .steps-scroll__visual { position: sticky; top: 6rem; }
}
.steps-scroll__step {
  padding-block: 2.5rem; border-top: 1px solid color-mix(in srgb, var(--text) 12%, transparent);
  opacity: .4; transition: opacity .35s ease;
}
.steps-scroll__step:first-child { border-top: 0; }
.steps-scroll__step.is-active { opacity: 1; }
.steps-scroll__num {
  font-family: var(--display); font-weight: 700; font-size: .85rem;
  color: var(--accent); letter-spacing: .06em; margin-bottom: .5rem; display: block;
}
@media (prefers-reduced-motion: reduce) { .steps-scroll__step { opacity: 1; transition: none; } }

/* ---------- Marquee-Band (Endlos-Laufband) ----------
   Für Logo-/Partnerreihen oder eine wiederholte kurze Botschaft. Reiner CSS-Loop,
   kein JS nötig. Inhalt im Markup EINMAL schreiben, das zweite Mal identisch
   kopieren (siehe blocks.html) — die Animation verschiebt exakt um 50%, damit
   der Loop nahtlos wirkt. Pausiert bei Hover und bei reduzierter Bewegung. */
.marquee { overflow: hidden; -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent); }
.marquee__track { display: flex; width: max-content; gap: 3.5rem; animation: marquee 26s linear infinite; }
.marquee:hover .marquee__track { animation-play-state: paused; }
@keyframes marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) { .marquee__track { animation: none; } .marquee { overflow-x: auto; } }

/* ---------- Cursor-Glow (dunkle Sektionen) ----------
   Ein weiches Licht folgt dem Mauszeiger — nur auf .section--glow, nur Desktop
   (kein Touch), nur wenn Bewegung erlaubt ist. Rein dekorativ, per JS über
   --mx/--my gesteuerte CSS-Variablen, kein Layout-Einfluss. */
.section--glow { position: relative; overflow: hidden; }
.section--glow::before {
  content: ""; position: absolute; z-index: 0; pointer-events: none;
  left: var(--mx, 50%); top: var(--my, 50%); width: 42rem; height: 42rem;
  transform: translate(-50%, -50%);
  background: radial-gradient(circle, color-mix(in srgb, var(--accent) 16%, transparent) 0%, transparent 70%);
  opacity: 0; transition: opacity .4s ease;
}
.section--glow.is-glowing::before { opacity: 1; }
.section--glow > * { position: relative; z-index: 1; }

/* ---------- Vorher/Nachher-Bildregler ----------
   Zwei Fotos exakt gleicher Größe übereinander, ein <input type="range">
   zieht die Trennlinie. Ohne JS/CSS-only: fixer 50/50-Split (Regler ist
   dann nur optisch inert, nicht kaputt) — JS synchronisiert clip-path und
   Griff live mit dem Wert. Für echte Vorher/Nachher-Fotos, nie erfunden. */
.compare-slider {
  position: relative; aspect-ratio: 16 / 10; overflow: hidden;
  border-radius: var(--radius); user-select: none; touch-action: pan-y;
}
.compare-slider__before, .compare-slider__after {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block;
}
.compare-slider__before { clip-path: inset(0 50% 0 0); }
.compare-slider__label {
  position: absolute; top: .75rem; z-index: 1;
  font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em;
  color: #fff; background: rgba(0,0,0,.5); padding: .3rem .7rem; border-radius: 999px;
  pointer-events: none;
}
.compare-slider__label--before { left: .75rem; }
.compare-slider__label--after { right: .75rem; }
.compare-slider__handle {
  position: absolute; top: 0; bottom: 0; left: 50%; width: 3px; background: #fff;
  transform: translateX(-50%); box-shadow: 0 0 0 1px rgba(0,0,0,.15); pointer-events: none; z-index: 1;
}
.compare-slider__handle::after {
  content: "\21d4"; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 2.4rem; height: 2.4rem; border-radius: 50%; background: #fff; color: #18181b;
  display: flex; align-items: center; justify-content: center; font-size: 1.1rem;
  box-shadow: 0 4px 14px rgba(0,0,0,.25);
}
.compare-slider__range {
  position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: ew-resize;
  -webkit-appearance: none; appearance: none; z-index: 2;
}

/* ---------- 3D-Tilt-Karten ----------
   Karte kippt minimal Richtung Mauszeiger (Perspektive), federt beim
   Verlassen zurück. Nur Desktop/Maus. Sparsam einsetzen — für einzelne
   Bild-/Produktkarten, nicht für ganze Grids gleichzeitig. */
.tilt { transition: transform .2s ease-out; will-change: transform; }

/* ---------- Scroll-Fortschrittsleiste ----------
   Hauchdünner fixierter Balken oben, zeigt den Lesefortschritt der Seite.
   <div class="scroll-progress"></div> einmal direkt nach <body> einfügen. */
.scroll-progress {
  position: fixed; top: 0; left: 0; width: 100%; height: 3px;
  background: var(--accent); transform: scaleX(0); transform-origin: 0 0;
  z-index: 70; pointer-events: none;
}

/* ---------- Bottom-Dock-Navigation ----------
   Alternative zur klassischen Kopfzeile (.site-header): schwebende Pille
   unten, zentriert, statt oben fixiert. Passt zu One-Page-Portfolios/
   Agentur-Auftritten mit wenigen Ankern. Der Link zum aktuell sichtbaren
   Abschnitt wird per JS hervorgehoben (scroll-spy) — ohne JS bleiben alle
   Links normal klickbar, nur ohne aktive Markierung. Ersetzt .site-header,
   nicht zusätzlich zu ihr einsetzen. */
.dock-nav {
  position: fixed; left: 50%; bottom: 1.25rem; transform: translateX(-50%);
  z-index: 60; display: flex; gap: .2rem; max-width: calc(100% - 2rem);
  overflow-x: auto; scrollbar-width: none;
  background: var(--bg); border-radius: 999px; padding: .3rem;
  box-shadow: 0 10px 30px rgba(0,0,0,.14), 0 0 0 1px color-mix(in srgb, var(--text) 10%, transparent);
}
.dock-nav::-webkit-scrollbar { display: none; }
.dock-nav a {
  display: inline-flex; align-items: center; white-space: nowrap; min-height: 40px;
  padding: .5rem 1.1rem; border-radius: 999px; font-weight: 700; font-size: .85rem;
  color: var(--text-dim); text-decoration: none; transition: background .2s, color .2s;
}
.dock-nav a:hover { color: var(--text); }
.dock-nav a.is-active { background: var(--accent); color: var(--accent-contrast); }

/* ---------- Scroll-Schärfe-Reveal ----------
   Statement/Bild kommt beim Scrollen unscharf-zu-scharf in den Fokus statt
   nur zu faden — wirkt filmischer als reines .reveal. Für einzelne
   Aussagen/Bilder, NICHT für Fließtext (Lesbarkeit während der Bewegung).
   Progressive enhancement: ohne JS/bei reduzierter Bewegung immer
   scharf & voll sichtbar. */
.blur-reveal { will-change: filter, opacity; }
@media (prefers-reduced-motion: reduce) { .blur-reveal { filter: none !important; opacity: 1 !important; } }

/* ---------- Punktraster-Hintergrund ----------
   Dezente Textur hinter Hero-Content/Cards, reines CSS, kein JS. Farbe
   folgt automatisch --text, funktioniert also auf hell und dunkel. */
.bg-dots {
  background-image: radial-gradient(color-mix(in srgb, var(--text) 18%, transparent) 1.5px, transparent 1.5px);
  background-size: 22px 22px;
}
/* Variante: Fadenkreuz-Raster statt Punkte — nüchterner/technischer,
   passt zu Kanzleien/Ingenieurbüros/Präzisionshandwerk. */
.bg-crosshair {
  background-image:
    linear-gradient(color-mix(in srgb, var(--text) 12%, transparent) 1px, transparent 1px),
    linear-gradient(90deg, color-mix(in srgb, var(--text) 12%, transparent) 1px, transparent 1px);
  background-size: 44px 44px;
  background-position: center;
}

/* ---------- Theme-Umschalter (Hell/Dunkel) ----------
   Setzt data-theme="light"/"dark" auf <html> und überschreibt damit
   gezielt die System-Einstellung — merkt sich die Wahl (localStorage).
   Setzt voraus, dass das Theme nach demselben Muster wie bei Artifacts
   aufgebaut ist: Basis-Palette auf :root, System-Dark über
   "@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) }",
   explizites Dark nochmal über ":root[data-theme=\"dark\"]" (siehe
   Theme-Vorgabe in Phase 2/3). Ohne JS bleibt der Button funktionslos,
   die Seite folgt einfach der System-Einstellung — kein Bruch. */
.theme-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; border-radius: 999px; flex: none;
  border: 1px solid color-mix(in srgb, var(--text) 18%, transparent);
  background: transparent; color: var(--text); cursor: pointer;
}
.theme-toggle svg { width: 18px; height: 18px; }
.theme-toggle__moon { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle__sun { display: none; }
  :root:not([data-theme="light"]) .theme-toggle__moon { display: block; }
}
:root[data-theme="dark"] .theme-toggle__sun { display: none; }
:root[data-theme="dark"] .theme-toggle__moon { display: block; }

/* ---------- Status-Punkt (verfügbar/online) ----------
   Kleiner pulsierender Punkt vor einer Verfügbarkeits-/Statusangabe
   ("2 Plätze frei im Juli", "Aktuell erreichbar"). Feste grüne Farbe
   (Semantik "verfügbar"), unabhängig von der Markenakzentfarbe — wie bei
   Ampelfarben üblich. **Nur bei echten, aktuellen Angaben**, nie als
   Deko-Attrappe (siehe Live-Mikro-Details-Regel in Phase 2). */
.status-dot {
  display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  background: #22c55e; position: relative; flex: none;
}
.status-dot::after {
  content: ""; position: absolute; inset: -4px; border-radius: 50%;
  background: inherit; opacity: .5; animation: status-pulse 2s ease-out infinite;
}
@keyframes status-pulse { 0% { transform: scale(.6); opacity: .5; } 100% { transform: scale(1.8); opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .status-dot::after { animation: none; display: none; } }

/* ---------- Anlass-Auswahl (Kontaktformular) ----------
   Kachel-Auswahl statt langweiligem <select> für "Worum geht es?" im
   Kontaktformular. Radio-Buttons visuell versteckt, Kachel reagiert auf
   :checked — reines CSS, keine Zeile JS. Funktioniert identisch ohne
   CSS (dann normale native Radios), also niemals kaputt. */
.anlass-waehler { display: flex; flex-wrap: wrap; gap: .6rem; }
.anlass-option { position: relative; }
.anlass-option input {
  position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer;
}
.anlass-option span {
  display: inline-flex; align-items: center; gap: .5rem; min-height: 44px;
  padding: .6rem 1.1rem; border-radius: 999px; border: 1.5px solid color-mix(in srgb, var(--text) 20%, transparent);
  font-weight: 600; font-size: .92rem; color: var(--text-dim); background: var(--bg);
  transition: border-color .15s, color .15s, background .15s;
}
.anlass-option input:checked + span {
  border-color: var(--accent); color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, var(--bg));
}
.anlass-option input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---------- Wunschzeit-Chips (Terminanfrage) ----------
   Mehrfachauswahl "wann passt es Ihnen" als Chips statt Freitext/Dropdown
   — für Betriebe, die echte Terminvorschläge statt Live-Kalender wollen
   (kein Cal.com/Buchungssystem, nur eine strukturierte Angabe fürs
   Kontaktformular). Checkbox-basiert, reines CSS. */
.zeit-chips { display: flex; flex-wrap: wrap; gap: .5rem; }
.zeit-chip { position: relative; }
.zeit-chip input { position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
.zeit-chip span {
  display: inline-flex; align-items: center; min-height: 40px; padding: .45rem .9rem;
  border-radius: var(--radius); border: 1.5px dashed color-mix(in srgb, var(--text) 25%, transparent);
  font-size: .88rem; color: var(--text-dim); transition: border-color .15s, color .15s, background .15s, border-style .15s;
}
.zeit-chip input:checked + span {
  border-style: solid; border-color: var(--accent); color: var(--accent-contrast); background: var(--accent);
}
.zeit-chip input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---------- Live-Ortszeit ----------
   Zeigt die aktuelle Uhrzeit am Firmenstandort, aktualisiert sich selbst.
   Ohne JS steht einfach der statische Fallback-Text im Markup (z. B. der
   Ortsname) da — kein "--:--", kein Bruch. Wirkt lebendig/lokal verankert,
   nur bei echtem Standort einsetzen (siehe Live-Mikro-Details-Regel). */
.local-time { font-variant-numeric: tabular-nums; }

/* ---------- Eckklammern (Viewfinder-Rahmen) ----------
   Vier kleine Eckklammern um ein Element, wie ein Kamera-Fokusrahmen —
   gibt Fotos/Hero-Flächen einen präzisen, "ausgewählten" Akzent. Reines
   CSS, kein JS. Sparsam einsetzen (1 Element pro Seite), gut für
   Fotografie/Architektur/Präzisionshandwerk. Markup: 4 leere <span
   aria-hidden="true"> vor dem eigentlichen Inhalt (siehe blocks.html). */
.frame-corners { position: relative; }
.frame-corners > span {
  position: absolute; width: 1.4rem; height: 1.4rem; pointer-events: none; z-index: 1;
  border: 2px solid var(--accent);
}
.frame-corners > span:nth-of-type(1) { top: -.5rem; left: -.5rem; border-right: 0; border-bottom: 0; }
.frame-corners > span:nth-of-type(2) { top: -.5rem; right: -.5rem; border-left: 0; border-bottom: 0; }
.frame-corners > span:nth-of-type(3) { bottom: -.5rem; left: -.5rem; border-right: 0; border-top: 0; }
.frame-corners > span:nth-of-type(4) { bottom: -.5rem; right: -.5rem; border-left: 0; border-top: 0; }

/* ---------- Film-Grain-Textur ----------
   Feines Rauschen über dunklen (oder hellen) Flächen statt flacher
   Farbfläche — wirkt hochwertiger/fotografischer. Reines SVG-Rauschen
   als Daten-URI, kein externer Request, kein Bild-Download. Sehr dezent
   dosiert (Opacity), overlay-Blend sorgt dafür, dass es auf hell wie
   dunkel funktioniert. Rein dekorativ, nimmt keinen Platz im Layout ein. */
/* ACHTUNG — .grain belegt ::after. Bekommt dieselbe Sektion ein zweites
   Overlay (Foto-Verlauf o. Ä.), darf dieses NICHT ebenfalls ::after nutzen:
   zwei Regeln für dasselbe Pseudo-Element mischen ihre Eigenschaften, das
   zweite Overlay erbt dann stillschweigend die opacity:.05 von hier und
   dunkelt praktisch nicht ab — der Text wird unlesbar, ohne dass im Code
   ein Fehler sichtbar wäre. Zweites Overlay deshalb auf ::before legen.
   Betrifft auch die fertigen Bausteine .chapter--photo, .hero-atmo,
   .opening und .portal__panel — die nutzen alle ::after. */
.grain { position: relative; isolation: isolate; }
.grain::after {
  content: ""; position: absolute; inset: 0; z-index: 5; pointer-events: none;
  opacity: .05; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
}

/* ---------- Schwebende Foto-Badges (Trust-Pills auf Portrait/Hero-Foto) ----------
   Kleine, freigestellte Kennzahl-Pillen, die auf einem Portrait- oder
   Team-Foto "schweben" statt in einer separaten Kennzahlen-Sektion zu
   stehen — bindet echte Zahlen (Jahre Erfahrung, Kundenanzahl) direkt an
   das Gesicht dahinter. Reines CSS, kein JS. Nur echte, belegbare Angaben,
   nie erfinden. Sparsam: max. 2 Badges pro Foto, sonst wirkt es unruhig. */
.foto-badges { position: relative; display: inline-block; line-height: 0; }
.foto-badges img { display: block; width: 100%; border-radius: var(--radius); }
.foto-badges__badge {
  position: absolute; z-index: 2;
  display: inline-flex; align-items: center;
  background: var(--bg); color: var(--text);
  padding: .6rem 1.1rem; border-radius: 999px;
  font-size: .85rem; font-weight: 600; line-height: 1.2; white-space: nowrap;
  box-shadow: 0 10px 28px rgba(0, 0, 0, .18);
}
.foto-badges__badge--tr { top: 1.25rem; right: -1rem; }
.foto-badges__badge--br { bottom: 1.25rem; right: -1rem; }
.foto-badges__badge--bl { bottom: 1.25rem; left: -1rem; }
.foto-badges__badge--tl { top: 1.25rem; left: -1rem; }
@media (max-width: 640px) {
  .foto-badges__badge--tr, .foto-badges__badge--tl { left: 1rem; right: auto; top: 1rem; }
  .foto-badges__badge--br, .foto-badges__badge--bl { left: 1rem; right: auto; bottom: 1rem; }
}

/* ---------- Video-Teaser mit Lightbox (ohne JS, reines :target) ----------
   Schwebende Abspielen-Pille über einem Vorschaubild statt eines
   eingebetteten <iframe> mit externem Anbieter (kein DSGVO-Cookie-Problem).
   Klick öffnet ein Vollbild-Modal per CSS-:target-Technik, komplett ohne
   JavaScript. Nur mit einem echten, selbst gehosteten Vorstellungsvideo
   einsetzen (z. B. kurzer Werkstatt-/Kanzlei-Rundgang), nie als Platzhalter. */
.video-teaser { position: relative; display: block; line-height: 0; }
.video-teaser__poster { display: block; width: 100%; border-radius: var(--radius); }
.video-teaser__play {
  position: absolute; right: 1.25rem; bottom: 1.25rem; z-index: 2;
  display: inline-flex; align-items: center; gap: .6rem;
  background: var(--bg); color: var(--text); text-decoration: none;
  padding: .5rem 1.1rem .5rem .5rem; border-radius: 999px;
  font-size: .85rem; font-weight: 600; line-height: 1.2;
  box-shadow: 0 10px 28px rgba(0, 0, 0, .22);
  transition: transform .2s ease;
}
.video-teaser__play:hover { transform: scale(1.04); }
.video-teaser__play-icon { position: relative; width: 2.25rem; height: 2.25rem; border-radius: 50%; background: var(--accent); flex-shrink: 0; }
.video-teaser__play-icon::before {
  content: ""; position: absolute; top: 50%; left: 57%; transform: translate(-50%, -50%);
  border-style: solid; border-width: .5rem 0 .5rem .8rem;
  border-color: transparent transparent transparent var(--accent-contrast);
}
.video-teaser__modal {
  position: fixed; inset: 0; z-index: 100; padding: 2rem;
  display: flex; align-items: center; justify-content: center;
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity .25s ease;
}
.video-teaser__modal:target { opacity: 1; visibility: visible; pointer-events: auto; }
.video-teaser__backdrop { position: absolute; inset: 0; background: rgba(0, 0, 0, .75); }
.video-teaser__modal-box { position: relative; width: 100%; max-width: 900px; }
.video-teaser__modal-box video { display: block; width: 100%; border-radius: var(--radius); }
.video-teaser__close {
  position: absolute; top: -2.75rem; right: 0; z-index: 1;
  width: 2.25rem; height: 2.25rem; display: flex; align-items: center; justify-content: center;
  color: #fff; font-size: 1.75rem; line-height: 1; text-decoration: none;
}
@media (prefers-reduced-motion: reduce) { .video-teaser__modal { transition: none; } }

/* ---------- Prozedurale 3D-Szene (scroll-gekoppelt) ----------
   Braucht zusätzlich <script type="module" src="assets/scene3d.js"></script>
   und assets/vendor/three.module.min.js — NICHT in base.js enthalten (siehe
   Kommentar dort). Ohne JS/WebGL/bei reduced-motion bleibt das Fallback-Bild
   stehen, die Sektion ist also nie leer. */
.scene-3d { position: relative; aspect-ratio: 1 / 1; }
.scene-3d canvas { display: block; width: 100%; height: 100%; }
.scene-3d__fallback { width: 100%; height: 100%; object-fit: contain; }
.scene-3d.is-live .scene-3d__fallback { display: none; }

/* ============================================================
   SEITENARCHITEKTUREN (Achse 1 des Design-Registers)
   ------------------------------------------------------------
   Jede der in SKILL.md Phase 2 gelisteten Architekturen hat hier ihr
   Layout-Fundament, damit sie genauso billig zu bauen ist wie der
   klassische Vertikal-Stapel — sonst gewinnt immer der Stapel, nur weil
   er der bequemste Weg ist. Markup-Skelette dazu: blocks.html, Abschnitt
   "SEITENARCHITEKTUREN". Pro Projekt genau EINE davon wählen.
   Alle Architekturen sind reines CSS (Ausnahme: Scroll-Spy der
   Seitenleisten-Navigation, siehe base.js) und mobil-tauglich.
============================================================ */

/* ---------- A) Persistente Seitenleiste ----------
   Feste schmale Spalte links (Logo/Navigation/Kontakt), rechts scrollt der
   Inhalt. Passt zu Kanzleien/Beratungen mit viel Text. Unter 1000px klappt
   die Leiste automatisch zu einem normalen Kopfbereich auf — kein Burger-
   Menü nötig, kein JS. Ersetzt .site-header, nicht zusätzlich einsetzen. */
.layout-sidebar__aside {
  padding: var(--pad);
  border-bottom: 1px solid color-mix(in srgb, var(--text) 12%, transparent);
  background: var(--bg);
}
.layout-sidebar__brand {
  font-family: var(--display); font-weight: 800; font-size: 1.5rem;
  color: var(--text); text-decoration: none; display: inline-block;
}
.layout-sidebar__nav ul { list-style: none; display: flex; flex-wrap: wrap; gap: .25rem 1.25rem; margin-top: 1rem; }
.layout-sidebar__nav a {
  display: inline-flex; align-items: center; min-height: 44px;
  color: var(--text-dim); text-decoration: none; font-weight: 600; font-size: .95rem;
  transition: color .2s;
}
.layout-sidebar__nav a:hover, .layout-sidebar__nav a.is-active { color: var(--text); }
.layout-sidebar__foot { display: none; }
@media (min-width: 1000px) {
  .layout-sidebar { display: grid; grid-template-columns: minmax(15rem, var(--sidebar-w, 26%)) minmax(0, 1fr); align-items: start; }
  .layout-sidebar__aside {
    position: sticky; top: 0; height: 100vh; height: 100svh;
    display: flex; flex-direction: column; gap: 2rem;
    border-bottom: 0; border-right: 1px solid color-mix(in srgb, var(--text) 12%, transparent);
    padding: clamp(1.75rem, 3vw, 2.75rem);
  }
  .layout-sidebar__nav ul { flex-direction: column; gap: 0; margin-top: 0; }
  /* Aktiver Punkt bekommt einen Akzent-Marker statt nur Farbwechsel. */
  .layout-sidebar__nav a { position: relative; padding-left: 1.1rem; }
  .layout-sidebar__nav a::before {
    content: ""; position: absolute; left: 0; top: 50%; transform: translateY(-50%);
    width: .4rem; height: .4rem; border-radius: 50%; background: var(--accent);
    opacity: 0; transition: opacity .2s;
  }
  .layout-sidebar__nav a.is-active::before { opacity: 1; }
  .layout-sidebar__foot { display: block; margin-top: auto; color: var(--text-dim); font-size: .9rem; }
  .layout-sidebar__foot a { color: inherit; text-underline-offset: 3px; }
  .layout-sidebar__main { min-width: 0; }
  /* Der Inhaltsbereich hat schon die Leiste neben sich — der innere
     Container darf deshalb nicht nochmal auf volle Seitenbreite zielen. */
  .layout-sidebar__main .container { max-width: 60rem; }
}

/* ---------- B) Kapitel-Sequenz ----------
   3–5 fast bildschirmfüllende "Momente" statt einer langen hellen Fläche mit
   einer Ausnahme-Sektion. Jedes Kapitel setzt seinen Ton per Inline-Variablen
   im Markup — weil es die Theme-Variablen selbst überschreibt, folgen Buttons,
   Kicker und Trennlinien automatisch mit:
     <section class="chapter" style="--chapter-bg:#12211C; --text:#fff; --text-dim:rgba(255,255,255,.72)">
   Gedeckte Markenvarianten verwenden, keine Neonfarben.
   ACHTUNG: Wer in einem dunklen Kapitel zusätzlich --accent aufhellt, MUSS
   --accent-contrast mitsetzen (z. B. auf den dunklen Kapitelgrund) — sonst
   steht weißer Buttontext auf hellem Akzent und der Kontrast fällt durch.
   Das ist der häufigste Fehler bei dieser Architektur. */
.chapter {
  background: var(--chapter-bg, var(--bg));
  color: var(--text);
  min-height: 86vh; min-height: 86svh;
  display: flex; align-items: center;
  padding-block: clamp(4rem, 9vw, 7.5rem);
}
/* PFLICHT bei allen Flex-Sektionen: ein .container als Flex-Item schrumpft
   sonst auf seine Inhaltsbreite (shrink-to-fit) und wird zentriert — die
   Sektion bekommt dadurch eine ANDERE linke Kante als der Rest der Seite.
   Das ist als uneinheitlicher Seitenabstand sichtbar und schwer zu finden. */
.chapter > .container { width: 100%; }
.chapter__num {
  font-family: var(--display); font-weight: 700; font-size: .8rem;
  letter-spacing: .22em; color: var(--accent); margin-bottom: 1rem; display: block;
}
/* Kapitel mit Vollbild-Foto als Grund: Verlauf großzügig über die ganze
   Fläche ziehen, damit auch ein dreizeilig umbrechender Titel auf dem Handy
   nicht in eine helle Bildstelle wächst. */
.chapter--photo { position: relative; overflow: hidden; color: #fff; }
.chapter--photo > img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0;
}
/* Deckkraft nirgends unter ~0.68: der Text sitzt vertikal zentriert, also
   genau dort, wo ein Verlauf üblicherweise am schwächsten ist. Bei 0.3 wäre
   weißer Text auf einem sonnigen Foto rechnerisch bei 1.9:1 — unlesbar, und
   Lighthouse meldet es nicht, weil es Text auf Bildern nicht bewerten kann.
   Faustregel: 0.68 ergibt auch über reinweißem Foto noch ~6.9:1. */
.chapter--photo::after {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(180deg, rgba(0,0,0,.74) 0%, rgba(0,0,0,.68) 40%, rgba(0,0,0,.86) 100%);
}
.chapter--photo > .container { position: relative; z-index: 2; text-shadow: 0 2px 18px rgba(0,0,0,.5); }

/* ---------- C) Durchgehender Asymmetrie-Split ----------
   Nicht nur der Hero: JEDE Sektion behält denselben schmal/breit-Split
   (Kicker/Meta links schmal, Inhalt rechts breit) als visuelle Leitlinie über
   die ganze Seite. Die Meta-Spalte bleibt am Desktop stehen, während der
   Inhalt daran vorbeiscrollt. */
.split-row { display: grid; gap: .75rem; }
.split-row__meta { color: var(--text-dim); font-size: .85rem; letter-spacing: .12em; text-transform: uppercase; font-weight: 700; }
.split-row__body { min-width: 0; }
@media (min-width: 880px) {
  .split-row { grid-template-columns: minmax(0, var(--split-meta, 13rem)) minmax(0, 1fr); gap: clamp(2.5rem, 6vw, 5rem); }
  .split-row__meta { position: sticky; top: 6rem; align-self: start; }
}

/* ---------- D) Dossier-Sequenz ----------
   Fortlaufende Kartei statt luftiger Cards: jeder Eintrag bekommt eine
   schmale Kennzeichnungsleiste ("Aktendeckel": Nummer + 1–2 Meta-Tags,
   Trennlinie), direkt gefolgt von großer Überschrift, kurzem Text und Bild.
   Die Blöcke stapeln sich dicht — gut für viele vergleichbare Einträge
   (Leistungen, Referenzen, Ablaufschritte). */
.dossier__item {
  border-top: 1px solid color-mix(in srgb, var(--text) 16%, transparent);
  padding-block: clamp(2rem, 5vw, 3.25rem);
}
.dossier__item:last-child { border-bottom: 1px solid color-mix(in srgb, var(--text) 16%, transparent); }
.dossier__tab {
  display: flex; align-items: baseline; gap: 1rem; flex-wrap: wrap;
  font-size: .74rem; letter-spacing: .16em; text-transform: uppercase;
  font-weight: 700; color: var(--text-dim); margin-bottom: 1.1rem;
}
.dossier__num { color: var(--accent); font-family: var(--display); }
.dossier__tags { margin-left: auto; display: flex; gap: 1rem; flex-wrap: wrap; }
.dossier__body { display: grid; gap: 1.25rem; }
.dossier__body h3 { font-family: var(--display); font-size: clamp(1.6rem, 3.6vw, 2.5rem); line-height: 1.1; }
@media (min-width: 880px) {
  .dossier__body { grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr); gap: clamp(2rem, 4vw, 3.5rem); align-items: start; }
}

/* ---------- E) Portal-Auswahl-Start ----------
   Statt eines einzigen Heldenmoments zwei (selten drei) gleich große
   Bild-Panels nebeneinander, die in getrennte Angebotsbereiche führen.
   NUR bei wirklich getrennten Bereichen/Zielgruppen einsetzen (Privat/
   Gewerbe, zwei Standorte, Neubau/Sanierung) — sonst wirkt es unmotiviert.
   Spaltenzahl über --portal-cols (Default 2). */
.portal { display: grid; grid-template-columns: 1fr; gap: 2px; }
@media (min-width: 820px) {
  .portal { grid-template-columns: repeat(var(--portal-cols, 2), 1fr); min-height: 88vh; min-height: 88svh; }
}
.portal__panel {
  position: relative; overflow: hidden; text-decoration: none; color: #fff;
  display: flex; align-items: flex-end;
  min-height: 46vh; min-height: 46svh;
  padding: clamp(1.5rem, 3.5vw, 3rem);
}
.portal__panel > img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0;
  transition: transform .7s cubic-bezier(.2,.8,.2,1);
}
/* Verlauf bewusst großzügig und unten sehr kräftig: die Panels bekommen
   im Zweifel ein helles Stockfoto, und der Text sitzt am unteren Rand —
   ein knapp bemessener Verlauf liefert dort grauen Text auf hellem Grund. */
.portal__panel::after {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(180deg, rgba(0,0,0,.42) 0%, rgba(0,0,0,.22) 28%, rgba(0,0,0,.72) 72%, rgba(0,0,0,.92) 100%);
}
.portal__panel > div { position: relative; z-index: 2; text-shadow: 0 2px 16px rgba(0,0,0,.55); }
.portal__panel h2 { font-family: var(--display); font-size: clamp(1.9rem, 4vw, 3.2rem); line-height: 1.05; }
.portal__panel p { color: rgba(255,255,255,.86); margin-top: .5rem; max-width: 34ch; }
.portal__panel:hover > img, .portal__panel:focus-visible > img { transform: scale(1.045); }
@media (prefers-reduced-motion: reduce) { .portal__panel > img { transition: none; } }

/* ---------- F) Ultra-minimaler Auftakt ----------
   Startseite besteht fast nur aus einem großen Foto + Wortmarke, der Inhalt
   folgt erst nach explizitem Scroll-Anker. Nur bei wenig Textinhalt und
   starkem Bildmaterial. Gehört zusammen mit .wordmark-echo als Seitenabschluss
   statt eines mehrspaltigen Footers. */
.opening {
  position: relative; overflow: hidden;
  min-height: 100vh; min-height: 100svh;
  display: grid; place-items: center; text-align: center;
}
.opening > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0; }
/* Wortmarke und Unterzeile sitzen mittig (place-items: center) — der Verlauf
   darf genau dort nicht einbrechen. 0.25 in der Mitte ergäbe über einem hellen
   Foto 1.7:1; die Unterzeile ist normaler Text und braucht 4.5:1. */
.opening::after {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(180deg, rgba(0,0,0,.5) 0%, rgba(0,0,0,.58) 45%, rgba(0,0,0,.8) 100%);
}
.opening__inner { position: relative; z-index: 2; padding: var(--pad); color: #fff; text-shadow: 0 2px 24px rgba(0,0,0,.5); }
.opening__mark {
  font-family: var(--display); font-weight: 800;
  font-size: clamp(2.6rem, 11vw, 8rem); line-height: .95; letter-spacing: -.02em;
}
.opening__sub { margin-top: 1rem; font-size: clamp(1rem, 2vw, 1.25rem); color: rgba(255,255,255,.88); }
.opening__scroll {
  position: absolute; left: 50%; bottom: 1.75rem; transform: translateX(-50%); z-index: 3;
  display: inline-flex; flex-direction: column; align-items: center; gap: .4rem;
  color: #fff; text-decoration: none; font-size: .78rem; letter-spacing: .16em; text-transform: uppercase;
  min-height: 44px; text-shadow: 0 2px 12px rgba(0,0,0,.7);
}
.opening__scroll::after {
  content: ""; width: 1px; height: 2rem;
  background: linear-gradient(180deg, #fff, transparent);
  animation: opening-hint 2.2s ease-in-out infinite;
}
@keyframes opening-hint { 0%, 100% { opacity: .35; transform: scaleY(.7); transform-origin: top; } 50% { opacity: 1; transform: scaleY(1); transform-origin: top; } }
/* Großes Wortmarken-Echo als Seitenabschluss. Bewusst am unteren Rand
   angeschnitten (overflow: hidden + negativer Abstand), damit es wie ein
   grafischer Schlusspunkt wirkt und nicht wie eine weitere Überschrift. */
.wordmark-echo { overflow: hidden; padding-top: clamp(3rem, 8vw, 6rem); }
.wordmark-echo > span {
  display: block; font-family: var(--display); font-weight: 800;
  font-size: clamp(3rem, 17vw, 13rem); line-height: .78; letter-spacing: -.03em;
  text-align: center; color: var(--text); margin-bottom: -.14em;
}

/* ---------- G) Organischer Streu-Layout ----------
   Karten/Fotos bewusst leicht versetzt statt exakt im Raster — wirkt
   handgemacht statt Baukasten. Dezent halten (Versatz in rem, nicht in
   Rotation), sparsam für Referenzen/Team mit wenigen Fotos einsetzen.
   Die Vignette entsteht über eine weiche Maske statt eines harten Beschnitts. */
.scatter { display: grid; gap: clamp(1.5rem, 4vw, 2.75rem); grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); align-items: start; }
.scatter__item { text-align: center; }
.scatter__item img {
  width: 100%; aspect-ratio: 1; object-fit: cover; border-radius: 50%;
  -webkit-mask-image: radial-gradient(circle at 50% 50%, #000 56%, transparent 76%);
  mask-image: radial-gradient(circle at 50% 50%, #000 56%, transparent 76%);
}
.scatter__item strong { display: block; margin-top: .75rem; font-family: var(--display); }
.scatter__item small { color: var(--text-dim); }
@media (min-width: 840px) {
  .scatter__item:nth-child(2n) { transform: translateY(2.75rem); }
  .scatter__item:nth-child(3n) { transform: translateY(-1.25rem) scale(1.07); }
}

/* ---------- Hintergrund-Wortmarke im Hero ----------
   Firmenname als sehr große Display-Typo mit niedrigem Kontrast hinter dem
   Hero-Inhalt, teils vom Rand angeschnitten. Nur bei kurzen, gut lesbaren
   Markennamen. Rein dekorativ (aria-hidden), nimmt keinen Platz ein und darf
   die eigentliche Headline nie stören — deshalb sehr niedriger Kontrast.
   Gehört in einen Container mit position: relative. */
.hero-wordmark {
  position: absolute; z-index: 0; pointer-events: none; user-select: none;
  left: 50%; top: 50%; transform: translate(-50%, -50%);
  font-family: var(--display); font-weight: 800; white-space: nowrap;
  font-size: clamp(6rem, 26vw, 22rem); line-height: 1; letter-spacing: -.04em;
  color: color-mix(in srgb, var(--text) 8%, transparent);
}
.hero-wordmark--edge { left: auto; right: -.08em; top: auto; bottom: -.22em; transform: none; }
/* Auf schmalen Screens liegt die Marke fast deckungsgleich unter der
   Headline und macht sie unruhig — dort deutlich weiter zurücknehmen. */
@media (max-width: 700px) { .hero-wordmark { color: color-mix(in srgb, var(--text) 5%, transparent); } }

/* ============================================================
   HERO-MUSTER (Achse 2 des Design-Registers)
   ------------------------------------------------------------
   Die drei Grundformen (Split / Zentriert+Band / Vollbild-Overlay) stehen
   als Skelett in blocks.html (HERO A–C). Hier liegen die aufwendigeren
   Muster, die in SKILL.md Phase 2 beschrieben sind — sie brauchen echtes
   Layout-CSS und wurden deshalb bisher nie gewählt.
   EIN Hero-Muster pro Projekt. Markup: blocks.html, Abschnitt "HERO-MUSTER".
============================================================ */

/* ---------- Nahtstellen-Wortmarke (vgl. apf-international.nl) ----------
   Firmenname sehr groß auf der Kante zwischen leerer Fläche und Foto: obere
   Buchstabenhälfte steht auf dem Seitenhintergrund, untere Hälfte liegt im
   Bild. Der Zweifarb-Effekt entsteht über einen harten Verlauf in der
   Schriftfüllung — eine zweite Textkopie wäre für Screenreader doppelt.
   Nur bei kurzen Markennamen (bis ~10 Zeichen), sonst wird die Marke winzig. */
.hero-seam { position: relative; }
/* Kein overflow: hidden — die obere Markenhälfte ragt absichtlich über die
   Bildkante hinaus, genau das ist der Nahtstellen-Effekt. */
.hero-seam__photo { position: relative; aspect-ratio: 16 / 7; }
.hero-seam__photo img { width: 100%; height: 100%; object-fit: cover; }
/* Die untere Markenhälfte ist weiß und liegt im oberen Bildstreifen — auf
   einem hellen Foto verschwindet sie sonst spurlos. Der Verlauf dunkelt
   genau diesen Streifen ab und macht das Muster unabhängig vom Motiv. */
.hero-seam__photo::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 42%; z-index: 1;
  background: linear-gradient(180deg, rgba(0,0,0,.5) 0%, rgba(0,0,0,.28) 45%, transparent 100%);
}
.hero-seam__mark {
  position: absolute; left: 50%; top: 0; transform: translate(-50%, -50%);
  z-index: 2; width: max-content; max-width: 96vw; pointer-events: none;
  font-family: var(--display); font-weight: 800; line-height: .8;
  font-size: clamp(2.75rem, 15vw, 12rem); letter-spacing: -.03em;
  color: var(--text);
}
/* color: transparent erst setzen, wenn die Schriftfüllung wirklich greift —
   sonst wäre die Marke in alten Browsern schlicht unsichtbar. */
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .hero-seam__mark {
    background-image: linear-gradient(180deg, var(--text) 0 50%, #fff 50% 100%);
    -webkit-background-clip: text; background-clip: text; color: transparent;
  }
}

/* ---------- Kreisausschnitt-Hero (vgl. j3facades.co.uk) ----------
   Ein großer heller Kreis liegt im Vollbild-Foto und trägt dort Eyebrow,
   Headline und CTA — punktuelle statt flächiger Lesbarkeit, das Foto bleibt
   unangetastet hell. Auf schmalen Screens fasst ein Kreis zu wenig Text,
   deshalb wird er dort zur abgerundeten Fläche: Lesbarkeit vor Form. */
.hero-circle { position: relative; overflow: hidden; display: grid; align-items: center; min-height: 82vh; min-height: 82svh; }
.hero-circle > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0; }
.hero-circle__disc {
  position: relative; z-index: 2; margin: var(--pad);
  background: var(--bg); color: var(--text);
  border-radius: var(--radius); padding: clamp(1.75rem, 5vw, 3rem);
  display: flex; flex-direction: column; justify-content: center;
}
@media (min-width: 820px) {
  .hero-circle { justify-items: var(--circle-side, end); }
  .hero-circle__disc {
    border-radius: 50%; aspect-ratio: 1; width: min(42vw, 30rem);
    text-align: center; align-items: center;
    padding: clamp(2.5rem, 5vw, 4rem); margin: clamp(1.5rem, 4vw, 4rem);
  }
  .hero-circle__disc > * { max-width: 26ch; }
}

/* ---------- Hero als eingerahmte Karte (vgl. titanintake.com) ----------
   Das Hero-Foto sitzt in einer stark abgerundeten Karte mit sichtbarem
   Seitenhintergrund ringsherum statt randlos bis zum Viewport-Rand — wirkt
   aufgeräumter/produkthafter. Die Vorteils-Pillen sitzen auf der unteren
   Kartenkante; sie liegen bewusst im normalen Fluss mit negativem Abstand
   statt absolut positioniert, damit sie auf dem Handy nichts überdecken. */
.hero-framed { padding: clamp(.5rem, 1.6vw, 1.25rem); }
.hero-framed__card {
  position: relative; overflow: hidden;
  border-radius: clamp(1rem, 3vw, 2rem);
  min-height: 64vh; min-height: 64svh;
  display: flex; align-items: flex-end;
  padding: clamp(1.5rem, 4vw, 3.5rem);
  color: #fff;
}
.hero-framed__card > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0; }
.hero-framed__card::after {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(180deg, rgba(0,0,0,.4) 0%, rgba(0,0,0,.2) 30%, rgba(0,0,0,.7) 75%, rgba(0,0,0,.88) 100%);
}
.hero-framed__card > .container, .hero-framed__card > div { position: relative; z-index: 2; width: 100%; text-shadow: 0 2px 16px rgba(0,0,0,.5); }
.hero-framed__pills {
  position: relative; z-index: 3; margin: -1.4rem auto 0;
  display: flex; flex-wrap: wrap; justify-content: center; gap: .5rem;
  padding-inline: var(--pad);
}
.hero-framed__pills > span {
  background: var(--bg); color: var(--text);
  border-radius: 999px; padding: .55rem 1.1rem;
  font-size: .85rem; font-weight: 600; line-height: 1.2;
  box-shadow: 0 8px 24px rgba(0,0,0,.14);
}

/* ---------- Portrait vor Farbkreis-Cluster (vgl. kraaccountancy.co.uk) ----------
   Freigestelltes Portrait vor zwei bis drei sanft überlappenden Kreisflächen
   in verwandten Markentönen statt in einem rechteckigen Fotorahmen — wirkt
   persönlicher/organischer. Für clean & corporate bewusst GEDECKT halten:
   die Töne entstehen aus --accent gemischt mit dem Seitenhintergrund, nicht
   aus Signalfarben. Gut für Einzelunternehmer:innen mit starkem Portrait. */
.hero-orbs { position: relative; display: block; max-width: 34rem; margin-inline: auto; }
.hero-orbs > span { position: absolute; border-radius: 50%; z-index: 0; }
/* Die Kreise ragen bewusst über die Bildkanten hinaus: bei einem sauber
   freigestellten Portrait entsteht so der Cluster-Eindruck, und selbst wenn
   das Bild doch rechteckig ist, bleibt das Muster sichtbar statt verdeckt. */
.hero-orbs > span:nth-of-type(1) { width: 84%; aspect-ratio: 1; left: 8%;   top: 2%;  background: color-mix(in srgb, var(--accent) 26%, var(--bg)); }
.hero-orbs > span:nth-of-type(2) { width: 48%; aspect-ratio: 1; left: -9%;  top: 40%; background: color-mix(in srgb, var(--accent) 44%, var(--bg)); }
.hero-orbs > span:nth-of-type(3) { width: 36%; aspect-ratio: 1; right: -7%; top: 10%; background: color-mix(in srgb, var(--accent) 16%, var(--bg)); }
.hero-orbs > img { position: relative; z-index: 1; width: 100%; }

/* ---------- Atmosphären-Hero mit scharfem Insert (vgl. schyns.de) ----------
   Stark unscharfes Foto füllt die Fläche als reiner Stimmungsträger, ein
   kleines scharfes Detailfoto liegt als "Beweisbild" darin. Löst den Fall,
   dass ein einzelnes scharfes Foto zu nüchtern wirkt — und der Weichzeichner
   nimmt dem Hintergrund gleichzeitig die Kontrastprobleme unter dem Text.
   scale(1.08) verhindert die hellen Ränder, die Blur an den Bildkanten
   erzeugt. Das unscharfe Bild ist dekorativ (alt=""), das Insert nicht. */
.hero-atmo { position: relative; overflow: hidden; display: flex; align-items: center; min-height: 78vh; min-height: 78svh; }
.hero-atmo > img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0;
  filter: blur(22px) saturate(1.15); transform: scale(1.08);
}
/* Der Weichzeichner mittelt die Helligkeit, er dunkelt aber NICHT ab: ein
   sonniges Motiv bleibt auch unscharf hell. Deshalb hier ebenfalls mindestens
   ~0.58 (ergibt über weissem Foto noch ~5.2:1). */
.hero-atmo::after {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(180deg, rgba(0,0,0,.58) 0%, rgba(0,0,0,.66) 100%);
}
/* width: 100% aus demselben Grund wie bei .chapter — ohne das rutscht der
   Hero-Text nach innen und sitzt nicht auf der Kante des übrigen Layouts. */
.hero-atmo > .container { position: relative; z-index: 2; width: 100%; color: #fff; text-shadow: 0 2px 18px rgba(0,0,0,.5); }
/* Das Insert liegt bewusst IM Grid und nicht absolut über dem Text: absolut
   positioniert kollidiert es zwangsläufig mit Headline, Fließtext oder CTA,
   sobald der Text breit genug wird oder der Viewport schmaler. Als eigene
   Spalte kann es den Text konstruktiv nicht mehr überdecken. Der schwebende
   Charakter bleibt über Radius und Schatten erhalten.
   Markup: das <figure class="hero-atmo__insert"> gehört IN den .container. */
@media (min-width: 900px) {
  .hero-atmo > .container { display: grid; grid-template-columns: 1.4fr .6fr; align-items: center; gap: clamp(2rem, 5vw, 4rem); }
}
.hero-atmo__insert {
  overflow: hidden; margin: 2rem 0 0;
  width: 100%; max-width: 20rem; aspect-ratio: 4 / 5;
  border-radius: var(--radius); box-shadow: 0 14px 40px rgba(0,0,0,.35);
}
@media (min-width: 900px) { .hero-atmo__insert { margin: 0; justify-self: end; } }
.hero-atmo__insert img { width: 100%; height: 100%; object-fit: cover; }

/* ---------- News-Teaser in der Bildecke (vgl. praxis4.vercel.app) ----------
   Kleine Karte mit Vorschaubild und Anreißer in der unteren Ecke eines
   Foto-Heros — ersetzt eine eigene News-Sektion. NUR mit echten, aktuellen
   Inhalten (Jubiläum, Aktion, Meldung), nie erfunden. Modifier für jeden
   Hero mit position: relative. Auf dem Handy über die volle Breite, weil
   eine Eckkarte dort das halbe Bild verdecken würde. */
.hero-news {
  position: absolute; z-index: 3; bottom: 1rem; left: 1rem; right: 1rem;
  display: flex; align-items: center; gap: .8rem;
  background: var(--bg); color: var(--text); text-decoration: none;
  border-radius: var(--radius); padding: .6rem;
  box-shadow: 0 12px 34px rgba(0,0,0,.22);
  transition: transform .2s ease;
}
.hero-news:hover { transform: translateY(-2px); }
.hero-news img { width: 3.4rem; height: 3.4rem; object-fit: cover; border-radius: calc(var(--radius) * .5); flex: none; }
.hero-news strong { display: block; font-size: .92rem; line-height: 1.3; }
.hero-news small { color: var(--accent); font-weight: 700; font-size: .78rem; }
@media (min-width: 720px) {
  .hero-news { left: auto; right: clamp(1.25rem, 3vw, 2.5rem); bottom: clamp(1.25rem, 3vw, 2.5rem); max-width: 21rem; }
}

/* ---------- Beweis-Hero: zweifarbige Headline + Fotoreihe (vgl. philsuttonlandscapes.co.uk) ----------
   Die Headline hat zwei Kontraststufen (Hauptaussage volltonig, Kontextzeile
   blass), direkt darunter beginnt schon eine schmale Reihe quadratischer
   Projektfotos statt einer eigenen Referenzen-Sektion weiter unten — bindet
   den Beweis unmittelbar ans Versprechen. Gut für Handwerk/Garten-/Baubetriebe
   mit vielen kleinen Vorher-Fertig-Fotos. Die Reihe scrollt bewusst
   horizontal (wie .swipe), das ist kein Layout-Unfall. */
/* 60 % ist bewusst kalibriert, nicht geschätzt: darunter reißt der Kontrast
   der Kontextzeile unter 4.5:1 (von Lighthouse als Fehler gemeldet), darüber
   verliert sie den blassen Charakter. Nicht "schöner" (blasser) machen. */
.hero-proof__title em { font-style: normal; display: block; color: color-mix(in srgb, var(--text) 60%, transparent); }
.hero-proof__strip {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(8.5rem, 1fr);
  gap: clamp(.5rem, 1.5vw, 1rem); overflow-x: auto; scrollbar-width: none;
  scroll-snap-type: x proximity; margin-top: clamp(1.5rem, 4vw, 2.5rem);
}
.hero-proof__strip::-webkit-scrollbar { display: none; }
.hero-proof__strip img {
  width: 100%; aspect-ratio: 1; object-fit: cover;
  border-radius: calc(var(--radius) * .5); scroll-snap-align: start;
}
