/* =========================================================
   safeT.watch — design tokens
   ========================================================= */
:root {
  --nav-h: 80px; /* fallback; JS overwrites with the nav's real rendered height */

  --paper: #F0F2F3;
  --paper-dim: #E4E7E9;
  --ink: #12141A;
  --ink-soft: #4B4E58;
  --ink-faint: #6C707B; /* darkened from #8A8D96 — that value read 3.02:1 on --paper, below WCAG AA's 4.5:1 for normal text */
  --line: rgba(18, 20, 26, 0.10);
  --cta-dark: #0B1A2E; /* logo navy — dark CTA buttons use this instead of --ink */

  /* Always-dark chrome — console, footer. Independent of the light/dark toggle:
     the Daylight-First Rule reserves darkness for real product surfaces. */
  --console: #0E1016;
  --console-line: rgba(255, 255, 255, 0.08);
  --console-text: #E7E6E0;
  --console-text-dim: #9497A3;

  --accent: #EB5C38;
  --accent-strong: #BC4A2D;
  --accent-soft: rgba(235, 92, 56, 0.10);
  /* Text/icon/line-art tint for the brand accent sitting directly on --paper/--ink.
     Plain --accent only reaches 3.12:1 on --paper (fails WCAG AA for normal
     text), so light mode uses --accent-strong instead; dark mode brightens to
     FF7A52, which is legible on the dark page surface. .console re-pins this
     locally since its background stays dark regardless of theme. */
  --accent-ink: var(--accent-strong);
  /* The exact orange of the logo assets (img/logo/**), for solid decorative
     shapes that sit next to the real logo (dots, thin lines) — not for text,
     where --accent-ink's darkening keeps WCAG AA contrast on --paper. Fixed
     across both themes, like the logo files themselves. */
  --accent-logo: #F26344;

  /* Success feedback (form submissions), same light/dark split as --accent-ink. */
  --success-ink: #1F7A4D;

  /* Hero hex-field gradient — its own quiet blue-grey family, deliberately
     separate from --accent: decorative background, not a signal color. */
  --hexfield-grad-a: #8892A6;
  --hexfield-grad-b: #4E5A72;

  /* Problem→Solution moment: fixed night/day palette, independent of the
     light/dark toggle — same exception as the console and the CTA gradient
     (Daylight-First Rule). Problem is always night, solution always day, so
     the crossfade reads as "into the dark" and "back into the light"
     regardless of which theme the visitor has chosen.
     Problem stays neutral grey (no accent colour at all) — solution alone
     gets to keep the brand orange, so the reveal itself carries meaning. */
  --morph-problem-bg: #14161B;
  --morph-problem-ink: #F3F1EA;
  --morph-problem-ink-soft: #B9BBC4;
  --morph-problem-ink-faint: #83868F;
  --morph-problem-line: rgba(255, 255, 255, 0.12);
  --morph-problem-accent-ink: #9AA0AC;

  --morph-solution-bg: #F0F2F3;
  --morph-solution-ink: #12141A;
  --morph-solution-ink-soft: #4B4E58;
  --morph-solution-ink-faint: #6C707B;
  --morph-solution-line: rgba(18, 20, 26, 0.10);
  --morph-solution-accent-ink: #EB5C38;

  --font-display: "Sora", ui-sans-serif, system-ui, sans-serif;
  --font-body: "Archivo", ui-sans-serif, system-ui, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, monospace;

  /* Type scale — every literal font-size in this file resolves to one of these steps */
  --text-2xs: 11px;
  --text-xs: 12px;
  --text-sm: 13px;
  --text-sm-alt: 14px;
  --text-body-sm: 14.5px;
  --text-body: 16px;
  --text-md: 15px;
  --text-lg: 17px;
  --text-xl: 18px;
  --text-title-sm: 20px;
  --text-title: 24px;
  --text-headline: clamp(28px, 4vw, 44px);
  --text-headline-lg: clamp(30px, 4.5vw, 50px);
  --text-display: clamp(42px, 6vw, 76px);

  --container: 1280px;
  --edge: clamp(24px, 5vw, 64px);
  --radius: 18px;
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
}

/* Dark mode: system preference by default, explicit toggle always wins.
   Only the "paper" side of the system flips — console/accent/footer chrome
   stays fixed, per the Daylight-First Rule. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper: #14161B;
    --paper-dim: #1B1E25;
    --ink: #F3F1EA;
    --ink-soft: #B9BBC4;
    --ink-faint: #83868F;
    --line: rgba(255, 255, 255, 0.12);
    --accent-ink: #FF7A52;
    --success-ink: #7BE0A8;
    --hexfield-grad-a: #94A3B8;
    --hexfield-grad-b: #64748B;
    --cta-dark: var(--ink);
  }
}
:root[data-theme="dark"] {
  --paper: #14161B;
  --paper-dim: #1B1E25;
  --ink: #F3F1EA;
  --ink-soft: #B9BBC4;
  --ink-faint: #83868F;
  --line: rgba(255, 255, 255, 0.12);
  --accent-ink: #FF7A52;
  --success-ink: #7BE0A8;
  --hexfield-grad-a: #94A3B8;
  --hexfield-grad-b: #64748B;
  --cta-dark: var(--ink);
}

* { box-sizing: border-box; }
html { scroll-behavior: auto; } /* Lenis owns smooth scrolling; native smooth would double up */

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--text-body);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
  transition: background-color 0.3s var(--ease), color 0.3s var(--ease);
}

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.05;
  letter-spacing: -0.02em;
  margin: 0;
}

p { margin: 0; }
a { color: inherit; text-decoration: none; }
button { font-family: inherit; }

/* faint film-grain overlay to keep flat colour fields from feeling sterile */
.grain {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 2000;
  opacity: 0.035;
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* =========================================================
   Reveal-on-scroll
   ========================================================= */
[data-reveal] {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity 0.9s var(--ease), transform 0.9s var(--ease);
}
[data-reveal].is-visible {
  opacity: 1;
  transform: translateY(0);
}

/* =========================================================
   Nav
   ========================================================= */
main#top,
section[id],
#kontakt,
#mitmachen-formular {
  scroll-margin-top: calc(var(--nav-h) + 16px);
}
/* #problem is the top of a 220vh pinned scroll container, not a plain
   section — its content only reaches its real (nav-clearing) position once
   position:sticky engages, which needs the scroll landing at the
   container's own top, not nav-h+16px short of it. Landing short (the
   generic rule above) stops the jump in the dead zone before sticky kicks
   in, where the content briefly renders in normal flow with its own
   internal nav padding stacked underneath the scroll-margin clearance —
   the same clearance counted twice, same as the doubled-offset bug this
   replaced. Zeroing it here lands exactly on the pinned container's top,
   which engages sticky immediately. Only where the section is actually
   pinned (see the .morph unpin query in the Problem → Lösung block below)
   — the stacked mobile/short-viewport fallback still needs the normal
   clearance, since nothing is sticky there. */
@media (min-width: 901px) and (min-height: 821px) {
  #problem { scroll-margin-top: 0; }
}

.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 1000;
  padding: 18px var(--edge) 0;
}
.nav__inner {
  position: relative; /* anchor for the collapsed nav panel */
  max-width: var(--container);
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 20px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--paper) 60%, transparent);
  backdrop-filter: blur(14px) saturate(1.4);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  border: 1px solid transparent;
  transition: background 0.4s var(--ease), border-color 0.4s var(--ease), box-shadow 0.4s var(--ease);
}
.nav.is-scrolled .nav__inner {
  background: color-mix(in srgb, var(--paper) 85%, transparent);
  border-color: var(--line);
  box-shadow: 0 8px 30px rgba(18, 20, 26, 0.06);
}
.nav__logo {
  display: flex;
  align-items: center;
}
.nav__logo img { height: 38px; width: auto; }

