/* CtxVault Landingpage — hand-written vanilla CSS (no build step, no CDN).
   Apple-dark redesign per docs/superpowers/specs/2026-07-18-ctxvault-landing-apple-redesign.md. */

:root {
  /* Canvas & Surfaces — Elevation NUR über Tonwert, nie Schatten */
  --bg: #0a0a0b;            /* Pitch Black Canvas, minimal angehoben fuer die Dot-Matrix-Textur (C1) */
  --surface-1: #161617;     /* Deep Graphite (Nav-Fill, subtile Hebung) */
  --card: #1d1d1f;          /* Space Gray Cards */
  --surface-3: #333336;     /* Storm Gray (Hover/Controls) */
  --light-band: #f5f5f7;    /* Ghost White — die EINE helle Sektion */
  --light-card: #ffffff;

  /* Text */
  --text: #f5f5f7;
  --text-secondary: #86868b;    /* Cool Gray */
  --text-on-light: #1d1d1f;     /* Space Gray auf Ghost White */
  --text-on-light-secondary: #6e6e73;

  /* Akzent — EIN Blau-System, sonst nichts */
  --cta: #0071e3;           /* Interactive Blue: NUR gefüllte Primär-CTAs */
  --cta-hover: #0077ed;
  --link: #2997ff;          /* Vivid Blue: Textlinks/Akzente auf dark */
  --link-on-light: #0066cc; /* Highlight Blue: Links auf light */

  /* Form */
  --radius-card: 28px;      /* alle Cards/Container */
  --radius-pill: 999px;     /* alle Buttons/Pills */
  --radius-sm: 12px;        /* kleine Elemente (Code-Chips) */
  --max-width: 980px;       /* Apple-Content-Breite */

  --font: -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text", "Helvetica Neue", "Inter", "Segoe UI", sans-serif;
  --font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  --font-display: "Departure Mono", ui-monospace, "SF Mono", Menlo, monospace; /* C2: H1 only */

  /* Helper (additive, not part of the token replacement) */
  --hairline: rgba(255, 255, 255, 0.08);
  --hairline-on-light: rgba(0, 0, 0, 0.08);
  --window-shadow: rgba(0, 0, 0, .1) 0 0 0 1px, rgba(0, 0, 0, .4) 0 12px 24px -8px, rgba(0, 0, 0, .2) 0 8px 24px 0;

  /* F2 (chef-directed cursor spotlight): sane pre-JS/no-hover default — calm,
     roughly centered, echoes the C1 light-beam's top-center origin. Never a
     hole parked in a corner if JS never runs. */
  --mx: 50vw;
  --my: 38vh;
}

/* C2: self-hosted display font, H1 only. SIL OFL 1.1, Helena Zhang — see
   assets/fonts/DepartureMono-LICENSE.txt. Departure Mono's native metrics
   (unitsPerEm 550, ascent 550, descent -150 -> ~127% of em) run much taller
   than the ui-monospace/SF Mono fallback (~90%/22%), which would show up as
   a visible line-box jump in the H1 the instant font-display:swap hands off.
   ascent/descent-override pin the *used* layout metrics to the fallback's
   ballpark so the swap doesn't reflow. */