/* =========================================================
   Theme toggle + logo light/dark swap
   ========================================================= */
.logo-dark-mode { display: none; }
:root[data-theme="dark"] .logo-light-mode { display: none; }
:root[data-theme="dark"] .logo-dark-mode { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .logo-light-mode { display: none; }
  :root:not([data-theme="light"]) .logo-dark-mode { display: block; }
}

.nav__meta {
  display: flex;
  align-items: center;
  gap: 14px;
}
.lang-switch {
  display: flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.04em;
}
.lang-switch__sep { color: var(--line); }
.lang-switch__option {
  background: none;
  border: none;
  padding: 0;
  color: var(--ink-faint);
  cursor: pointer;
}
.lang-switch__option:disabled { cursor: default; }
.lang-switch__option.is-active { color: var(--ink); font-weight: 600; }

.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--ink-soft);
  cursor: pointer;
  transition: border-color 0.2s var(--ease), color 0.2s var(--ease), transform 0.2s var(--ease);
}
/* hover:hover guard: an unguarded :hover on a touch device makes the first
   tap only apply the hover state (border/color change) and the second tap
   fire the actual click — exactly the "toggle needs two taps" bug on
   mobile. Real pointers keep the hover feedback; touch skips straight to
   the click. */
@media (hover: hover) {
  .theme-toggle:hover { border-color: var(--ink); color: var(--ink); transform: translateY(-1px); }
}
.theme-toggle__icon--moon { display: none; }
:root[data-theme="dark"] .theme-toggle__icon--sun { display: none; }
:root[data-theme="dark"] .theme-toggle__icon--moon { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle__icon--sun { display: none; }
  :root:not([data-theme="light"]) .theme-toggle__icon--moon { display: block; }
}

@media (max-width: 720px) {
  .theme-toggle { width: 32px; height: 32px; }
}
.nav__links {
  display: flex;
  gap: 32px;
  font-size: var(--text-sm-alt);
  color: var(--ink-soft);
}
.nav__links a { transition: color 0.2s; }
.nav__links a:hover { color: var(--ink); }
.nav__cta {
  font-size: var(--text-sm);
  font-weight: 600;
  padding: 9px 18px;
  border-radius: 999px;
  background: var(--cta-dark);
  color: var(--paper);
  transition: background 0.2s var(--ease), transform 0.2s var(--ease);
}
.nav__cta:hover { background: var(--accent); transform: translateY(-1px); }

/* Burger: hidden until the links stop fitting beside the logo and CTA.
   Previously the links were simply display:none below 720px, which left
   small screens with no way to reach any section at all. */
.nav__burger {
  display: none;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: transparent;
  color: var(--ink-soft);
  cursor: pointer;
  transition: border-color 0.2s var(--ease), color 0.2s var(--ease);
}
.nav__burger:hover { border-color: var(--ink); color: var(--ink); }
.nav__burger-box {
  position: relative;
  width: 16px;
  height: 12px;
}
.nav__burger-line {
  position: absolute;
  left: 0;
  width: 100%;
  height: 1.5px;
  border-radius: 999px;
  background: currentColor;
  transition: transform 0.3s var(--ease), opacity 0.2s var(--ease);
}
.nav__burger-line:nth-child(1) { top: 0; }
.nav__burger-line:nth-child(2) { top: 5.25px; }
.nav__burger-line:nth-child(3) { top: 10.5px; }
.nav.is-menu-open .nav__burger-line:nth-child(1) { transform: translateY(5.25px) rotate(45deg); }
.nav.is-menu-open .nav__burger-line:nth-child(2) { opacity: 0; }
.nav.is-menu-open .nav__burger-line:nth-child(3) { transform: translateY(-5.25px) rotate(-45deg); }

/* 1070px is where four links, the logo and the CTA stop coexisting in the
   pill. Below it the same list becomes a panel under the pill — the markup
   is reused rather than duplicated, so there is only one set of links. */
@media (max-width: 1070px) {
  .nav__burger { display: inline-flex; }
  .nav__logo img { height: 28px; }
  .nav__links {
    position: absolute;
    top: calc(100% + 12px);
    left: 0;
    right: 0;
    flex-direction: column;
    gap: 2px;
    padding: 12px;
    border-radius: var(--radius);
    font-size: var(--text-lg);
    background: color-mix(in srgb, var(--paper) 94%, transparent);
    backdrop-filter: blur(14px) saturate(1.4);
    -webkit-backdrop-filter: blur(14px) saturate(1.4);
    border: 1px solid var(--line);
    box-shadow: 0 18px 40px rgba(18, 20, 26, 0.10);
    /* visibility (not display) so the panel can transition, and so its links
       leave the tab order while collapsed */
    opacity: 0;
    visibility: hidden;
    transform: translateY(-8px);
    transition: opacity 0.28s var(--ease), transform 0.28s var(--ease), visibility 0.28s;
  }
  .nav__links a {
    padding: 11px 14px;
    border-radius: 10px;
  }
  .nav__links a:hover { background: var(--paper-dim); }
  .nav__links .nav__panel-cta:hover { background: var(--accent); }
  .nav.is-menu-open .nav__links {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
  }
}

/* The panel's own CTA. Only ever visible while the pill's CTA is hidden,
   so the two never announce the same action twice. */
.nav__panel-cta { display: none; }

@media (max-width: 560px) {
  .nav__cta { font-size: var(--text-xs); padding: 8px 13px; }
}

/* Below 500px the pill can no longer carry logo, theme toggle, CTA and
   burger at once — the CTA moves into the panel and leads it as a full
   width primary button. */
@media (max-width: 500px) {
  .nav__cta { display: none; }
  .nav__logo img { height: 24px; }
  .nav__links .nav__panel-cta {
    display: block;
    margin-top: 10px;
    padding: 13px 14px;
    border-radius: 999px;
    background: var(--cta-dark);
    color: var(--paper);
    font-size: var(--text-md);
    font-weight: 600;
    text-align: center;
  }
}

/* =========================================================
   Buttons
   ========================================================= */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 15px 26px;
  border-radius: 999px;
  font-size: var(--text-md);
  font-weight: 600;
  border: 1px solid transparent;
  cursor: pointer;
  transition: transform 0.25s var(--ease), background 0.25s var(--ease), border-color 0.25s var(--ease), color 0.25s var(--ease);
}
.btn svg { transition: transform 0.25s var(--ease); }
.btn:hover svg { transform: translateX(3px); }
.btn:hover { transform: translateY(-2px); }

.btn--primary { background: var(--cta-dark); color: var(--paper); }
.btn--primary:hover { background: var(--accent); }
.btn--primary.btn--light { background: #F6F4EE; color: var(--accent-strong); }
.btn--primary.btn--light:hover { background: #fff; }

.btn--ghost { background: transparent; color: var(--ink); border-color: var(--line); }
/* Size step for a section whose whole purpose is one action. */
.btn--lg { font-size: var(--text-lg); padding: 18px 34px; }
.btn--ghost:hover { border-color: var(--ink); }

/* =========================================================
   Hero
   ========================================================= */
.hero {
  position: relative;
  min-height: 100svh;
  padding: 160px var(--edge) 100px;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  gap: 60px;
  align-items: center;
  max-width: var(--container);
  margin: 0 auto;
}
.hero__bg {
  position: absolute;
  /* Full-bleed: spans the whole viewport width regardless of .hero's own
     max-width container, so the hex field never looks cropped at the
     content edge on wide screens. body has overflow-x:hidden already, so
     100vw here can't cause a horizontal scrollbar. */
  top: 0;
  bottom: 0;
  left: 50%;
  width: 100vw;
  transform: translateX(-50%);
  z-index: -1;
  overflow: hidden;
  /* Own compositing layer: keeps the hex-field animation smooth when the
     console tilts on hover instead of both repainting together. */
  will-change: transform;
}
.hero__hexfield {
  display: block;
  width: 100%;
  height: 100%;
}

.hero__headline {
  font-size: var(--text-display);
  margin-bottom: 26px;
}
.hero__headline--logo {
  /* Grid-stack both theme variants in the same cell so a light/dark toggle
     can never reflow or shift the logo — only one is ever visible, but both
     always claim the same box. */
  display: grid;
}
.hero__headline--logo img {
  grid-area: 1 / 1;
  width: min(100%, 540px);
  height: auto;
  aspect-ratio: 595.11 / 342.71;
}

.hero__sub,
.btn--ghost {
  transition: color 220ms var(--ease), background-color 220ms var(--ease), border-color 220ms var(--ease);
}

@media (max-width: 920px) {
  .hero__headline--logo img { width: min(100%, 420px); }
}

.hero__sub {
  font-size: var(--text-xl);
  color: var(--ink-soft);
  max-width: 46ch;
  margin-bottom: 34px;
}

.hero__actions { display: flex; flex-direction: column; align-items: flex-start; gap: 16px; }
.hero__cta-row { display: flex; gap: 14px; flex-wrap: wrap; align-items: center; }

/* A status pill above the buttons, not a caption squeezed beside or under
   them — its own row removes any ambiguity about where it sits. Faint tint
   rather than a solid fill, so it reads as a quiet status, not a third CTA. */
.hero__status {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.04em;
  color: var(--accent-ink);
  background: var(--accent-soft);
  padding: 6px 14px 6px 10px;
  border-radius: 999px;
}
.hero__status-dot {
  width: 5px;
  height: 5px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--accent-ink);
  animation: hero-status-pulse 2.6s ease-in-out infinite;
}
@keyframes hero-status-pulse {
  0%, 100% { box-shadow: 0 0 0 3px var(--accent-soft); }
  50% { box-shadow: 0 0 0 6px transparent; }
}

/* console mockup */
.hero__console { perspective: 1400px; }
.console {
  --accent-ink: #FF7A52; /* always-dark surface: stay bright regardless of page theme */
  box-shadow: 0 40px 80px -20px rgba(14, 16, 22, 0.45);
  transform: rotateY(-6deg) rotateX(2deg);
  transition: transform 0.6s var(--ease);
  border-radius: var(--radius); /* shadow needs the same radius to sit flush */
  /* Own compositing layer: the hover tilt below used to force a repaint of
     the whole hero on hover, visibly stalling the hex-field animation. */
  will-change: transform;
}
/* Clipping (overflow:hidden + border-radius) lives on its own, non-rotated
   element rather than the 3D-transformed .console: some browsers rasterize
   a rounded clip sloppily when it sits directly on a tilted layer, showing a
   thin colored fringe (like chromatic aberration) along the curve. */
.console__clip {
  background: var(--console);
  border-radius: var(--radius);
  overflow: hidden;
}
.console__mock { display: block; width: 100%; height: auto; }
.hero__console:hover .console { transform: rotateY(-2deg) rotateX(0deg); }

@media (max-width: 920px) {
  .hero { grid-template-columns: 1fr; padding-top: 130px; }
  .console { transform: none; }
}

/* =========================================================
   Problem → Lösung (ein gepinnter Übergang)
   ========================================================= */
/* Problem and solution are one section, not two: the four costs and the four
   answers occupy the same four places (one .cardflip per place, showing one
   side or the other), so the page can show the second as a change of state
   rather than as a new list. The mark sits in the centre of both, the one
   element that persists through the change.

   Nothing here is scroll-scrubbed any more — every step is a threshold
   trigger (js/main.js decides only WHEN, never HOW FAR) plus a fixed-time
   CSS transition/animation, the same principle for the section-wide reveal
   (.morph.is-solved) as for each card's own flip (.cardflip.is-flipped).
   That's deliberate: a value bound continuously to scroll position means
   every in-between value is a reachable resting frame — two overlapping
   headlines mid-crossfade, a half-open eye — and scrolling can then get
   "thrown back" by a stabilising snap. A threshold trigger has no such
   in-between: once crossed, the animation plays out on its own clock
   regardless of whether scrolling continues, stops, or reverses. */
.morph {
  background: var(--morph-problem-bg);
  transition: background 0.6s var(--ease);
}
.morph.is-solved { background: var(--morph-solution-bg); }

/* Extra travel (vs. a plain 100vh-equivalent) buys room at the end of the
   sequence for .morph__viewport to gently release its sticky lock and let
   any remaining height (see below) scroll into view normally, instead of
   running out of pin mid-reveal. */
.morph__pin { height: 280vh; position: relative; }
.morph__viewport {
  position: sticky;
  top: 0;
  /* min-height, not a hard height: the stage should never need to clip, but
     on the rare viewport where even the room freed by retracting the
     headline (below) isn't quite enough, the box is allowed to grow taller
     than one screen — sticky then releases near the end of .morph__pin's
     travel and the surplus scrolls into view like any normal block growing
     taller than expected, instead of being cut off. */
  min-height: 100vh;
  max-width: var(--container);
  margin: 0 auto;
  padding: calc(var(--nav-h) + clamp(16px, 3vh, 32px)) var(--edge) clamp(16px, 3vh, 32px);
  display: flex;
  flex-direction: column;
  justify-content: safe center;
  gap: clamp(20px, 4vh, 48px);
}
/* Retracts once the cards start flipping (js toggles .morph.is-retracted) —
   two rows of cards (up to 375px each once flipped) plus a multi-line
   headline plus the hexagon simply don't fit inside one screen's height at
   a lot of common window sizes (a 24" 16:9 monitor's usable height
   included), and neither smaller text nor less content was an option.
   Sliding the (by then already-read) headline out of the way instead hands
   the hub the full viewport for the part of the sequence that actually
   needs it. */
.morph__head {
  display: grid;
  overflow: hidden;
  max-height: 400px;
  opacity: 1;
  transform: translateY(0);
  transition: max-height 0.5s var(--ease), opacity 0.4s var(--ease), transform 0.5s var(--ease);
}
.morph.is-retracted .morph__head {
  max-height: 0;
  opacity: 0;
  transform: translateY(-40px);
}
.morph__copy {
  grid-area: 1 / 1;
  max-width: 62ch;
  transition: opacity 0.3s var(--ease), transform 0.3s var(--ease);
}
.morph__copy h2 {
  /* Pure vh, not the shared --text-headline token: this section is only
     ever pinned at 901px+ width (see the unpin media query below), so width
     was never the constraint here — height is, and a wide-but-short window
     (common on laptops with the browser not maximized vertically) would
     otherwise keep sizing off vw and stay large regardless of how little
     vertical room is actually available. */
  font-size: clamp(24px, 5.5vh, 40px);
  max-width: 22ch;
  overflow-wrap: break-word;
  margin-bottom: 16px;
}
.morph__copy p { font-size: var(--text-lg); max-width: 58ch; }
/* Threshold-triggered (js toggles .morph.is-solved), not scroll-scrubbed:
   two overlapping text blocks look unreadable at ANY in-between opacity, no
   matter how short the crossfade window — a one-off, fast swap avoids that
   double-exposure entirely instead of just shortening it. */
.morph__copy--problem { opacity: 1; transform: translateY(0); color: var(--morph-problem-ink); }
.morph__copy--problem p { color: var(--morph-problem-ink-soft); }
.morph__copy--solution { opacity: 0; transform: translateY(10px); color: var(--morph-solution-ink); }
.morph__copy--solution p { color: var(--morph-solution-ink-soft); }
.morph.is-solved .morph__copy--problem { opacity: 0; transform: translateY(-10px); }
.morph.is-solved .morph__copy--solution { opacity: 1; transform: translateY(0); }

/* Must not grow: with flex: 1 the stage swallowed the whole remaining
   viewport height and centred the diagram inside it, tearing a gap open
   between heading and cards. Head and stage now stay one block, which the
   viewport's justify-content centres as a whole. */
.morph__stage {
  position: relative;
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 100%;
}
/* Chaos-to-order hex grid, drawn by js/morph-hexfield.js — jittered/rotated
   while the problem holds, settling into an aligned grid as the solution
   takes over. Absolutely positioned within .morph__stage (relative), not
   grid-stacked like .hub/.morph__mark: a canvas with no HTML width/height
   attributes yet defaults to a 300×150 (2:1) intrinsic size, and
   grid-area:1/1 would make it a sizing input to the stage's auto-height row
   — absolute positioning takes it out of that calculation entirely, it just
   fills whatever height the real content settles the stage to. */
.morph__hexfield {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: -1;
  pointer-events: none;
  display: block;
  mask-image: radial-gradient(ellipse 62% 62% at 50% 50%, #000 55%, transparent 100%);
  -webkit-mask-image: radial-gradient(ellipse 62% 62% at 50% 50%, #000 55%, transparent 100%);
}

/* The mark is the one element that persists through the change: the ring
   stays put (only a scale pulse marks the trigger), the eye blends in
   time-delayed and grows from its centre — Ring+Auge share one colour that
   goes from white (problem, on the dark ground) to the brand orange
   (solution) in the same step. */
.morph__mark {
  grid-area: 1 / 1;
  width: clamp(240px, 38vh, 420px);
  pointer-events: none;
  z-index: 1;
}
.mark {
  display: block;
  width: 100%;
  height: auto;
  color: #f6f4ee;
  transition: color 0.6s var(--ease);
}
.morph.is-solved .mark { color: var(--accent-logo); }
.mark__ring,
.mark__brow,
.mark__pupil { fill: currentColor; }
.mark__ring { transform-box: fill-box; transform-origin: center; }
.morph.is-solved .mark__ring { animation: ring-explode 0.7s var(--ease); }
@keyframes ring-explode {
  0% { transform: scale(1); }
  45% { transform: scale(1.16); }
  100% { transform: scale(1); }
}
/* Reveal wrapper around brow+eye: opacity/scale grow-in, delayed so the
   ring's pulse reads first and the eye "wakes up" a beat after. */
.mark__eyegroup {
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  transform: scale(0.55);
  transition: opacity 0.5s var(--ease) 0.25s, transform 0.5s var(--ease) 0.25s;
}
.morph.is-solved .mark__eyegroup { opacity: 1; transform: scale(1); }
/* Only the pupil moves — glances up into the gap under the brow while shut
   (a fixed −12px bias), blending back to centre and handing off to
   pointer-follow once --open reaches 1. Follow and blink sit on two nested
   elements because they drive the same transform property at very
   different speeds. js/main.js still sets --open (0/1, not continuous) —
   this transition is what turns that flip into a smooth blend. */
.mark__eye {
  transform-box: fill-box;
  transform-origin: center;
  transform: translate(
    calc(var(--px, 0px) * var(--open, 1)),
    calc(var(--py, 0px) * var(--open, 1) + (var(--open, 1) - 1) * 12px)
  );
  transition: transform 0.5s var(--ease);
}
.mark__pupil {
  transform-box: fill-box;
  transform-origin: center;
  transition: transform 0.11s ease-in-out;
}
.mark__pupil.is-blinking { transform: scaleY(0.08); }

.hub {
  grid-area: 1 / 1;
  width: 100%;
  display: grid;
  grid-template-columns: 1fr minmax(150px, 190px) 1fr;
  grid-template-rows: auto auto;
  gap: clamp(28px, 5vh, 56px) clamp(24px, 4vh, 48px);
  align-items: center;
}
/* justify-self docks each card to the outer edge of its column instead of
   stretching the (fairly wide) 1fr column — otherwise the card reaches
   visually into the mark, since the mark's real size (up to 190px here,
   more on the hero-scale hexfield below) can exceed the narrow middle
   spacer column. */
.slot--1 { grid-column: 1; grid-row: 1; text-align: left; justify-self: start; }
.slot--2 { grid-column: 3; grid-row: 1; text-align: right; justify-self: end; }
.slot--3 { grid-column: 1; grid-row: 2; text-align: left; justify-self: start; }
.slot--4 { grid-column: 3; grid-row: 2; text-align: right; justify-self: end; }

/* The outer (.cardflip) turns once, scroll-triggered, from the problem side
   to the solution side — real 3D, one flip. The problem side's own
   title↔text reveal on hover/focus is deliberately NOT a second, nested 3D
   flip (see .card__text below): Safari and Firefox don't reliably hide the
   backface of a 3D-rotated element when it sits inside ANOTHER 3D
   (preserve-3d) context — the inner flip broke backface-visibility on the
   outer one, showing a mirrored ghost of whichever face should have been
   hidden. A single, non-nested 3D context per card is the one combination
   that's actually safe cross-browser.

   Height is deliberately two-tier rather than uniform: the problem side is
   a compact title chip (--problem-h), and only grows to the height the
   solution boxes actually need (--solution-h, per slot) once it flips —
   otherwise the title would sit as a tiny chip inside a mostly-empty shell.
   The height change travels as its own transition alongside the turn. */
.cardflip {
  --problem-h: 168px;
  --solution-h: 300px;
  position: relative;
  width: 300px;
  -webkit-perspective: 1400px;
  perspective: 1400px;
  height: var(--problem-h);
  transition: height 0.5s cubic-bezier(0.77, 0, 0.175, 1);
}
.cardflip.is-flipped { height: var(--solution-h); }
.cardflip.is-flipped .cardflip__inner { transform: rotateY(180deg); }
.slot--3 .cardflip { --solution-h: 375px; }
/* Builds up continuously while scrolling toward this card's own flip
   trigger — without it the wait before each flip would sit completely
   dead, several wheel notches with no visible reaction before it fires. */
.cardflip::after {
  content: '';
  position: absolute;
  inset: -10px;
  border-radius: 14px;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.55), 0 0 26px 6px rgba(255, 255, 255, 0.6);
  opacity: calc(var(--anticipate, 0) * 0.4);
  pointer-events: none;
}
/* Short, bright flash right at the flip trigger — layers on top of the
   continuous --anticipate glow above as a one-shot @keyframes animation. */
.cardflip.is-flashing::after { animation: cardflip-flash 0.6s ease-out; }
@keyframes cardflip-flash {
  0% { opacity: 0; }
  35% { opacity: 1; }
  100% { opacity: 0; }
}
.cardflip__inner {
  position: relative;
  width: 100%;
  height: 100%;
  -webkit-transform-style: preserve-3d;
  transform-style: preserve-3d;
  /* Fixed-time turn, not scroll-scrubbed: the scroll only decides WHEN
     .cardflip gets is-flipped; how the turn itself looks is entirely this
     transition's job, independent of scroll speed. */
  transition: transform 0.5s cubic-bezier(0.77, 0, 0.175, 1);
}
.cardflip__face {
  position: absolute;
  inset: 0;
  /* Unprefixed backface-visibility alone is unreliable in Safari/Firefox —
     without -webkit-, the rotated-away face can stay visible (mirrored) instead of
     hiding. */
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.slot--2 .cardflip__face, .slot--4 .cardflip__face { align-items: flex-end; }
/* Visible card shell instead of free-floating text — bare text doesn't
   read as something you can hover. Colours follow .morph.is-solved rather
   than the page theme (at p=0, before any scroll, this card is the first
   thing shown, still on the dark ground). */
/* visibility, not just backface-visibility: Firefox doesn't reliably cull a
   rotated-away face by backface-visibility alone (a known, long-standing
   engine bug, not fixable by vendor-prefixing) — the mirrored problem title
   was bleeding through behind the solution content. Delaying the visibility
   step to half the flip's duration keeps it looking like a natural 3D cull
   (face "disappears" as it turns edge-on) rather than an instant cut, while
   no longer depending on the browser to get backface culling right at all. */
.cardflip__face--problem {
  color: var(--morph-problem-ink);
  border: 1px solid var(--morph-problem-line);
  border-radius: 16px;
  padding: 20px 24px;
  background: rgba(255, 255, 255, 0.04);
  cursor: pointer;
  visibility: visible;
  transition: background 0.3s var(--ease), border-color 0.3s var(--ease), box-shadow 0.3s var(--ease), color 0.3s var(--ease), visibility 0s linear 0.25s;
}
.cardflip.is-flipped .cardflip__face--problem { visibility: hidden; }
.morph.is-solved .cardflip__face--problem {
  color: var(--morph-solution-ink);
  border-color: var(--morph-solution-line);
  background: rgba(18, 20, 26, 0.03);
}
.cardflip__face--problem:hover,
.cardflip__face--problem:focus-within {
  background: rgba(255, 255, 255, 0.08);
  border-color: rgba(255, 255, 255, 0.32);
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.22);
}
.morph.is-solved .cardflip__face--problem:hover,
.morph.is-solved .cardflip__face--problem:focus-within {
  background: rgba(18, 20, 26, 0.06);
  border-color: rgba(18, 20, 26, 0.28);
}
.cardflip__face--solution {
  color: var(--morph-solution-ink);
  transform: rotateY(180deg);
  visibility: hidden;
  transition: visibility 0s linear 0.25s;
}
.cardflip.is-flipped .cardflip__face--solution { visibility: visible; }

.cardflip h3 { font-size: var(--text-title-sm); margin-bottom: 8px; }
/* Simple reveal, not a flip: collapsed to 0 height by default, fades/grows
   open on hover/focus of the card. Capped at 4 lines when open rather than
   growing freely — fits reliably inside the compact problem-card height
   (--problem-h) without the card needing to grow further for it. */
.card__text {
  font-size: var(--text-body-sm);
  line-height: 1.55;
  color: var(--morph-problem-ink-faint);
  margin: 8px 0 0;
  max-width: 32ch;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 4;
  line-clamp: 4;
  opacity: 0;
  max-height: 0;
  overflow: hidden;
  transition: opacity 0.3s var(--ease), max-height 0.35s var(--ease), color 0.3s var(--ease);
}
.cardflip__face--problem:hover .card__text,
.cardflip__face--problem:focus-within .card__text {
  opacity: 1;
  max-height: 160px;
}
.morph.is-solved .card__text { color: var(--morph-solution-ink-faint); }

.cardflip__face--solution h3 { margin-bottom: clamp(8px, 1.5vh, 14px); }
/* Boxes with the explanation written directly in, not a hover tooltip —
   the previous data-info pill only revealed this text on hover, which
   doesn't fit a page built to stay readable without interaction. */
.card__boxes {
  display: flex;
  flex-direction: column;
  gap: 8px;
  list-style: none;
  margin: 0;
  padding: 0;
}
.slot--2 .card__boxes, .slot--4 .card__boxes { align-items: flex-end; }
.card__boxes li {
  border: 1px solid var(--morph-solution-line);
  border-radius: 8px;
  padding: 8px 12px;
  background: rgba(18, 20, 26, 0.03);
  max-width: 34ch;
  text-align: left;
}
.card__boxes strong {
  display: block;
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--morph-solution-ink);
  margin-bottom: 2px;
}
.card__boxes span {
  display: block;
  font-size: var(--text-2xs);
  line-height: 1.4;
  color: var(--morph-solution-ink-faint);
}

/* Signals that there's more to scroll through while the section is pinned
   — without it, nothing tells a visitor the page hasn't just stalled.
   Fades in at the very start and out just before the pin releases. */
.scrollcue {
  position: absolute;
  left: 50%;
  bottom: 10px;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  font-size: 11px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.75);
  opacity: 0;
  transition: color 0.3s var(--ease);
  pointer-events: none;
}
.morph.is-solved .scrollcue { color: rgba(18, 20, 26, 0.55); }
.scrollcue svg { width: 18px; height: 18px; animation: scrollcue-bounce 1.6s ease-in-out infinite; }
@keyframes scrollcue-bounce {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(6px); }
}