@font-face {
  font-family: "Departure Mono";
  src: url("fonts/DepartureMono-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  size-adjust: 100%;
  ascent-override: 90%;
  descent-override: 22%;
  line-gap-override: 0%;
}

* { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  color-scheme: dark;
}

body {
  position: relative;
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 17px;
  line-height: 1.47;
  letter-spacing: -0.016em;
  -webkit-font-smoothing: antialiased;
}

/* Canvas depth (C1): fixed light-beam vignette from top-center + a quiet dot-matrix
   grid, layered behind all content. .light-band (Pricing) sits on top and
   covers it for most of the section — no separate override needed there.
   A5: this stopped being a full/unconditional cover three commits later,
   when Z2 gave .light-band's own background a top/bottom fade (see
   .light-band::before below) — inside that ~56px fade band the dot/beam
   texture IS visible through the light section, by Z2's design, not a gap
   in this comment's original claim so much as a claim this file no longer
   makes elsewhere without qualification.
   z-index -2: this is the bottom-most layer, the dark F2 box (body::after,
   z-index -1) sits above it and is what actually gets masked.
   Z1 (chef-directed): dot opacity raised 0.035→0.09 so the texture actually
   reads instead of being near-invisible — real, measured (9.45% changed
   pixels at 1440px, 3.04% on a touch device with no cursor-hole). The
   light-beam's own opacity was also raised in that pass (0.045→0.08) but
   reverted (A2): a pixel-diff with the dots removed and the cursor
   spotlight neutralized, isolating the beam alone, found 0 changed pixels
   at 1440x900 (maxDelta=0) between the two values — a point-probe at the
   viewport's top-center, where the beam should be brightest, read
   min=10 max=10 mean=10 (flat #0a0a0b) for both old and new. Cause: body::
   after (z-index -1, below content but above this layer) is an opaque
   var(--bg) box; its mask is solid (#fff) across ~78%/70% of the viewport
   centered at 50% 40%, and the beam sits at 50% -8% — entirely under that
   opaque region. Changing the beam's opacity here is a no-op unless
   body::after's mask is opened above it too; not done, so left at its
   original 0.045. .light-band's background occludes both layers wherever
   it's fully opaque (unaffected by either value there) — everywhere except
   its own top/bottom fade band, see A5 above. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -2;
  pointer-events: none;
  background-image:
    radial-gradient(ellipse 900px 560px at 50% -8%, rgba(255, 255, 255, 0.045), rgba(255, 255, 255, 0) 70%),
    radial-gradient(rgba(255, 255, 255, 0.09) 1px, transparent 1.5px);
  background-size: 100% 100%, 5px 5px;
  background-repeat: no-repeat, repeat;
  background-position: 0 0, 0 0;
}

/* F2 (chef-directed): a solid --bg box sitting ABOVE the dot canvas (never
   below content), masked so it fades softly into the dots at its own edges
   instead of the hard-edged seam an opaque .hero background used to leave —
   and, on fine-pointer/hover devices with motion allowed, punches a soft
   cursor-follow hole so the dots show through wherever the mouse is. No
   box-shadow (Elevation-über-Tonwert rule) — masking IS the softness here. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: var(--bg);
  -webkit-mask-image: radial-gradient(ellipse 78% 70% at 50% 40%, #fff 60%, #fff 82%, transparent 100%);
  mask-image: radial-gradient(ellipse 78% 70% at 50% 40%, #fff 60%, #fff 82%, transparent 100%);
}

@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  body::after {
    mask-image:
      radial-gradient(circle 260px at var(--mx) var(--my), transparent 0%, transparent 35%, #fff 100%),
      radial-gradient(ellipse 78% 70% at 50% 40%, #fff 60%, #fff 82%, transparent 100%);
    mask-composite: intersect;
  }
}

img, video { max-width: 100%; display: block; }

a { color: inherit; text-decoration: none; }

h1, h2, h3 { text-wrap: balance; }

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

.wrap {
  max-width: var(--max-width);
  margin: 0 auto;
  padding-left: 24px;
  padding-right: 24px;
}

/* ---------- Nav ---------- */

.nav-sticky {
  position: sticky;
  top: 0;
  z-index: 50;
  background: transparent;
  border-bottom: 1px solid transparent;
  transition: background-color .3s ease, border-color .3s ease, backdrop-filter .3s ease;
}

.nav-sticky.is-scrolled {
  background: rgba(22, 22, 23, 0.8);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  backdrop-filter: saturate(180%) blur(20px);
  border-bottom-color: var(--hairline);
}

.nav-pill {
  max-width: var(--max-width);
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 24px;
}

.nav-logo {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 700;
  font-size: 17px;
  letter-spacing: -0.01em;
}

.nav-logo-icon {
  display: block;
  height: 24px;
  width: 24px;
}

.nav-logo .accent {
  color: var(--link);
}

.nav-links {
  display: flex;
  align-items: center;
  gap: 24px;
}

.nav-links a:not(.btn) {
  color: var(--text-secondary);
  font-size: 14px;
}

.nav-links a:not(.btn):hover { color: var(--text); }

/* ---------- Buttons (pill CTAs) ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 44px;
  border-radius: var(--radius-pill);
  padding: 12px 24px;
  font-size: 17px;
  font-weight: 600;
  border: 1px solid transparent;
  cursor: pointer;
  white-space: nowrap;
  transition: background-color .15s ease, transform .15s ease;
}

.btn-primary {
  background: var(--cta);
  color: #fff;
}

.btn-primary:hover {
  background: var(--cta-hover);
  transform: scale(1.02);
}

.btn-sm { padding: 12px 20px; font-size: 14px; min-height: 44px; }

/* Secondary action idiom: textlink + chevron, or ghost pill */
.link-chevron {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--link);
  font-size: 17px;
  font-weight: 600;
}

.link-chevron:hover { text-decoration: underline; }

.btn-ghost {
  background: transparent;
  border-color: var(--hairline);
  color: var(--text);
}

.btn-ghost:hover { border-color: rgba(255, 255, 255, 0.24); }

/* ---------- macOS window chrome (Sleeve-borrow: ONLY here, shadows allowed) ---------- */

.mac-window {
  border-radius: var(--radius-card);
  overflow: hidden;
  background: var(--surface-1);
  box-shadow: var(--window-shadow);
}

.mac-window-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 14px 18px;
  background: var(--surface-1);
}