@media (prefers-reduced-motion: reduce) {
  .scrollcue svg { animation: none; }
  .card__text { transition: none; }
}

@media (max-width: 1100px) {
  .hub { grid-template-columns: 1fr minmax(120px, 150px) 1fr; }
}

/* Unpin wherever the transition cannot be shown honestly: too narrow, too
   short, or motion turned down.
   Unpinned layout: same technique the section used before the redesign —
   nothing here should look or behave any different from that. Both states
   stand as two full, separate blocks in document order (all four problem
   items, then all four solution items), following the page's own light/dark
   theme rather than the fixed night/day palette (that duality only reads as
   intentional while the two states crossfade live into each other; stacked
   as plain static blocks it just looks like the wrong theme stuck on one of
   them). `display: contents` all the way down from .morph__head/.morph__pin
   to .cardflip__inner un-wraps every desktop-only flip/sticky wrapper, so
   the actual content boxes (.morph__copy, .cardflip__face, .morph__mark)
   become direct items of .morph's own grid — `order` then sequences them. */
@media (max-width: 900px), (max-height: 820px), (prefers-reduced-motion: reduce) {
  .morph {
    background: var(--paper);
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    padding: 110px var(--edge);
  }
  .morph__head,
  .morph__pin,
  .morph__viewport,
  .morph__stage,
  .hub,
  .slot,
  .cardflip,
  .cardflip__inner {
    display: contents;
  }
  .morph__hexfield { display: none; }
  .mark { color: var(--accent-logo); }
  .mark__ring { animation: none; }
  .mark__eyegroup { opacity: 1; transform: scale(1); transition: none; }

  .morph__copy {
    grid-area: auto;
    opacity: 1 !important;
    transform: none !important;
    color: var(--ink);
    max-width: none;
    order: 1;
  }
  .morph__copy--problem p,
  .morph__copy--solution p { color: var(--ink-soft); }
  .morph__copy--solution { order: 3; margin-top: 110px; }
  .morph__mark { order: 4; position: static; width: 112px; margin: 40px auto; }

  .cardflip::after { display: none; }
  .cardflip__face {
    grid-area: auto;
    position: static;
    transform: none !important;
    /* .cardflip never gets is-flipped here (JS bails out below the pinned
       breakpoint) — both faces need to show, stacked, not flip-hidden. */
    visibility: visible !important;
    width: auto;
    color: var(--ink);
    background: var(--paper-dim);
    border: none;
    border-radius: var(--radius);
    padding: 32px var(--edge);
    cursor: default;
  }
  /* Visual order among same-order items follows DOM order regardless of
     `display: contents`, i.e. slot 1 → 2 → 3 → 4 — so slot 1's face is
     always the first card in its group and gets the larger lead-in margin
     (matching the old layer's margin-top after the heading/mark above it);
     the rest just need list spacing between each other. */
  .cardflip__face--problem { order: 2; margin-top: 20px; }
  .slot--1 .cardflip__face--problem { margin-top: 56px; }
  .cardflip__face--solution { order: 5; margin-top: 20px; }
  .slot--1 .cardflip__face--solution { margin-top: 0; }
  .slot--2, .slot--4 { text-align: left; }
  .slot--2 .cardflip__face,
  .slot--4 .cardflip__face,
  .slot--2 .card__boxes,
  .slot--4 .card__boxes { align-items: flex-start; }

  /* The hover reveal is a desktop-only affordance — on mobile the text is
     simply always there, same as every other static block on this layout. */
  .card__text {
    color: var(--ink-soft);
    display: block;
    opacity: 1;
    max-height: none;
    -webkit-line-clamp: unset;
    line-clamp: unset;
    overflow: visible;
  }

  .card__boxes li { border-color: var(--line); background: transparent; }
  .card__boxes strong { color: var(--ink); }
  .card__boxes span { color: var(--ink-faint); }

  .scrollcue { display: none; }
}

/* =========================================================
   Status / roadmap
   ========================================================= */
/* Centred, unlike every other section: phases 4 and 5 run in parallel, and
   a fork only reads as a fork when it has a centre axis to fork from. The
   heading block follows the graphic so the centring looks decided, not
   accidental. */
.status {
  background: var(--paper-dim);
  padding: 88px var(--edge);
  text-align: center;
}
.status__inner {
  max-width: var(--container);
  margin: 0 auto;
}
.status__inner h2 { font-size: var(--text-headline); margin-bottom: 14px; }
.status__sub {
  color: var(--ink-soft);
  max-width: 54ch;
  margin: 0 auto 44px;
  font-size: var(--text-body);
}

.roadmap {
  --branch: 110px; /* half-width of the parallel fork */
  --dot: 15px;
  --gutter: 32px;
  max-width: 1100px;
  margin: 0 auto;
  text-align: left;
}

.roadmap__spine {
  position: relative;
  list-style: none;
  margin: 0;
  padding: 0;
}
.roadmap__spine:not(.roadmap__spine--end)::before {
  content: '';
  position: absolute;
  left: 50%;
  top: 7px;
  bottom: 0;
  width: 1px;
  margin-left: -0.5px;
  background: var(--line);
}
/* Scroll-linked fill, kept from the previous timeline: it only ever covers
   the phases that are actually done or running, so the movement cannot
   claim progress the project has not made. */