.mac-window-bar .dot {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--surface-3);
  flex-shrink: 0;
}

/* ---------- Hero ---------- */

.hero {
  padding-top: 128px;
  padding-bottom: 64px;
  /* F1→H1-centered: back to centered. The rotator no longer shares a line
     with any neighbor (see .rotator below) so the old left/leftover-width
     coupling that forced .hero left in 114b658 no longer exists. */
  text-align: center;
}

/* the demo panel/desktop scene below stays centered on its own
   (.demo-panel keeps margin:0 auto) — H1/sub/CTA copy block and the trust
   badges (.badges) are centered too, as one coherent unit. */

.hero h1 {
  font-family: var(--font-display);
  font-size: clamp(30px, 9vw, 72px);
  font-weight: 400;
  line-height: 1.15;
  letter-spacing: normal;
  margin: 0 auto 24px;
  max-width: 840px;
  /* A1: min-height reservation reinstated (was dropped in e76d51d with the
     reasoning "word swaps can't change the static text's line count" —
     true, but that was never what this guarded against; the original
     comment said font-METRICS, not rotation). Departure Mono's glyphs run
     wider than the fallback chain (ui-monospace/SF Mono) it swaps in for
     during font-display:swap's block period, so the STATIC pre-text wraps
     to a different number of lines per font at the same width — unrelated
     to the rotator, which is now its own block either way.
     Remeasured from scratch (blocked-font, i.e. the fallback that's
     actually on screen before the swap — measuring with the font already
     loaded, as a naive re-check would, only shows the post-swap state and
     hides the jump): total h1 rows (static-text lines + the rotator's own
     line) sweep 300-1500px, 1-10px steps —
       fallback:      4 rows <610px, 3 rows >=610px (flat 248.39px 800-1500,
                      font-size clamp hits its 72px ceiling at 800px)
       Departure Mono: 4 rows <827px, 3 rows >=827px (crossover pinned to
                      the exact px via a 1px sweep 815-840: 826px=331.19px/
                      4 rows, 827px=248.39px/3 rows)
     Fallback never needs MORE rows than the loaded font at any width
     checked (300-1500, verified no anomaly) — so reserving for whichever
     needs 4 rows longest (Departure Mono, up to 827px) covers both. Same
     4-vs-3 split and the same 827px crossover as the pre-e76d51d rule,
     confirmed independently rather than assumed — coincidence of the
     underlying static copy, not a leftover. em-based so it tracks the
     h1's own clamp()'d font-size at any width. */
  min-height: calc(4 * 1.15em);
}

@media (min-width: 827px) {
  .hero h1 { min-height: calc(3 * 1.15em); }
}

/* Rotating word, CSS Grid stack (unchanged from the third attempt): all 6
   word+period variants live in the same grid cell; the auto-sized grid
   track always equals the widest of the 6 ("Claude Code."), at whatever
   font/viewport is currently painting. No JS measurement, no width ever
   set or transitioned — nothing to go stale after a font swap or resize.
   Only opacity/transform cross-fade between the stacked words.

   H1-centered fix: previous attempts fought over whether the rotator
   should share its line with "your" (text-align:left → drift under a
   centered .hero; text-align:center → gap before the word). Both were
   symptoms of the same mistake: sharing a line at all. display:grid
   (was inline-grid) blockifies .rotator, which breaks the surrounding
   inline flow and puts it on its OWN line automatically — no <br>, no
   restructuring of the grid-stack mechanism. width:fit-content shrinks
   that block back down to the widest word (a block's width would
   otherwise stretch to fill the h1), and margin:0 auto centers that box
   within the (now centered) h1. With no neighbor left on the line,
   text-align:center on .rotator-text is now correct: the active word
   sits centered inside the fixed-width box, i.e. centered on the page. */
.rotator {
  display: grid;
  grid-template-columns: 1fr;
  width: fit-content;
  margin: 0 auto;
  color: var(--link);
}

.rotator-text {
  grid-area: 1 / 1;
  text-align: center;
  /* ponytail: F6, documented not fixed — nowrap on the widest word ("Claude
     Code.") is a hard min-content floor for the whole H1 line. Fine at
     normal zoom, but past ~1.3x user text-zoom at 320px width the floor no
     longer fits and the page gains horizontal scroll. Upgrade path if this
     ever matters: allow the rotator to wrap at extreme zoom (drop nowrap
     under a zoom-detecting media feature, or cap font-size growth there). */
  white-space: nowrap;
  opacity: 0;
  visibility: hidden;
  transform: translateY(3px);
  transition: opacity 0.25s ease, transform 0.25s ease, visibility 0s linear 0.25s;
}

.rotator-text.is-active {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  transition: opacity 0.25s ease, transform 0.25s ease;
}

@media (prefers-reduced-motion: reduce) {
  .rotator-text { transition: none; }
}

.hero .sub {
  color: var(--text-secondary);
  font-size: 21px;
  line-height: 1.38;
  font-weight: 400;
  max-width: 620px;
  margin: 0 auto 36px;
}

.hero-ctas {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 24px;
  flex-wrap: wrap;
  margin-bottom: 64px;
}

/* Demo panel — video/fallback inside the macOS window frame */

.demo-panel {
  max-width: 920px;
  margin: 0 auto;
}

.demo-media {
  position: relative;
  width: 100%; /* without this, aspect-ratio derives width FROM the max-height cap
                  instead of filling the panel, leaving a bare gap beside it (C3 fix,
                  latent since the 2026-07-18 redesign — invisible there because the
                  single fallback line didn't span the box). */
  aspect-ratio: 16 / 10;
  max-height: 420px; /* + ~40px titlebar = ~460px total panel, keeps the empty state from ballooning */
  background: var(--card);
}

/* M6: default is hidden — the video has no real source (data-src, see the
   markup comment above) until a demo.webm/.mp4 lands, and an empty <video>
   with no working source rendered a bare grey box without JS to hide it.
   The composed scene below is the default now; JS only reveals the video
   once script.js confirms a real `src` is actually wired up. */
.demo-video {
  display: none;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* C3: composed desktop scene — abstract wallpaper + two overlapping .mac-window
   terminals + the notch-island pill. Static positioning only, nothing here
   animates, so there is nothing for prefers-reduced-motion to switch off.
   Visible by default (no JS needed) — see .demo-panel.has-video below for
   the flip once a real video source exists. */
.demo-fallback {
  display: block;
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.scene-wallpaper {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(120% 100% at 12% 0%, rgba(41, 151, 255, 0.32), transparent 60%),
    radial-gradient(100% 90% at 100% 100%, rgba(0, 199, 248, 0.16), transparent 55%),
    linear-gradient(165deg, #131318, #08080b 72%);
}

.scene-window {
  position: absolute;
  width: 58%;
  box-shadow: var(--window-shadow);
}

.scene-window--back {
  top: 12%;
  left: 5%;
  z-index: 1;
  opacity: 0.88;
}

.scene-window--front {
  right: 5%;
  bottom: 8%;
  z-index: 2;
  width: 62%;
}

/* Descendant selector (not a same-specificity .scene-terminal-body class) so this
   reliably beats the later, plain .terminal-body rule regardless of source order. */
.scene-window .terminal-body {
  padding: 14px 16px;
  min-height: 0;
  font-size: 11px;
  line-height: 1.5;
  text-align: left; /* explicit regardless of .hero's own alignment (centered again) — terminal content always reads left-to-right */
}

.notch-pill {
  position: absolute;
  top: 5%;
  left: 50%;
  transform: translateX(-50%);
  z-index: 3;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
  background: var(--surface-3);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-pill);
  padding: 7px 16px;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text);
}

.notch-pill-check {
  color: var(--link);
}

.notch-pill .accent-path {
  color: var(--link);
}

@media (max-width: 480px) {
  /* Scene reduces to one window + the pill — no horizontal scroll, no clutter. */
  .scene-window--back { display: none; }
  .scene-window--front { width: 88%; right: 6%; left: 6%; bottom: 10%; }
  .notch-pill { font-size: 11px; padding: 6px 12px; }
}

/* The outer window titlebar is meant to frame a real video capture; the scene
   brings its own two window chromes, so the outer one stays hidden (default,
   above) until .has-video is set. M6/reduced-motion never need an override
   here any more — hidden is already the resting state without JS. */
.demo-panel > .mac-window-bar {
  display: none;
}

/* script.js adds this only once a <source> actually has a real `src` (and
   removes it again on a video error) — see the fallback-vs-video comments
   above for why the default (no class) shows the scene, not the video. */
.demo-panel.has-video .demo-video {
  display: block;
}

.demo-panel.has-video .demo-fallback {
  display: none;
}

.demo-panel.has-video > .mac-window-bar {
  display: flex;
}

@media (prefers-reduced-motion: reduce) {
  .demo-panel.has-video .demo-video { display: none; }
  .demo-panel.has-video .demo-fallback { display: block; }
  .demo-panel.has-video > .mac-window-bar { display: none; }
}

/* ---------- Trust badges ---------- */

.badges {
  display: flex;
  justify-content: center; /* H1-centered: back to center, one visual unit with the centered H1/sub/CTA above it */
  flex-wrap: wrap;
  gap: 12px;
  padding: 32px 0 96px;
}

.badge {
  display: inline-flex;
  align-items: center;
  background: var(--surface-1);
  border-radius: var(--radius-pill);
  padding: 9px 18px;
  font-size: 13px;
  color: var(--text-secondary);
}

/* ---------- Section shell ---------- */

section { padding: clamp(72px, 12vw, 144px) 0; }

.section-head {
  max-width: 640px;
  margin: 0 auto 48px;
  text-align: center;
}

.section-head h2 {
  font-size: clamp(32px, 5vw, 56px);
  font-weight: 600;
  letter-spacing: -0.005em;
  line-height: 1.07;
  margin: 0 0 14px;
}

.section-head p {
  color: var(--text-secondary);
  font-size: 17px;
  margin: 0;
}

/* ---------- Problem / Solution (2-col, tonal panels) ---------- */

.problem-grid {
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  gap: 56px;
  align-items: center;
}

.problem-grid h2 {
  font-size: clamp(28px, 4vw, 48px);
  font-weight: 600;
  line-height: 1.08;
  letter-spacing: -0.003em;
  margin: 0 0 18px;
  text-align: left;
}

.problem-grid .issue-refs {
  color: rgba(245, 245, 247, 0.78); /* multi-sentence body <17px: lighter than --text-secondary per spec rule 6 */
  font-size: 14px;
  margin: 0 0 24px;
}

.mono-chip {
  font-family: var(--font-mono);
  color: var(--text);
  background: var(--card);
  border-radius: var(--radius-sm);
  padding: 2px 8px;
  font-size: 13px;
  /* M5: the real source path (codex-rs/tui/src/bottom_pane/chat_composer.rs) is
     44 no-space chars — wider than a 320px viewport's remaining line width.
     Let it wrap inside the chip instead of pushing the page into horizontal
     scroll; the path stays fully readable, just on 2 lines instead of 1. */
  overflow-wrap: anywhere;
  /* ponytail: F7, documented not fixed — at 320px this wraps onto 3 lines
     (not the 2 the M5 comment above assumed), and .mono-chip is inline: its
     pill background applies per line fragment, not as one shape around the
     whole block, so the "pill" visibly breaks into 3 disjointed segments.
     Text stays fully readable either way. Upgrade path if this ever matters:
     display:inline-block (turns it into one padded box, but then it no
     longer flows inline with the surrounding sentence the way M5 wanted). */
}

.flood-panel {
  border-radius: var(--radius-card);
  background-color: var(--card);
  background-image: radial-gradient(rgba(255, 255, 255, 0.02) 1px, transparent 1.5px);
  background-size: 6px 6px;
  padding: 28px;
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--text-secondary);
}