.roadmap__progress {
  position: absolute;
  left: 50%;
  top: 7px;
  bottom: 0;
  width: 1px;
  margin-left: -0.5px;
  background: var(--accent-logo);
  transform-origin: top;
  transform: scaleY(0);
}

.phase {
  position: relative;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: start;
  padding-bottom: 36px;
}
.roadmap__spine .phase > .phase__dot { grid-column: 2; }
.roadmap__spine .phase > .phase__body { grid-column: 3; padding-left: var(--gutter); }
.roadmap__spine--end .phase { padding-bottom: 0; }

.phase__dot {
  width: var(--dot);
  height: var(--dot);
  border-radius: 50%;
  background: var(--paper);
  border: 2px solid var(--ink-faint);
  margin-top: 6px; /* nudged down from the row's top edge to sit level with the first text line, not above it */
  position: relative;
  z-index: 1;
  transition: transform 0.25s var(--ease);
}
.phase--done .phase__dot { background: var(--accent-logo); border-color: var(--accent-logo); }
.phase--active .phase__dot {
  border-color: var(--accent-logo);
  animation: phase-pulse 2.6s ease-in-out infinite;
}
@keyframes phase-pulse {
  0%, 100% { box-shadow: 0 0 0 4px var(--accent-soft); }
  50% { box-shadow: 0 0 0 10px transparent; }
}
.phase:hover .phase__dot { transform: scale(1.3); }

.phase__meta {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--accent-ink);
  letter-spacing: 0.05em;
  margin-bottom: 8px;
}
.phase__body h3 { font-size: var(--text-title-sm); margin-bottom: 8px; }
.phase__body p {
  color: var(--ink-faint);
  font-size: var(--text-body-sm);
  line-height: 1.55;
  margin-bottom: 12px;
  max-width: 42ch;
}
.phase__state {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: 0.06em;
  padding: 4px 12px;
  border-radius: 999px;
  border: 1px solid var(--line);
  color: var(--ink-faint);
}
.phase--done .phase__state { border-color: var(--accent-ink); color: var(--accent-ink); }
.phase__state--active {
  background: var(--accent-ink);
  border-color: var(--accent-ink);
  color: var(--paper);
}

/* Fork and merge are drawn 1:1 (viewBox width equals CSS width), so the
   curves keep their shape and the branch ends land exactly on the two
   branch lines below. */
.roadmap__fork,
.roadmap__merge {
  display: block;
  width: calc(var(--branch) * 2 + 20px);
  height: 60px;
  margin: 0 auto;
  overflow: visible;
}
.roadmap__fork path,
.roadmap__merge path {
  fill: none;
  stroke-width: 1;
}
.rm-line { stroke: var(--line); }
.rm-fill { stroke: var(--accent-logo); }

.roadmap__parallel {
  position: relative;
  z-index: 0; /* pins a stacking context here so .phase__dot's z-index:1 is
    guaranteed to paint over the branch lines, instead of both competing in
    whatever ancestor context happens to be the nearest one that sets one. */
  display: grid;
  grid-template-columns: 1fr calc(var(--branch) * 2) 1fr;
  align-items: start;
  padding-bottom: 16px;
}
.roadmap__parallel::before,
.roadmap__parallel::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1px;
  background: var(--line);
}
.roadmap__parallel::before { left: calc(50% - var(--branch)); }
.roadmap__parallel::after { left: calc(50% + var(--branch)); }
/* Accent overlay on the two branch lines, filled by the same scroll
   fraction that drives spine, fork and merge — so the orange runs the whole
   roadmap instead of stopping where the spine ends. */
.roadmap__branchfill {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1px;
  background: var(--accent-logo);
  transform-origin: top;
  transform: scaleY(0);
}
.roadmap__branchfill--l { left: calc(50% - var(--branch)); }
.roadmap__branchfill--r { left: calc(50% + var(--branch)); }

.phase--branch { display: block; padding-bottom: 0; }
.phase--left { grid-column: 1; text-align: right; padding-right: var(--gutter); }
.phase--right { grid-column: 3; text-align: left; padding-left: var(--gutter); }
.phase--left .phase__body p { margin-left: auto; }
.phase--branch .phase__dot { position: absolute; top: 6px; margin-top: 0; }
.phase--left .phase__dot { right: calc(var(--dot) / -2); }
.phase--right .phase__dot { left: calc(var(--dot) / -2); }

/* Below the fork's comfortable width the whole roadmap becomes one left
   aligned run. */
@media (max-width: 820px) {
  .status { text-align: left; }
  .status__sub { margin-left: 0; margin-right: 0; }
  .roadmap { --branch: 7px; }
  .roadmap__branchfill--r { display: none; }
  .roadmap__branchfill--l { left: 7px; }
  .roadmap__fork,
  .roadmap__merge { display: none; }
  .roadmap__spine:not(.roadmap__spine--end)::before,
  .roadmap__progress { left: 7px; margin-left: 0; }

  .phase { grid-template-columns: auto 1fr; }
  .roadmap__spine .phase > .phase__dot { grid-column: 1; }
  .roadmap__spine .phase > .phase__body { grid-column: 2; padding-left: 24px; }

  .roadmap__parallel { display: block; }
  .roadmap__parallel::before { left: 7px; }
  .roadmap__parallel::after { display: none; }

  .phase--branch {
    display: grid;
    grid-template-columns: auto 1fr;
    text-align: left;
    padding: 0 0 36px;
  }
  .phase--branch:last-child { padding-bottom: 0; }
  .phase--left,
  .phase--right { padding-left: 0; padding-right: 0; }
  .phase--branch .phase__dot {
    position: static;
    grid-column: 1;
    margin-top: 6px;
  }
  .phase--branch .phase__body {
    grid-column: 2;
    padding-left: 24px;
    text-align: left;
  }
}

/* =========================================================
   Mitwirkung + Kontakt (CTA-Band)
   ========================================================= */
/* One loud surface, one loud action. The former three equal cards gave the
   visitor no way to tell what to do first, so the form now carries the
   section and the two remaining paths are visibly subordinate. */
.cta {
  background: linear-gradient(160deg, var(--accent) 0%, var(--accent-strong) 100%);
  color: #fff;
  padding: 140px var(--edge);
}
.cta__inner {
  max-width: var(--container);
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 72px;
  align-items: start;
}
.cta__inner h2 { font-size: var(--text-headline-lg); margin-bottom: 18px; }
.cta__intro {
  color: rgba(255,255,255,0.82);
  font-size: var(--text-lg);
  max-width: 46ch;
  margin-bottom: 40px;
}

.ways { display: flex; flex-direction: column; gap: 14px; }
.way {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255,255,255,0.16);
  border-radius: var(--radius);
  padding: 20px 22px;
  transition: transform 0.3s var(--ease), background 0.3s var(--ease), border-color 0.3s var(--ease);
}
.way:hover {
  transform: translateY(-3px);
  background: rgba(255,255,255,0.12);
  border-color: rgba(255,255,255,0.32);
}
.way__icon {
  flex: none;
  width: 20px;
  height: 20px;
  margin-top: 2px;
  color: rgba(255,255,255,0.85);
  transition: transform 0.3s var(--ease);
}
.way__icon svg { width: 100%; height: 100%; display: block; }
.way:hover .way__icon { transform: scale(1.12); }
.way h3 { font-size: var(--text-lg); color: #fff; margin-bottom: 6px; }
.way p { font-size: var(--text-sm); color: rgba(255,255,255,0.72); line-height: 1.5; margin-bottom: 10px; }

.way__link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--text-sm);
  font-weight: 600;
  color: #fff;
}
/* The arrow used to twitch 3px on hover. Now it travels, and a short line
   grows out behind it, so the movement reads as the arrow drawing a path
   rather than as a nudge. One gesture, two parts. */
.way__trail {
  display: block;
  width: 14px;
  height: 1.5px;
  border-radius: 999px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.35s var(--ease);
}
.way__link svg { transition: transform 0.35s var(--ease); }
.way:hover .way__trail { transform: scaleX(1); }
.way:hover .way__link svg { transform: translateX(8px); }

/* Form on the accent ground, as DESIGN.md's input-field component always
   specified — it drifted to paper tokens while it lived in the light
   Vertrauen section. */
/* The panel darkens its ground instead of lightening it. White on the light
   end of the gradient (#EB5C38) only reaches 3.43:1 — below AA — and the form
   put far more small text on that surface than the section had before. A 28%
   ink tint lifts it to 5.57:1, and the fields darken once more on top. */