.flood-panel .before,
.flood-panel .after {
  border-radius: var(--radius-sm);
  padding: 16px;
  margin-bottom: 14px;
}

.flood-panel .before {
  background: var(--surface-1);
}

.flood-panel .after {
  background: var(--surface-3);
  color: var(--text);
  margin-bottom: 0;
  overflow-wrap: anywhere;
}

.flood-panel .after .accent-path { color: var(--link); }

.flood-panel .label {
  display: block;
  font-family: var(--font);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  opacity: 0.7;
  margin-bottom: 8px;
}

/* ---------- How it works (steps + terminal demo) ---------- */

.how-grid {
  display: grid;
  grid-template-columns: 0.9fr 1.1fr;
  gap: 56px;
  align-items: center;
}

.steps {
  list-style: none;
  margin: 0;
  padding: 0;
  counter-reset: step;
}

.steps li {
  counter-increment: step;
  position: relative;
  padding-left: 40px;
  margin-bottom: 20px;
  color: var(--text-secondary);
  font-size: 17px;
}

.steps li strong { color: var(--text); }

.steps li::before {
  content: counter(step);
  position: absolute;
  left: 0;
  top: -1px;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--card);
  color: var(--link);
  font-size: 13px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
}

.terminal-body {
  margin: 0;
  padding: 24px;
  min-height: 88px;
  font-family: var(--font-mono);
  font-size: 13px;
  line-height: 1.6;
  color: var(--text);
  white-space: pre-wrap;
  word-break: break-all;
}

.terminal-body .caret {
  display: inline-block;
  width: 7px;
  height: 14px;
  margin-left: 2px;
  background: var(--link);
  vertical-align: -2px;
  animation: caret-blink 1s step-end infinite;
}

@keyframes caret-blink {
  50% { opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .terminal-body .caret { display: none; }
}

/* ---------- Feature grid ---------- */

.feature-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
}

.feature-card {
  background-color: var(--card);
  background-image: radial-gradient(rgba(255, 255, 255, 0.02) 1px, transparent 1.5px);
  background-size: 6px 6px;
  border-radius: var(--radius-card);
  padding: 32px;
}

.feature-card .icon {
  display: inline-flex;
  width: 44px;
  height: 44px;
  margin-bottom: 20px;
  align-items: center;
  justify-content: center;
  color: var(--text);
  background: var(--surface-3);
  border-radius: var(--radius-sm);
}

.feature-card .icon svg { width: 24px; height: 24px; }

.feature-card h3 {
  font-size: 24px;
  font-weight: 600;
  letter-spacing: -0.003em;
  line-height: 1.17;
  margin: 0 0 8px;
}

.feature-card p {
  color: var(--text-secondary);
  font-size: 17px;
  margin: 0;
}

.feature-card code {
  font-family: var(--font-mono);
  background: var(--surface-3);
  border-radius: 6px;
  padding: 2px 6px;
  font-size: 0.85em;
}

/* ---------- Pricing (the one light section) ---------- */

/* Z2 (chef-directed): soft top/bottom edges into the dark canvas instead of
   a hard cut, same masking technique as the hero's body::after (alpha-
   gradient, no box-shadow — Elevation-über-Tonwert only allows box-shadow on
   .mac-window chrome). The 56px band is comfortably inside the
   clamp(80px,...) section padding at every width, so it only ever eats into
   empty margin, never behind text.
   Full-bleed (chef-directed, follow-up): .light-band no longer carries
   .wrap — it used to only by accident (reused for the free centered-column
   padding), and the resulting 980px-capped band left a hard VERTICAL cut of
   its own at the section's left/right edges. Chef call: go full-bleed
   instead of fading that seam. .wrap now lives on a dedicated inner div in
   index.html so the card still centers on the same 980px column.
   That removes the left/right hard edge entirely (the light background now
   runs to both viewport edges), so the horizontal fade + its
   @media(min-width:680px) gate from 493cbc5 is INTENTIONALLY reverted here,
   not lost: a gradient fading into a viewport edge that's still just as
   "hard" as before (the edge itself, not .light-band's edge) would be
   visible nonsense. Vertical fade is untouched. */
/* A3: scroll-margin-top so the sticky nav (.nav-sticky) doesn't park over
   the section top — and with it the .launch-badge, which sits above the
   card — after the "Buy" nav link's #pricing anchor jump. Measured nav
   height (Playwright, real height incl. any wrap): 75.58px at >=365px
   viewport width, 116.56px at <=363px where .nav-pill's flex-wrap (see
   @media max-width:560px below) actually kicks in to a 2nd row. +20px
   breathing room on top of each. */
.light-band {
  position: relative;
  z-index: 0; /* contains the ::before's z-index:-1 to this element's own
                 stacking context, instead of falling behind body::before/
                 body::after (z-index -2/-1) in the page's root context */
  padding-top: clamp(80px, 14vw, 144px);
  padding-bottom: clamp(80px, 14vw, 144px);
  color: var(--text-on-light);
  scroll-margin-top: 96px;
}

@media (max-width: 364px) {
  .light-band { scroll-margin-top: 137px; }
}

/* A4: the fade now lives on a ::before that owns ONLY the background, not
   on .light-band directly. mask-image on .light-band itself masked its
   ENTIRE rendered subtree as one compositing group — including
   .pricing-card and, worse, .launch-badge, which pokes up above the card
   by design (top:-38px) and sits only ~42-90px into the section depending
   on breakpoint padding, i.e. inside the 56px top fade zone at the
   narrowest widths. Measured (revealed state, not the pre-.reveal
   translateY(24px) resting state — that transiently shifts the card down
   and understates the overlap): mask alpha 0.631 at the badge at 320/
   390px, rgb(2,94,186) rendered vs rgb(0,113,227) unmasked. Splitting the
   mask onto a background-only pseudo keeps the soft edge (still fades to
   transparent into the dark canvas) while every real content layer —
   card, badge — paints on top at full opacity, never touched by the mask. */
.light-band::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--light-band);
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #fff 56px, #fff calc(100% - 56px), transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0, #fff 56px, #fff calc(100% - 56px), transparent 100%);
}

.pricing-card {
  position: relative;
  max-width: 460px;
  margin: 0 auto;
  text-align: center;
  background: var(--light-card);
  border-radius: var(--radius-card);
  padding: 48px 36px;
}

/* C5: launch-price sticker. Flat fill, no box-shadow (Elevation-über-Tonwert
   rule) — a rotated circle reads as a sticker on its own without one. */
.launch-badge {
  position: absolute;
  top: -38px;
  right: -8px;
  width: 84px;
  height: 84px;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  border-radius: 50%;
  background: var(--cta);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.03em;
  line-height: 1.3;
  text-transform: uppercase;
  transform: rotate(-10deg);
}

.pricing-card .price {
  font-size: 56px;
  font-weight: 700;
  letter-spacing: -0.02em;
  margin: 8px 0 4px;
  color: var(--text-on-light);
}

.pricing-card .price span {
  font-size: 18px;
  color: var(--text-on-light-secondary);
  font-weight: 500;
}