.cta__form {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
  background: rgba(18, 20, 26, 0.28);
  border: 1px solid rgba(255,255,255,0.2);
  border-radius: var(--radius);
  padding: 30px;
}
.cta__form-title { grid-column: span 2; font-size: var(--text-title); color: #fff; }
.cta__form-hint {
  grid-column: span 2;
  margin-top: -6px;
  font-size: var(--text-xs);
  color: rgba(255,255,255,0.9);
  line-height: 1.5;
}
.cta__field { display: flex; flex-direction: column; gap: 8px; }
.cta__field--wide, .cta__field:last-of-type { grid-column: span 2; }
.cta__field label {
  font-size: var(--text-xs);
  color: rgba(255,255,255,0.9);
  font-family: var(--font-mono);
}
.cta__field input, .cta__field select, .cta__field textarea {
  background: rgba(18, 20, 26, 0.3);
  border: 1px solid rgba(255,255,255,0.24);
  border-radius: 10px;
  padding: 12px 14px;
  color: #fff;
  font-family: var(--font-body);
  font-size: var(--text-body-sm);
  resize: vertical;
  transition: border-color 0.2s var(--ease), background 0.2s var(--ease);
}
.cta__field input:focus, .cta__field textarea:focus {
  outline: none;
  background: rgba(18, 20, 26, 0.42);
  border-color: rgba(255,255,255,0.7);
}
.cta__field input::placeholder, .cta__field textarea::placeholder { color: rgba(255,255,255,0.72); }
.cta__consent {
  grid-column: span 2;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-size: var(--text-xs);
  color: rgba(255,255,255,0.9);
  line-height: 1.4;
}
.cta__consent input { margin-top: 2px; }
.cta__form .btn { grid-column: span 2; justify-content: center; margin-top: 6px; }
.cta__note {
  grid-column: span 2;
  font-size: var(--text-xs);
  color: rgba(255,255,255,0.9);
  text-align: center;
}
.form-feedback:empty { display: none; }
.form-feedback.is-success { color: #7be0a8; }
.form-feedback.is-error { color: #ff9c8a; }
/* Umfrage-Seite steht auf --paper statt auf der dunklen Kontakt-Karte,
   braucht daher die theme-abhängigen Tokens statt der Fixfarben oben. */
.survey-page .form-feedback.is-success { color: var(--success-ink); }
.survey-page .form-feedback.is-error { color: var(--accent-ink); }

.visually-hidden-field {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

@media (max-width: 980px) {
  .cta__inner { grid-template-columns: 1fr; gap: 48px; }
  .cta__intro { max-width: none; }
}

@media (max-width: 640px) {
  .cta__form { grid-template-columns: 1fr; padding: 24px; }
  .cta__form-title, .cta__form-hint,
  .cta__field--wide, .cta__field:last-of-type,
  .cta__consent, .cta__form .btn, .cta__note { grid-column: span 1; }
}

/* =========================================================
   Umfrage
   ========================================================= */
/* Full-bleed band with the larger headline step: this is the page's primary
   ask (the hero leads with it), and as a plain paper block between two other
   paper blocks it read as a footnote rather than as the invitation it is. */
.umfrage {
  padding: 160px var(--edge);
  background: var(--paper-dim);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.umfrage__inner { max-width: var(--container); margin: 0 auto; }
.umfrage__inner h2 { font-size: var(--text-headline); margin-bottom: 14px; }
.umfrage__sub { color: var(--ink-soft); font-size: var(--text-body); max-width: 50ch; margin-bottom: 56px; }
.umfrage__inner--teaser { text-align: center; max-width: 780px; }
.umfrage__inner--teaser h2 {
  font-size: var(--text-headline-lg);
  max-width: 18ch;
  margin: 0 auto 20px;
}
.umfrage__inner--teaser .umfrage__sub {
  font-size: var(--text-xl);
  max-width: 46ch;
  margin: 0 auto 44px;
}
.umfrage__inner--teaser .btn { margin: 0 auto; }

.survey__options {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px 20px;
}
.survey__options label {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: var(--text-body-sm);
  color: var(--ink-soft);
  cursor: pointer;
}
.survey__options input { accent-color: var(--accent-ink); width: 16px; height: 16px; flex-shrink: 0; }
.survey__contact { display: flex; gap: 14px; flex-wrap: wrap; }
.survey__contact input {
  flex: 1 1 220px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 12px 14px;
  font-family: var(--font-body);
  font-size: var(--text-body-sm);
  color: var(--ink);
}
@media (max-width: 640px) {
  .survey__options { grid-template-columns: 1fr; }
}

/* =========================================================
   Umfrage — standalone wizard page (umfrage.html)
   ========================================================= */
.survey-page {
  padding: 160px var(--edge) 80px;
  max-width: var(--container);
  margin: 0 auto;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
}
.survey-page__inner {
  max-width: 640px;
  width: 100%;
  margin: 0 auto;
  flex: 1;
  display: flex;
  flex-direction: column;
}

.survey-progress { display: flex; align-items: center; gap: 16px; margin-bottom: 64px; }
.survey-progress__track {
  flex: 1;
  height: 4px;
  border-radius: 999px;
  background: var(--line);
  overflow: hidden;
}
.survey-progress__fill {
  display: block;
  width: 100%;
  height: 100%;
  background: var(--accent-ink);
  border-radius: 999px;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.4s var(--ease);
}
.survey-progress__label {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--ink-faint);
  white-space: nowrap;
}

.survey-steps { position: relative; flex: 1; }
.survey-step {
  display: none;
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.4s var(--ease), transform 0.4s var(--ease);
  scroll-margin-top: 120px;
}
.survey-step.is-active { display: block; }
.survey-step.is-active.is-shown { opacity: 1; transform: translateY(0); }
.survey-step__eyebrow {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--accent-ink);
  letter-spacing: 0.06em;
  margin-bottom: 18px;
}
.survey-step h1 { font-size: var(--text-headline); margin-bottom: 36px; max-width: 22ch; }

.survey__options--stack { display: flex; flex-direction: column; gap: 12px; }
.survey__options--stack label {
  padding: 16px 18px;
  border: 1px solid var(--line);
  border-radius: 10px;
  transition: border-color 0.2s var(--ease), background 0.2s var(--ease);
}
.survey__options--stack label:hover { border-color: var(--accent-ink); }
.survey__options--stack label:has(input:checked) {
  border-color: var(--accent-ink);
  background: var(--accent-soft);
}

.survey__contact--stack { flex-direction: column; }
.survey__contact--stack input { flex: none; width: 100%; }

.survey__option--other { flex-wrap: wrap; }
.survey__options input.survey__other-input {
  width: auto;
  height: auto;
  flex: 1 1 160px;
  min-width: 0;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 8px 12px;
  font-family: var(--font-body);
  font-size: var(--text-body-sm);
  color: var(--ink);
}

.survey-nav { display: flex; justify-content: space-between; gap: 16px; margin-top: 48px; }
.survey-nav .btn { min-width: 132px; justify-content: center; }
.survey-nav .btn[data-hidden] { visibility: hidden; }

.survey__feedback { margin-top: 16px; font-size: var(--text-xs); }

.survey-step--thanks { text-align: center; }
.survey-thanks__mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent-ink);
  margin-bottom: 28px;
}
.survey-step--thanks h1 { max-width: none; }
.survey-step--thanks p {
  color: var(--ink-soft);
  max-width: 40ch;
  margin: 0 auto 32px;
}
.survey-step--thanks .btn { margin: 0 auto; }

@media (max-width: 640px) {
  .survey-page { padding-top: 130px; }
  .survey-nav { flex-direction: column-reverse; }
  .survey-nav .btn { width: 100%; }
}

/* =========================================================
   FAQ
   ========================================================= */
.faq { background: var(--paper-dim); padding: 140px var(--edge); }
.faq__inner { max-width: 780px; margin: 0 auto; }
.faq__inner h2 { font-size: var(--text-headline); margin-bottom: 48px; text-align: center; }

.faq__list { display: flex; flex-direction: column; gap: 1px; background: var(--line); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
.faq__item { background: var(--paper); padding: 22px 24px; }
.faq__item summary {
  cursor: pointer;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--text-lg);
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.faq__item summary::-webkit-details-marker { display: none; }
/* A drawn arrow rather than the former "+" glyph: the rest of the page uses
   authored SVG icons at a consistent stroke, and a text character in that
   slot never matched them. It flips a half turn on open, so the direction
   always names what the next click does. */
.faq__icon {
  flex-shrink: 0;
  color: var(--accent-ink);
  transition: transform 0.3s var(--ease);
}
.faq__item[open] .faq__icon { transform: rotate(180deg); }
.faq__item p {
  margin-top: 14px;
  font-size: var(--text-body-sm);
  color: var(--ink-faint);
  line-height: 1.6;
  max-width: 60ch;
}

/* =========================================================
   Warum wir
   ========================================================= */
.warum { padding: 140px var(--edge); max-width: var(--container); margin: 0 auto; }
.warum__inner { max-width: 640px; margin: 0 auto; }
.warum__text h2 { font-size: var(--text-headline); margin-bottom: 26px; }
.warum__text p {
  color: var(--ink-soft);
  font-size: var(--text-body);
  line-height: 1.62;
  max-width: 58ch;
}
.warum__text p + p { margin-top: 20px; }
/* The claim is the one place in this text that is brand voice rather than
   prose, so it carries the display face instead of an italic. */
.warum__claim {
  font-style: normal;
  font-family: var(--font-display);
  font-weight: 600;
  color: var(--accent-ink);
}

.warum__people {
  display: flex;
  flex-direction: column;
  gap: 5px;
  margin-top: 32px;
}
.warum__names { font-family: var(--font-display); font-weight: 600; font-size: var(--text-md); }
.warum__roles {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--ink-faint);
  letter-spacing: 0.04em;
}

/* =========================================================
   Footer
   ========================================================= */
.footer { background: var(--console); color: var(--console-text-dim); padding: 80px var(--edge) 32px; }
.footer__inner {
  max-width: var(--container);
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1.6fr 1fr 1fr;
  gap: 40px;
  padding-bottom: 60px;
}
.footer__col { display: flex; flex-direction: column; gap: 14px; }
.footer__col a { font-size: var(--text-sm); color: var(--console-text-dim); transition: color 0.2s; }
.footer__col a:hover { color: var(--console-text); }
.footer__col a[href^="mailto:"],
.footer__col a[target="_blank"] { display: inline-flex; align-items: center; gap: 8px; }
.footer__col-title {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.4);
  margin-bottom: 4px;
}
.footer__logo { display: inline-flex; }
.footer__logo img { height: 28px; width: auto; }
.footer__tagline { font-size: var(--text-sm); line-height: 1.5; margin-top: 4px; }

.footer__bottom {
  max-width: var(--container);
  margin: 0 auto;
  padding-top: 28px;
  border-top: 1px solid var(--console-line);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.footer__legal { display: flex; flex-wrap: wrap; gap: 24px; font-size: var(--text-sm); }
.link--underline { text-decoration: underline; }
.footer__legal a { color: var(--console-text-dim); transition: color 0.2s; }
.footer__legal a:hover { color: var(--console-text); }
.footer .lang-switch__option { color: rgba(255,255,255,0.45); }
.footer .lang-switch__option.is-active { color: var(--console-text); }
.footer .lang-switch__sep { color: rgba(255,255,255,0.2); }
.footer .theme-toggle { border-color: rgba(255,255,255,0.2); color: var(--console-text-dim); }
@media (hover: hover) {
  .footer .theme-toggle:hover { border-color: var(--console-text); color: var(--console-text); }
}

@media (max-width: 760px) {
  .footer__inner { grid-template-columns: 1fr; gap: 36px; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  [data-reveal] {
    transition: none !important;
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
}