.pricing-card .price .price-was {
  text-decoration: line-through;
  margin-right: 2px;
}

/* Chef-directed 2026-07-22: unbefristeter Polar-Rabatt, kein Enddatum
   behaupten (Abmahnrisiko) — .launch-note mit Datum ersatzlos gestrichen,
   der Streichpreis in .price trägt den Vergleichsanker allein. Knappheit
   stattdessen hier: Polar-Einlöse-Limit auf den Rabatt-Code, keine Zahl/
   Frist genannt (Seite ist statisch, jede Zahl wäre ab dem ersten Verkauf
   falsch), Knappheit ausdrücklich am Rabatt festgemacht, nicht am Produkt
   (digitale Lizenzen sind unbegrenzt). */
.scarcity-note {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: 8px;
  color: var(--text-on-light-secondary);
  font-size: 13px;
  line-height: 1.5;
  margin: 0 0 24px;
}

.pulse-dot {
  width: 7px;
  height: 7px;
  flex-shrink: 0;
  margin-top: 6px; /* aligns dot to the cap-height of line 1, not the
                       vertical center of the whole (possibly 2-line,
                       narrow-viewport) paragraph */
  border-radius: 50%;
  background: var(--link-on-light);
  animation: pulse-dot 2s ease-in-out infinite;
}

@keyframes pulse-dot {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.4; transform: scale(0.7); }
}

@media (prefers-reduced-motion: reduce) {
  .pulse-dot { animation: none; }
}

.pricing-card .tagline {
  color: var(--text-on-light-secondary);
  font-size: 14px;
  margin-bottom: 28px;
}

.pricing-list {
  list-style: none;
  margin: 0 0 32px;
  padding: 0;
  text-align: left;
  font-size: 14px;
  color: var(--text-on-light-secondary);
}

.pricing-list li {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 0;
  border-top: 1px solid var(--hairline-on-light);
}

.pricing-list li:first-child { border-top: none; }

.pricing-list li svg {
  color: var(--link-on-light);
  flex-shrink: 0;
  width: 18px;
  height: 18px;
}

.pricing-card .btn { width: 100%; }

.pricing-card .via {
  margin-top: 14px;
  font-size: 12px;
  color: var(--text-on-light-secondary);
}

.checkout-consent-note {
  margin-top: 20px;
  font-size: 12px;
  line-height: 1.43;
  color: var(--text-on-light-secondary);
}

.checkout-consent-note a {
  color: var(--link-on-light);
  text-decoration: underline;
}

/* ---------- FAQ ---------- */

.faq-list {
  max-width: 760px;
  margin: 0 auto;
}

.faq-item {
  background-image: linear-gradient(180deg, rgba(255, 255, 255, 0.015), rgba(255, 255, 255, 0) 60%);
  border-bottom: 1px solid var(--hairline);
}

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

.faq-question {
  width: 100%;
  background: none;
  border: none;
  color: var(--text);
  font-family: inherit;
  font-size: 17px;
  font-weight: 600;
  text-align: left;
  padding: 22px 4px;
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
}

.faq-question .chevron {
  color: var(--text-secondary);
  display: inline-flex;
  transition: transform 0.2s ease;
  flex-shrink: 0;
}

.faq-item.is-open .chevron { transform: rotate(180deg); }

.faq-answer {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.25s ease;
  color: rgba(245, 245, 247, 0.78); /* multi-sentence body <17px: lighter than --text-secondary per spec rule 6 */
  font-size: 14px;
}

.faq-answer > div {
  overflow: hidden;
  padding: 0 4px;
}

.faq-item.is-open .faq-answer {
  grid-template-rows: 1fr;
}

.faq-item.is-open .faq-answer > div {
  padding: 0 4px 22px;
}

/* ---------- Footer ---------- */

footer {
  border-top: 1px solid var(--hairline);
  padding: 48px 0 64px;
}

.footer-inner {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 20px;
}

.footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
}

.footer-links a {
  color: var(--text-secondary);
  font-size: 13px;
}

.footer-links a:hover { color: var(--text); }

.footer-brand {
  font-size: 13px;
  color: var(--text-secondary);
}

.footer-brand .accent { color: var(--link); font-weight: 600; }

/* ---------- Legal pages (Impressum/Datenschutz/AGB/Widerruf) ---------- */

.legal-content { padding: 64px 0 96px; max-width: 720px; }
.legal-content h1 {
  font-size: clamp(32px, 5vw, 56px);
  font-weight: 700;
  letter-spacing: -0.01em;
  margin: 0 0 8px;
}
.legal-content .legal-updated { color: var(--text-secondary); font-size: 14px; margin-bottom: 32px; }
.legal-content h2 { font-size: 20px; margin: 40px 0 12px; }
.legal-content h3 { font-size: 16px; margin: 24px 0 8px; }
.legal-content p { color: var(--text-secondary); margin: 0 0 12px; }
.legal-content p strong, .legal-content li strong { color: var(--text); }
.legal-content ul, .legal-content ol { color: var(--text-secondary); padding-left: 20px; margin: 0 0 16px; }
.legal-content li { margin-bottom: 6px; }
.legal-content a { color: var(--link); text-decoration: underline; }
.legal-content .placeholder {
  color: var(--link);
  font-family: var(--font-mono);
  font-size: 0.85em;
}

/* ---------- Motion: scroll-reveals (shared IntersectionObserver, .reveal + .js) ---------- */

html.js .reveal {
  opacity: 0;
  transform: translateY(24px);
  filter: blur(8px);
  transition: opacity .6s ease-out, transform .6s ease-out, filter .6s ease-out;
}

html.js .reveal.is-visible {
  opacity: 1;
  transform: none;
  filter: blur(0);
}

/* stagger 60ms per child inside grouped grids */
.stagger > .reveal:nth-child(1) { transition-delay: 0ms; }
.stagger > .reveal:nth-child(2) { transition-delay: 60ms; }
.stagger > .reveal:nth-child(3) { transition-delay: 120ms; }
.stagger > .reveal:nth-child(4) { transition-delay: 180ms; }
.stagger > .reveal:nth-child(5) { transition-delay: 240ms; }
.stagger > .reveal:nth-child(6) { transition-delay: 300ms; }

@media (prefers-reduced-motion: reduce) {
  html.js .reveal,
  html.js .reveal.is-visible {
    opacity: 1;
    transform: none;
    filter: none;
    transition: none;
  }
}

/* ---------- Responsive ---------- */

@media (max-width: 860px) {
  .problem-grid { grid-template-columns: 1fr; gap: 40px; }
  .how-grid { grid-template-columns: 1fr; gap: 40px; }
  .feature-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 560px) {
  .hero { padding-top: 96px; padding-bottom: 40px; }
  .feature-grid { grid-template-columns: 1fr; }
  .nav-pill { flex-wrap: wrap; padding: 14px; }
  .nav-links { gap: 14px; }
  .footer-inner { flex-direction: column; align-items: flex-start; }
}

/* ---------- SEO Guides + Compare page (site/guides/*.html, site/alternatives.html) ----------
   Appended block, Strand D (2026-07-22). Reuses .wrap.legal-content for article typography —
   only new component styles below, .guide-/.compare- prefixed per spec guardrail. */

.guide-footer-groups { display: flex; flex-direction: column; gap: 10px; }
.guide-footer-label { font-size: 13px; color: var(--text); font-weight: 600; margin-right: 4px; }

.guide-cta {
  margin-top: 56px;
  padding: 40px;
  border-radius: var(--radius-card);
  background: var(--card);
  text-align: center;
}
.guide-cta h2 { margin: 0 0 8px; font-size: 22px; }
.guide-cta p { color: var(--text-secondary); margin: 0 0 20px; }
/* .legal-content a (0,1,1) otherwise beats .btn-primary (0,1,0) on color + adds an underline;
   this rule (0,3,1) restores the button's white label and removes the underline without
   touching the .legal-content a rule itself (append-only guardrail). */
.legal-content .guide-cta a.btn-primary { color: #fff; text-decoration: none; }

.guide-related { margin-top: 40px; padding-top: 24px; border-top: 1px solid var(--hairline); }
.guide-related h2 {
  font-size: 13px;
  margin: 0 0 12px;
  color: var(--text-secondary);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.guide-related ul { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 8px; }

.compare-table-wrap { overflow-x: auto; margin: 24px 0; border-radius: var(--radius-sm); border: 1px solid var(--hairline); }
.compare-table-wrap:focus-visible { outline: 2px solid var(--link); outline-offset: -2px; }
.compare-table { width: 100%; border-collapse: collapse; font-size: 14px; min-width: 640px; }
.compare-table caption {
  caption-side: top;
  text-align: left;
  font-size: 13px;
  color: var(--text-secondary);
  padding: 12px 14px;
  background: var(--card);
  border-bottom: 1px solid var(--hairline);
}
.compare-table th, .compare-table td { text-align: left; padding: 12px 14px; border-bottom: 1px solid var(--hairline); vertical-align: top; }
.compare-table thead th { color: var(--text); font-weight: 600; background: var(--card); }
.compare-table tbody th[scope="row"] { color: var(--text); font-weight: 600; background: transparent; }
.compare-table td { color: var(--text-secondary); }
.compare-table tbody tr:last-child th, .compare-table tbody tr:last-child td { color: var(--text); }
.compare-table tbody tr:last-child { background: rgba(41, 151, 255, 0.06); }
