/* =========================================================================
 * v2.css — everything that is NEW in V2. site.css stays a faithful copy of V1
 * so the two variants can be diffed honestly: this file IS the change.
 *
 * Three ideas live here:
 *   1. the hero shows the running product instead of an abstraction
 *   2. the section after it slides over the pinned window
 *   3. two products, two pages, reached through a dropdown of chips
 * ========================================================================= */

/* -------------------------------------------------------------------------
 * 1. Nav dropdown
 * ---------------------------------------------------------------------- */
/* The trigger is a real link to products.html, not just a hover target — a
   dropdown that only opens on hover is a dead menu item on touch. */
.nav-drop { position: relative; display: flex; align-items: center; }
.nav-drop-trigger { display: inline-flex; align-items: center; gap: 5px; }
.nav-caret { width: 9px; height: 9px; flex: none; transition: transform .18s ease; }
.nav-drop:hover .nav-caret,
.nav-drop:focus-within .nav-caret { transform: rotate(180deg); }
.nav-drop-trigger:hover { color: var(--acc-feat); }

.nav-drop-panel {
  position: absolute; top: 100%; left: 50%; transform: translate(-50%, 6px);
  /* the header shrinks 62→46px on scroll; anchoring to the trigger keeps the
     panel attached through that transition instead of jumping */
  padding-top: 14px; width: min(380px, calc(100vw - 32px));
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity .18s ease, transform .18s ease, visibility .18s;
  z-index: 40;
}
.nav-drop:hover .nav-drop-panel,
.nav-drop:focus-within .nav-drop-panel {
  opacity: 1; visibility: visible; pointer-events: auto; transform: translate(-50%, 0);
}
.nav-drop-inner {
  display: flex; flex-direction: column; gap: 6px; padding: 8px;
  border-radius: 14px; background: var(--surface-2); border: 1px solid var(--line-2);
  -webkit-backdrop-filter: blur(24px) saturate(140%); backdrop-filter: blur(24px) saturate(140%);
  box-shadow: 0 18px 44px rgba(0, 0, 0, 0.20);
}

/* product chip — logo · title + subtitle · arrow */
.prod-chip {
  display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 13px;
  padding: 12px 13px; border-radius: 10px; text-decoration: none; color: var(--ink);
  transition: background .16s ease;
}
.prod-chip:hover, :root.kbd .prod-chip:focus-visible { background: color-mix(in srgb, var(--ink) 5%, transparent); }
.prod-chip-mark { width: 26px; height: 26px; display: grid; place-content: center; color: var(--pc-acc, var(--ink)); }
.prod-chip-mark svg { width: 100%; height: 100%; display: block; }
/* both are spans (an <a> may not contain block elements in older parsers), so the
   block display has to be explicit or the title and subtitle run together on one line */
.prod-chip-title { display: block; font-family: var(--font-display); font-weight: 700; font-size: 15px; letter-spacing: -0.01em; line-height: 1.2; text-transform: none; }
.prod-chip-sub { display: block; margin-top: 2px; font-size: 12.5px; line-height: 1.35; color: var(--dim); text-transform: none; letter-spacing: 0; }
.prod-chip-go { width: 17px; height: 17px; color: var(--faint); transition: transform .18s ease, color .18s ease; }
.prod-chip:hover .prod-chip-go, :root.kbd .prod-chip:focus-visible .prod-chip-go { transform: translateX(3px); color: var(--pc-acc, var(--ink)); }
.prod-chip:hover .prod-chip-title, :root.kbd .prod-chip:focus-visible .prod-chip-title { color: var(--pc-acc, var(--ink)); }
.pc-journal { --pc-acc: var(--acc-feat); }
.pc-copier  { --pc-acc: var(--acc-copier); }

/* the mobile overlay gets the same two chips as a flat list — no accordion needed
   for two items, and a disclosure that hides half the menu behind a tap is worse */
.menu-nav .menu-sub { display: flex; flex-direction: column; gap: 4px; margin: 6px 0 10px 2px; }
.menu-nav .menu-sub a {
  font-family: var(--font-display); font-size: clamp(17px, 4.4vw, 22px); font-weight: 500;
  letter-spacing: -0.01em; text-transform: none; color: var(--dim);
  display: inline-flex; align-items: center; gap: 9px;
}
.menu-nav .menu-sub a svg { width: 16px; height: 16px; flex: none; }
.menu-nav .menu-sub a.ms-journal { color: var(--acc-feat); }
.menu-nav .menu-sub a.ms-copier  { color: var(--acc-copier); }

/* -------------------------------------------------------------------------
 * 2. Hero — the running product in a floating window
 * ---------------------------------------------------------------------- */
/* Scroll choreography. The dashboard appears TWICE, which is what shapes everything
   below — it is covered, and then uncovered again:
     1. the copy scrolls away normally and the window rises with it
     2. the window's top reaches --hw-top and it pins, clear of the collapsed header
     3. the three callouts play (--anno-run)
     4. .cover — products + stay synced — climbs over the pinned window
     5. .cover carries on past the top and leaves the window uncovered again
     6. a pause (--dev-hold), then tablet, phone and the headline (--dev-run)
     7. .lift climbs over it for good

   Step 5 is why .cover lives INSIDE .hero-pin. Behind the pin it could only ever
   cover the window once — there would be nothing left below it to scroll past.

   --hw-top: 46px collapsed header + breathing room.
   --hw-h:   window height, so it sits fully on screen once pinned.

   The runways must be real content height, not padding: a sticky child is confined to
   its parent's CONTENT box, so padding adds nothing to its travel and the window
   would scroll away as if position:sticky weren't there at all. */
/* On :root, not .hero-pin — .lift lives outside the pin and must use the exact same
   --hw-h, or the runway and the pull-up drift apart and the overlap breaks. */
:root {
  --hw-top: 72px;      /* pinned offset — leaves 26px clear of the 46px collapsed header */
  --hw-bottom: 30px;   /* matching breathing room at the bottom edge */
  --hw-h: calc(100vh - var(--hw-top) - var(--hw-bottom));
  /* Scroll spent on the three callouts. hero.js splits it into a lead-in, one equal
     share per callout, and a tail — so this single number sets how long each one stands
     still: (1360 − 80 − 80) / 3 = 400px each. Raise it to let them dwell longer. */
  --anno-run: 1360px;
  --dev-hold: 200px;   /* the dashboard alone, after the card lifts off it */
  --dev-run: 700px;    /* …then the tablet and phone beat, before .lift starts */
  --wipe-run: 400px;   /* …then the day/night wipe, inside the beat's own trailing runway */
  --wipe-hold: 500px;  /* …then a pause with "Day and night." fully settled, before .lift starts */
  --wipe-tilt: 3%;     /* how far the wipe's edge leans, top vs bottom of the window */
}
@supports (height: 100dvh) {
  :root { --hw-h: calc(100dvh - var(--hw-top) - var(--hw-bottom)); }
}
.hero-pin { position: relative; }

/* The two runways. Each buys its beat the scroll it needs, and each also carries the
   pull-up of the card that follows it — every .over pulls itself up by --hw-h, and if
   the runway does not add that height back, the beat before it loses exactly that much
   travel. That is the whole arithmetic; get one term wrong and a beat runs short. */
#annoRun {
  height: calc(var(--anno-run)      /* the three callouts */
             + var(--hw-h));        /* + what .cover pulls itself up by */
}
#devRun {
  height: calc(var(--hw-h) + var(--hw-bottom)   /* uncovering: .cover's bottom edge from
                                                   the viewport floor up to the window top */
             + var(--dev-hold)      /* the dashboard alone for a beat */
             + var(--dev-run)       /* tablet, phone, headline */
             + var(--wipe-run)      /* day/night wipe, after the entrance settles */
             + var(--wipe-hold)     /* a beat to let it be read before .lift covers it */
             + var(--hw-h));        /* + what .lift pulls itself up by */
}

/* The stage is what pins now — full width, so the callout boxes have room beside the
   window once it zooms back. The window itself sits centred inside it. */
/* Centring is done by flex, NOT by left:50% + translateX(-50%): the window also
   carries .rise, whose keyframes end on `transform: none`, and an animation beats a
   normal declaration — so a translate-based centring silently dies the moment the
   entrance finishes, throwing the window half its width to the right. Keeping
   transform free for the zoom alone avoids the whole class of conflict. */
/* The stage no longer centres anything itself — .hero-devices does, because all
   three screens have to travel together in the last beat. */
.hero-stage { position: sticky; top: var(--hw-top); z-index: 0; height: var(--hw-h); width: 100%; }

/* Carrier for desktop + tablet + phone. Shifts up in the device beat to make room
   for the heading underneath. */
/* Only ever moves vertically. There used to be a sideways nudge here as well — an optical
   centring of the device trio — but it moved the dashboard with it, and the dashboard is
   the one thing on screen both before and after the beat. The trio is composed through the
   two devices' own overlaps instead, so this carrier has nothing left to shift sideways. */
.hero-devices {
  position: absolute; inset: 0; display: flex; justify-content: center;
  transform: translateY(var(--dev-shift, 0px));
  transition: transform .6s cubic-bezier(.2, .7, .2, 1);
}

/* the gap to the window lives here, not as the window's margin-top: sticky positions
   the MARGIN box, so a top margin would push the pinned window that much further down
   and quietly unbalance --hw-top / --hw-bottom */
.hv { max-width: 1320px; width: 100%; margin: 0 auto; padding: clamp(18px, 3.4vh, 44px) clamp(20px, 4vw, 48px) clamp(14px, 2.6vh, 34px); text-align: center; }
/* inline-flex + text-align centring: as a flex item this stretched to the full column
   width and its contents sat at the left edge, which read as a left-aligned eyebrow */
.hv-eyebrow { display: inline-flex; align-items: center; gap: 10px; font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.22em; text-transform: uppercase; color: var(--dim); }
.hv-eyebrow .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--acc-start); box-shadow: 0 0 10px var(--acc-start); }
/* vh terms keep the copy from eating the window's height on short laptops */
/* the size is a named token because the second line has to be positioned as a fraction
   of IT, and an em inside .kh-second would resolve against .kh-second's own size */
.hv h1 { --kh-size: clamp(38px, min(6.4vw, 8.6vh), 92px);
  margin-top: clamp(8px, 1.6vh, 16px); font-family: var(--font-display); font-weight: 700; letter-spacing: -0.045em; line-height: 0.92; font-size: var(--kh-size); color: var(--ink); }
/* V1's headline is left-aligned, V2's hero is centred — the word row is a flex
   container, so text-align alone doesn't reach it. Everything else about the
   carousel (slot width in em, .kw stack) comes from site.css. */
/* One line: "Know your" and the rotating word sit on the same row, so the gap between
   them has to read as a word space — hence em, not the px clamp site.css uses for the
   old side-by-side layout, which would drift away from the text as the type scales. */
.hv .kh-row { justify-content: center; gap: 0.22em; }
/* The second line is now its own line under the row, not a neighbour of the word. It
   therefore centres with the page, and the baseline machinery in site.js no longer
   applies to it. */
/* Pulled up against the row: the slot is 1.28em tall, so its box carries a lot of empty
   space below the word's baseline, and a positive margin on top of that leaves the two
   lines reading as unrelated. Negative, and as a fraction of the HEADLINE's size, so the
   optical distance stays the same at every viewport. */
.hv .kh-second {
  display: block; text-align: center; white-space: nowrap;
  font-size: clamp(13px, min(1.7vw, 2.4vh), 24px);
  margin-top: calc(var(--kh-size) * -0.22);
  /* Held back until the word has landed, then eased in — it is the pay-off line, not part
     of the headline's entrance. The drift rides `translate` rather than `transform`,
     because site.js writes the editor's manual offset to `transform`; on one property the
     two would overwrite each other, on two they compose. */
  opacity: 0; translate: 0 8px;
  transition: opacity .6s ease, translate .6s cubic-bezier(.2, .7, .2, 1);
}
.hv .kh-second.in { opacity: 1; translate: 0 0; }
.hv-sub { margin: clamp(8px, 1.6vh, 14px) auto 0; max-width: 620px; font-size: clamp(13.5px, 1.5vw, 16.5px); line-height: 1.5; color: var(--dim); }
.hv-cta { margin-top: clamp(12px, 2.2vh, 24px); display: flex; align-items: center; justify-content: center; gap: clamp(18px, 3vw, 34px); flex-wrap: wrap; }
/* Product pick, rebuilt as a chip (same family as .hero-open-app below, and the
   site's plain .chip): an outline at rest, filled on hover by a directional wipe
   of the product's own two-tone gradient — not a flat colour swap. The wipe is a
   scaleX transform on a ::before behind the content, same "sweep" language as
   the underline/text reveals elsewhere, just applied to the chip's fill instead.
   Icon and label never move — only the trailing arrow still animates. Stops are
   mixed further from --acc-feat/--acc-copier toward a deep and a light version
   of the same hue (not white alone, and not black) so "blue" and "aqua" read as
   themselves at both ends, with enough range left for white icon/text on top. */
.pick-journal { --hover: color-mix(in srgb, var(--acc-feat) 72%, #0B2E99 28%); --hover-2: color-mix(in srgb, var(--acc-feat) 72%, white 28%); }
.pick-copier  { --hover: color-mix(in srgb, var(--acc-copier) 72%, #024656 28%); --hover-2: color-mix(in srgb, var(--acc-copier) 72%, white 28%); }

.hv-cta .hero-pick {
  position: relative; overflow: hidden; isolation: isolate;
  padding: 7px 15px 7px 11px; border-radius: 999px; gap: 6px;
  border: 1.5px solid var(--line-2); background: var(--surface-2);
  font-size: clamp(13px, 1.4vw, 17px);
  transition: border-color .25s ease;
}
.hv-cta .hero-pick:hover { border-color: transparent; }
.hv-cta .hero-pick::before {
  content: ''; position: absolute; inset: 0; z-index: 0;
  background: linear-gradient(105deg, var(--hover), var(--hover-2));
  transform: scaleX(0); transform-origin: left center;
  transition: transform .45s cubic-bezier(.2, .7, .2, 1);
}
.hv-cta .hero-pick:hover::before { transform: scaleX(1); }
.hv-cta .hero-pick::after { content: none; }         /* no underline on a pill */
.hv-cta .hero-pick p::before { content: none; }       /* replaced by the chip fill above, not a text sweep */
.hv-cta .hero-pick p { position: relative; z-index: 1; color: inherit; transition: color .25s ease; }

.hv-cta .pick-mark {
  position: relative; z-index: 1; width: 15px; height: 15px; display: grid; place-content: center;
  color: var(--ink); flex-shrink: 0; transition: color .25s ease;
}
/* color:inherit, not a value — site.css's sitewide .ul-link svg / .ul-link:hover
   svg rules would otherwise paint this specific svg element directly (a rule
   matching the element itself always wins over inheriting from its parent,
   regardless of specificity), decoupling it from .pick-mark's own ink/white. */
.hv-cta .pick-mark svg { width: 100%; height: 100%; display: block; color: inherit; }
/* direct-child selector: the arrow is the <a>'s own trailing svg, .pick-mark's
   svg is a grandchild — this is what keeps the logo from also sliding on hover */
.hv-cta .hero-pick > svg { position: relative; z-index: 1; width: 14px; height: 14px; color: var(--ink); transition: transform .25s ease, color .25s ease; }
.hv-cta .hero-pick:hover > svg { transform: translate(3px, -3px); }

.hv-cta .hero-pick:hover p,
.hv-cta .hero-pick:hover .pick-mark,
.hv-cta .hero-pick:hover > svg { color: #fff; }

/* the window itself — centred in the stage, and scaled back during the callout phase
   so the boxes get margins to sit in. A transform, deliberately: changing its width
   would force the iframe to re-layout the whole app mid-animation. */
.hero-window {
  position: relative; flex: none;
  transform: scale(var(--anno-zoom, 1));
  transform-origin: center center;
  transition: transform .55s cubic-bezier(.2, .7, .2, 1);
  height: 100%; display: flex; flex-direction: column;
  width: min(1180px, 92vw); border-radius: 15px; overflow: hidden;
  background: var(--surface-2); border: 1px solid var(--line-2);
  /* deep enough to read as "floating above the page" without becoming a modal;
     the page behind it stays visible through the sticky scroll */
  box-shadow: 0 34px 80px rgba(0, 0, 0, 0.26), 0 4px 12px rgba(0, 0, 0, 0.08);
}
:root[data-theme="dark"] .hero-window { box-shadow: 0 34px 80px rgba(0, 0, 0, 0.55), 0 4px 12px rgba(0, 0, 0, 0.30); }

/* Real "open the app" invitation — hidden until js/hero.js adds .in (stage pinned
   AND the first callout has landed, same travelled>=ANNO_LEAD check the boxes use).
   Floats just under the window's bottom edge, inside .hero-stage's own --hw-bottom
   margin — the window's callout-phase zoom-back (.anno-zoom) opens up exactly the
   room this needs, so it never fights the boxes for space. */
.hero-open-app {
  position: absolute; left: 50%; bottom: 4px; z-index: 4;
  transform: translateX(-50%) translateY(10px);
  opacity: 0; pointer-events: none;
  display: inline-flex; align-items: center; gap: 8px;
  padding: 11px 22px; border-radius: 999px;
  font-family: var(--font-display); font-weight: 600; font-size: 15px;
  background: var(--ink); color: var(--bg); text-decoration: none;
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.22);
  transition: opacity .4s ease, transform .4s cubic-bezier(.2, .7, .2, 1);
}
.hero-open-app svg { width: 16px; height: 16px; transition: transform .25s ease; }
.hero-open-app:hover svg { transform: translate(3px, -3px); }
.hero-open-app.in { opacity: 1; transform: translateX(-50%) translateY(0); pointer-events: auto; }
/* The shared .rise entrance animates transform, which would fight the zoom above.
   Same timing and delay, opacity only. */
@keyframes annoFadeIn { from { opacity: 0; } to { opacity: 1; } }
.hero-window.rise { animation-name: annoFadeIn; }
.hw-bar { flex: none; display: flex; align-items: center; gap: 7px; padding: 10px 14px; border-bottom: 1px solid var(--line); background: var(--surface); }
.hw-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--line-2); flex: none; }
.hw-url { margin-left: 10px; font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.1em; color: var(--faint); }

/* The app is a 12-column desktop dashboard; below ~1280px it collapses to an
   icon rail and fluid columns and stops reading as one. So on desktop the frame is
   always laid out at HW_W logical pixels and scaled to fit — the shot stays the real
   desktop app at every window size. JS writes --hw-scale. */
.hw-screen { position: relative; flex: 1 1 auto; min-height: 0; overflow: hidden; background: var(--bg); }
/* width is the logical HW_W; height is written by JS from the box it has to fill,
   divided by the scale — so the app gets a real 1440px-wide viewport and fills the
   window exactly, with no clipped bottom row. */
.hw-frame {
  position: absolute; top: 0; left: 0; width: 1440px; height: 880px; border: 0;
  transform: scale(var(--hw-scale, 1)); transform-origin: 0 0;
  opacity: 0; transition: opacity .45s ease;
}
.hero-window.hw-live .hw-frame { opacity: 1; }

/* The poster: a real capture of the dashboard, holding the window until the app inside
   the frame says it has finished rendering. This replaced a grey skeleton — the skeleton
   was cheap and theme-aware, but it never looked like the product, and the frame used to
   be revealed on its `load` event, which fires with the app still assembling. So the
   first thing a visitor saw was either a placeholder that promised nothing or a dashboard
   building itself in front of them.

   One capture per theme, exactly like the tablet and phone frames. Same geometry as
   .hw-frame, so the swap moves nothing. */
/* Same box as .hw-frame, down to the logical height JS writes on both — otherwise the
   poster is shorter than the frame and the handover shifts. The capture is deliberately
   TALLER than any window needs, so `cover` always crops the bottom instead of scaling
   up: the visible part is then the app's top region at 1:1, exactly what the frame
   shows. */
.hw-poster {
  position: absolute; top: 0; left: 0; width: 1440px; height: 880px;
  object-fit: cover; object-position: top center;
  transform: scale(var(--hw-scale, 1)); transform-origin: 0 0;
  transition: opacity .45s ease;
}
.hero-window.hw-live .hw-poster { opacity: 0; pointer-events: none; }
/* Inert until asked for: otherwise a scroll that starts over the window is
   swallowed by the app's own scroll container instead of moving the page. */
.hero-window:not(.hw-active) .hw-frame { pointer-events: none; }

/* Day/night wipe (js/hero.js enterDev/renderWipe/leaveDev): for the whole device
   beat the window shows its poster pair, never the live frame — a live app can't
   be split into light/dark halves. Both shots stay stacked and visible at once
   (overriding the sitewide theme toggle above), and whichever one is the current
   theme's OPPOSITE is clipped to a sliver at the edge, growing to cover half the
   window as the visitor scrolls. Direction is picked once per beat entry
   (.wipe-from-right = light theme running, dark grows in from the right;
   .wipe-from-left = dark theme running, light grows in from the left) — never
   mid-scroll, so the two halves never fight over which one is "correct". */
.hero-window.dev-poster-forced .hw-frame { opacity: 0; pointer-events: none; }
.hero-window.dev-poster-forced .hw-poster { opacity: 1; pointer-events: none; }
.hero-window.dev-poster-forced .hw-poster.shot-light,
.hero-window.dev-poster-forced .hw-poster.shot-dark { display: block; }
.hero-window.wipe-from-right .hw-poster.shot-dark {
  clip-path: polygon(
    calc(var(--wipe-x, 100%) + var(--wipe-tilt, 3%)) 0%, 100% 0%, 100% 100%,
    calc(var(--wipe-x, 100%) - var(--wipe-tilt, 3%)) 100%
  );
}
.hero-window.wipe-from-left .hw-poster.shot-light {
  z-index: 1;   /* light is earlier in the DOM than dark — lift it above on this side */
  clip-path: polygon(
    0% 0%, calc(var(--wipe-x, 0%) + var(--wipe-tilt, 3%)) 0%,
    calc(var(--wipe-x, 0%) - var(--wipe-tilt, 3%)) 100%, 0% 100%
  );
}
/* The visible edge. An SVG line, not a skewed div: js/hero.js writes x1/x2 as the
   exact same wx+tilt / wx-tilt percentages the clip-path polygon above uses, in the
   same 0..100 coordinate space — so the line and the clip edge can never drift
   apart the way a fixed skew() angle would once the window's own aspect ratio
   changes per breakpoint. non-scaling-stroke keeps the 2px width constant even
   though preserveAspectRatio="none" stretches the viewBox non-uniformly. */
.hw-wipe-line {
  position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 0; pointer-events: none; z-index: 2;
  transition: opacity .3s ease;
}
.hw-wipe-line line {
  stroke: rgba(255, 255, 255, 0.85); stroke-width: 2;
  filter: drop-shadow(0 0 6px rgba(0, 0, 0, 0.35)) drop-shadow(0 0 2px rgba(0, 0, 0, 0.4));
}
.hero-window.dev-poster-forced .hw-wipe-line { opacity: 1; }

.hw-hint {
  position: absolute; left: 50%; bottom: 14px; transform: translateX(-50%);
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 15px; border-radius: 999px; border: 1px solid var(--line-2);
  background: var(--surface-2); -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px);
  font-family: var(--font-mono); font-size: 10.5px; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--dim); cursor: pointer; z-index: 3;
  transition: opacity .3s ease, transform .3s ease, color .16s;
}
.hw-hint:hover { color: var(--ink); }
.hw-hint .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--pos); box-shadow: 0 0 8px var(--pos); flex: none; }
.hero-window.hw-active .hw-hint { opacity: 0; transform: translate(-50%, 8px); pointer-events: none; }

/* -------------------------------------------------------------------------
 * 2a. Tablet and phone — the same app at its own breakpoints
 * ---------------------------------------------------------------------- */
/* Frames are drawn, not photographed: pure CSS means they follow the theme and
   cost nothing to download. JS sets left/top/width/height from the measured
   desktop window, so the trio stays composed at any viewport width. */
.dev-frame {
  position: absolute; z-index: 4; opacity: 0;
  padding: 9px; border-radius: 24px;
  background: var(--surface-2); border: 1px solid var(--line-2);
  box-shadow: 0 26px 64px rgba(0, 0, 0, 0.30), 0 3px 10px rgba(0, 0, 0, 0.10);
  transition: transform .7s cubic-bezier(.2, .7, .2, 1), opacity .5s ease;
  pointer-events: none;   /* decorative — only the desktop window is clickable */
}
:root[data-theme="dark"] .dev-frame { box-shadow: 0 26px 64px rgba(0, 0, 0, 0.6), 0 3px 10px rgba(0, 0, 0, 0.35); }
/* they arrive from their own side, with a slight tilt that settles */
.dev-tablet { transform: translateX(-58%) rotate(-5deg); }
.dev-phone  { transform: translateX(58%) rotate(5deg); border-radius: 34px; padding: 8px; }
.dev-frame.dev-in { opacity: 1; transform: none; }

.dev-screen { position: relative; width: 100%; height: 100%; overflow: hidden; border-radius: 15px; background: var(--bg); }
.dev-phone .dev-screen { border-radius: 27px; }
/* the capture's aspect ratio is what sizes the frame, so this lands exactly */
.dev-shot { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: top center; display: block; }
.shot-dark { display: none; }
:root[data-theme="dark"] .shot-light { display: none; }
:root[data-theme="dark"] .shot-dark { display: block; }
/* Day/night wipe, tablet and phone half: no clip-path here — per the brief, only
   the desktop window shows an actual light/dark split, these two just settle on
   their fixed target (tablet always lands light, phone always lands dark) as a
   whole. Both shots stacked and visible (same override as the desktop poster
   pair above); js/hero.js fades the non-target shot's opacity out over the wipe
   instead of an instant display swap — a no-op if that shot was already at 0. */
.dev-frame.wipe-forced .dev-shot.shot-light,
.dev-frame.wipe-forced .dev-shot.shot-dark { display: block; }
/* js/hero.js only ever animates the OVERLAY shot's opacity (the one that has to
   fade away to reveal the permanent one underneath) — tablet's overlay is
   shot-dark, phone's is shot-light. Markup order is light-then-dark for both, so
   shot-dark already paints on top with no help, which happens to be exactly what
   tablet needs. Phone needs the opposite stacking, so its overlay gets the same
   z-index lift the desktop poster's wipe-from-left case uses for the same reason
   — without it the phone's "fade" is invisible, hidden under the never-faded dark
   shot the whole time. */
.dev-phone.wipe-forced .dev-shot.shot-light { z-index: 1; }
/* the phone's dynamic-island pill, sitting over the screen */
.dev-notch {
  position: absolute; top: 15px; left: 50%; transform: translateX(-50%);
  width: 34%; height: 11px; border-radius: 999px; background: var(--ink); opacity: 0.85; z-index: 2;
}

/* The line the whole beat is building to. It centres by spanning the full stage
   (left:0/right:0 + text-align), so it must never be given a width or a horizontal
   offset — both pin it to the one viewport they were measured at and it lands off
   centre everywhere else. The size is a clamp for the same reason: a flat px value
   would overflow a 1025px-wide desktop, which is where this beat still runs. */
.dev-head {
  position: absolute; left: 0; right: 0; bottom: 44px; text-align: center;
  font-family: var(--font-display); font-size: clamp(26px, 3.4vw, 64px); font-weight: 700;
  letter-spacing: -0.03em; color: var(--ink);
  opacity: 0; transform: translateY(14px);
  transition: opacity .5s ease, transform .5s cubic-bezier(.2, .7, .2, 1);
  z-index: 5; pointer-events: none;
}
.dev-head.in { opacity: 1; transform: none; }
/* the day/night wipe's pay-off line — smaller, sits where .dev-head used to sit
   alone, only appears once the wipe (js/hero.js renderWipe) has settled */
.dev-sub {
  position: absolute; left: 0; right: 0; bottom: 10px; text-align: center;
  font-family: var(--font-display); font-size: clamp(14px, 1.6vw, 22px); font-weight: 600;
  letter-spacing: -0.01em; color: var(--dim);
  opacity: 0; transform: translateY(10px);
  transition: opacity .5s ease, transform .5s cubic-bezier(.2, .7, .2, 1);
  z-index: 5; pointer-events: none;
}
.dev-sub.in { opacity: 1; transform: none; }

/* -------------------------------------------------------------------------
 * 2b. Callout layer — arrows from outside pointing into the live dashboard
 * ---------------------------------------------------------------------- */
.hero-anno { position: absolute; inset: 0; pointer-events: none; z-index: 3; }
/* Above .anno-box (z-index 1): the start dot sits right on the card's edge and
   must read as sitting ON TOP of it, not tucked half-underneath. */
.anno-svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; z-index: 2; }

/* A sketched swoop: one hairline bezier curve (js/hero.js), rounded caps, a real
   dash pattern instead of a solid stroke, and a filled dot at BOTH ends (same
   size) instead of a dot-and-arrowhead pair — reads as a hand-drawn callout
   line, not a technical leader. */
.anno-lead { fill: none; stroke: var(--ink); stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; opacity: 0.7; }
.anno-dot, .anno-dot-end { fill: var(--ink); opacity: 0.7; }

/* Highlight instead of an outline: a soft blurred patch in the callout's accent,
   sitting over the element the arrow lands on. Reads as "look here" without
   drawing a box around live UI. */
.anno-glow { fill: var(--acc, var(--acc-feat)); opacity: 0; filter: url(#annoBlur); }
.anno-core { fill: var(--acc, var(--acc-feat)); opacity: 0; }

/* draw-on: the shaft's dashes slide into their resting phase, the end dot and the
   glow land a beat later. Once settled the shaft keeps drifting — same trick the
   copier page's wire diagram uses for its "live" routes (site.css .cp-wire.live /
   @keyframes cpFlow): stroke-dashoffset looping by exactly one dash-pattern
   length (6+5=11) so it repeats with no seam. Slower here on purpose — this is
   ambient motion on a page that just loaded, not a "data is flowing" indicator. */
.anno-g { opacity: 0; }
.anno-g.in { opacity: 1; }
.anno-g .anno-lead { stroke-dasharray: 6 5; stroke-dashoffset: var(--len, 400); }
.anno-g .anno-dot, .anno-g .anno-dot-end { opacity: 0; }
.anno-g.in .anno-lead {
  transition: stroke-dashoffset .5s cubic-bezier(.35, .7, .3, 1);
  stroke-dashoffset: 0;
  animation: annoFlow 2.6s linear .5s infinite;
}
@keyframes annoFlow { to { stroke-dashoffset: -11px; } }
.anno-g.in .anno-dot { transition: opacity .2s ease; opacity: 0.7; }
.anno-g.in .anno-dot-end { transition: opacity .2s ease .44s; opacity: 0.7; }
.anno-g .anno-glow, .anno-g .anno-core { transform-box: fill-box; transform-origin: center; transform: scale(0.72); }
.anno-g.in .anno-glow {
  transition: transform .42s cubic-bezier(.2, 1.25, .4, 1) .36s, opacity .42s ease .36s;
  transform: scale(1); opacity: 0.24;
}
.anno-g.in .anno-core {
  transition: transform .42s cubic-bezier(.2, 1.25, .4, 1) .40s, opacity .42s ease .40s;
  transform: scale(1); opacity: 0.11;
}

/* the box — the site's own card, tightened up and given a deliberate rhythm */
/* Width tracks the viewport so the box always fits the margin the zoom opens up.
   The title is a fixed 18px, not viewport-scaled, so the width that keeps
   "Build it the way you think" (the longest of the three) on one line is a
   fixed floor too — measured at 246px before it wraps, 258px is that plus a
   safety margin, held all the way down to the narrowest desktop breakpoint. */
.anno-box {
  position: absolute; z-index: 1; width: clamp(258px, 14vw, 304px);
  padding: 16px 18px 17px; border-radius: 14px;
  background: var(--surface-2); border: 1px solid var(--line-2);
  -webkit-backdrop-filter: blur(20px) saturate(140%); backdrop-filter: blur(20px) saturate(140%);
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.16);
  opacity: 0; transform: translateY(10px) scale(0.97);
  transition: opacity .38s ease, transform .38s cubic-bezier(.2, .7, .2, 1);
}
.anno-box.in { opacity: 1; transform: none; }
/* horizontal anchoring; the vertical position is set by JS from the target's own
   height, so a box always sits level with the thing it names */
.anno-box.anno-left { left: clamp(14px, 2.2vw, 48px); }
.anno-box.anno-right { right: clamp(14px, 2.2vw, 48px); }
.anno-eyebrow { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.18em; text-transform: uppercase; font-weight: 600; color: var(--acc-feat); }
.anno-box h3 { margin-top: 7px; font-family: var(--font-display); font-size: 18px; font-weight: 700; letter-spacing: -0.025em; line-height: 1.15; color: var(--ink); }
.anno-box ul { margin-top: 11px; display: flex; flex-direction: column; gap: 7px; }
.anno-box li { list-style: none; position: relative; padding-left: 14px; font-size: 12.5px; line-height: 1.4; color: var(--dim); }
.anno-box li::before { content: ''; position: absolute; left: 0; top: 7px; width: 4px; height: 4px; border-radius: 50%; background: var(--acc-feat); opacity: 0.75; }
/* each callout borrows the accent of the thing it points at */
.anno-box[data-anno="health"]  { --acc-feat: var(--acc-start); }
.anno-box[data-anno="aiFab"]   { --acc-feat: var(--acc-ai); }

/* -------------------------------------------------------------------------
 * 2c. Static fallback — the same three messages without the choreography
 * ---------------------------------------------------------------------- */
.anno-static { display: none; }
.anno-static-item h3 { margin-top: 7px; font-family: var(--font-display); font-size: clamp(17px, 2.2vw, 20px); font-weight: 700; letter-spacing: -0.025em; color: var(--ink); }
.anno-static-item ul { margin-top: 10px; display: flex; flex-direction: column; gap: 7px; }
.anno-static-item li { list-style: none; position: relative; padding-left: 14px; font-size: 13px; line-height: 1.45; color: var(--dim); }
.anno-static-item li::before { content: ''; position: absolute; left: 0; top: 7px; width: 4px; height: 4px; border-radius: 50%; background: var(--acc-feat); opacity: 0.75; }
.anno-static-item:nth-child(2) { --acc-feat: var(--acc-start); }
.anno-static-item:nth-child(3) { --acc-feat: var(--acc-ai); }

/* -------------------------------------------------------------------------
 * 3. The section that rides over the pinned window
 * ---------------------------------------------------------------------- */
/* Two things climb over the pinned window: .cover (products + stay synced, inside the
   pin) and .lift (pricing, after it). They behave identically, so the behaviour lives
   in one class — two copies of this drift apart the first time one of them is tuned.
   Opaque background is load-bearing: the running app sits right behind it.
   The pull-up must be matched by the runway above, or the card starts in the wrong place. */
.over {
  position: relative; z-index: 2;       /* .hero-stage is z-index 0 */
  margin-top: calc(-1 * var(--hw-h));
  background: var(--bg); border-radius: 26px 26px 0 0;
  box-shadow: 0 -26px 60px rgba(0, 0, 0, 0.16);
  border-top: 1px solid var(--line);
}
:root[data-theme="dark"] .over { box-shadow: 0 -26px 60px rgba(0, 0, 0, 0.44); }

/* .cover is the only one that leaves again, so it gets a bottom edge too — same
   radius, same shadow mirrored. Without it the moment the dashboard reappears is a
   straight cut across the screen instead of a card lifting off it. */
.cover {
  border-radius: 26px;
  border-bottom: 1px solid var(--line);
  box-shadow: 0 -26px 60px rgba(0, 0, 0, 0.16), 0 26px 60px rgba(0, 0, 0, 0.16);
}
:root[data-theme="dark"] .cover { box-shadow: 0 -26px 60px rgba(0, 0, 0, 0.44), 0 26px 60px rgba(0, 0, 0, 0.44); }

/* -------------------------------------------------------------------------
 * 4. Product split
 * ---------------------------------------------------------------------- */
.psplit { max-width: 1180px; margin: 0 auto; padding: clamp(50px, 9vh, 96px) clamp(20px, 4vw, 48px); }
.psplit-bare { padding-top: clamp(16px, 3vh, 34px); }   /* .phead already carries the heading */
.psplit-head { text-align: center; margin-bottom: clamp(28px, 5vh, 48px); }
.psplit-head h2 { font-family: var(--font-display); font-size: clamp(28px, 4.4vw, 46px); font-weight: 700; letter-spacing: -0.035em; line-height: 1.02; color: var(--ink); margin-top: 8px; }
.psplit-head p { margin: 12px auto 0; max-width: 520px; font-size: clamp(13.5px, 1.5vw, 16px); line-height: 1.55; color: var(--dim); }
.psplit-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: clamp(16px, 2.4vw, 26px); }

.pcard {
  display: flex; flex-direction: column; padding: clamp(24px, 3vw, 34px);
  border-radius: 16px; background: var(--surface-2); border: 1px solid var(--line);
  -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px);
  text-decoration: none; color: var(--ink);
  transition: transform .2s cubic-bezier(.2,.7,.2,1), box-shadow .2s ease, border-color .2s ease;
}
.pcard:hover, :root.kbd .pcard:focus-visible { transform: translateY(-4px); box-shadow: 0 16px 36px rgba(0,0,0,0.14); border-color: color-mix(in srgb, var(--pc-acc) 45%, var(--line)); }
.pcard-mark { width: 34px; height: 34px; color: var(--pc-acc); }
.pcard-mark svg { width: 100%; height: 100%; display: block; }
.pcard h3 { margin-top: 16px; font-family: var(--font-display); font-size: clamp(22px, 2.6vw, 30px); font-weight: 700; letter-spacing: -0.03em; color: var(--ink); }
.pcard-sub { margin-top: 9px; font-size: clamp(13px, 1.5vw, 15.5px); line-height: 1.55; color: var(--dim); }
.pcard-list { margin-top: 18px; display: flex; flex-direction: column; gap: 8px; }
.pcard-list li { list-style: none; position: relative; padding-left: 17px; font-size: 13.5px; line-height: 1.5; color: var(--dim); }
.pcard-list li::before { content: ''; position: absolute; left: 0; top: 8px; width: 6px; height: 6px; border-radius: 50%; background: var(--pc-acc); }
.pcard-foot { margin-top: auto; padding-top: 22px; display: inline-flex; align-items: center; gap: 8px; font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--pc-acc); }
.pcard-foot svg { width: 15px; height: 15px; transition: transform .18s ease; }
.pcard:hover .pcard-foot svg, :root.kbd .pcard:focus-visible .pcard-foot svg { transform: translateX(4px); }

/* -------------------------------------------------------------------------
 * 5. Pricing teaser
 * ---------------------------------------------------------------------- */
/* ===================== Pricing =====================
 * Der Kaufentscheid. Aufbau: drei Stufen → Zusatzprodukte → vollständige
 * Gegenüberstellung mit den Mengengrenzen als Zahlen → aufklappbares FAQ.
 *
 * Farbaufteilung: --acc-price (violett, steht schon in der Navigation) markiert den
 * Preisbereich; die Stufenfarben Grün/Blau sind die Abzeichen aus der App, damit eine
 * Stufe hier dieselbe Farbe trägt wie später im Produkt. Alles andere kommt aus den
 * bestehenden Bausteinen (.card, .btn, .feat-inner, .feat-eyebrow) — dieser Block
 * bringt bewusst KEINE eigenen Tokens mit.
 * ================================================================== */
.prsec { border-top: 1px solid var(--line); }
/* Hover-Schatten als Token, weil sie im dunklen Thema eine ganz andere Deckkraft
   brauchen — sonst müsste jede der drei Karten-Regeln dreimal dastehen. */
.prsec{
  --pr-hover-karte:0 16px 40px rgba(31,31,31,.11);
  --pr-hover-featured:0 26px 66px rgba(31,31,31,.15);
  --pr-hover-ext:0 14px 34px rgba(31,31,31,.10);
}
:root[data-theme="dark"] .prsec{
  --pr-hover-karte:0 16px 40px rgba(0,0,0,.42);
  --pr-hover-featured:0 26px 66px rgba(0,0,0,.50);
  --pr-hover-ext:0 14px 34px rgba(0,0,0,.38);
}
@media (prefers-color-scheme: dark){ :root:not([data-theme="light"]) .prsec{
  --pr-hover-karte:0 16px 40px rgba(0,0,0,.42);
  --pr-hover-featured:0 26px 66px rgba(0,0,0,.50);
  --pr-hover-ext:0 14px 34px rgba(0,0,0,.38);
} }
/* ===================== Abschnittskopf ===================== */
.pr-head{text-align:center; margin-bottom:clamp(22px,3vh,34px)}
.pr-sub{margin:12px auto 0; color:var(--dim); font-size:clamp(13px,1.8vw,16px); max-width:56ch}

/* Abrechnungsschalter — die Landing hat noch keinen, also neu, aber in ihrer Sprache:
   Mono-Versalien wie die Knöpfe, Pille wie die Hinweis-Pillen. */
.pr-billing{display:flex; align-items:center; justify-content:center; gap:14px; flex-wrap:wrap; margin-bottom:10px}
.pr-bill-opt{font-family:var(--font-mono); font-size:11.5px; letter-spacing:.14em; text-transform:uppercase;
             color:var(--faint); font-weight:500; transition:color .16s; cursor:pointer;
             background:none; border:0; padding:4px 2px}
.pr-bill-opt.on{color:var(--ink)}
.pr-switch{
  position:relative; width:58px; height:30px; border-radius:99px; cursor:pointer; flex:none;
  background:var(--surface-2); border:1px solid var(--line-2); padding:0;
  transition:background .18s, border-color .18s;
}
:root.kbd .pr-switch:focus-visible{outline:2px solid var(--acc-price); outline-offset:3px}
.pr-knob{
  position:absolute; top:3px; left:3px; width:22px; height:22px; border-radius:50%;
  background:var(--acc-price); transition:transform .2s cubic-bezier(.4,0,.2,1);
}
[data-billing="yearly"] .pr-knob{transform:translateX(28px)}
.pr-save{
  font-family:var(--font-mono); font-size:11px; letter-spacing:.1em; text-transform:uppercase; font-weight:500;
  color:var(--acc-price); background:color-mix(in srgb,var(--acc-price) 14%,transparent);
  border:1px solid color-mix(in srgb,var(--acc-price) 30%,transparent);
  border-radius:99px; padding:4px 11px;
}
/* Währungswahl + Steueraussage in einer Zeile: der Schalter steht dort, wo steht,
   was er ändert („VAT included" gilt nur für EUR). */
.pr-cur{
  display:flex; align-items:center; justify-content:center; gap:14px; flex-wrap:wrap;
  margin:0 0 clamp(26px,4vh,44px);
}
.pr-cur-seg{display:inline-flex; border:1px solid var(--line-2); border-radius:99px; padding:2px}
.pr-cur-opt{
  font-family:var(--font-mono); font-size:10.5px; letter-spacing:.1em;
  color:var(--faint); background:none; border:0; cursor:pointer;
  border-radius:99px; padding:4px 11px; transition:color .16s, background .16s;
}
.pr-cur-opt:hover{color:var(--dim)}
.pr-cur-opt.on{color:var(--ink); background:var(--surface-2)}
:root.kbd .pr-cur-opt:focus-visible{outline:2px solid var(--acc-price); outline-offset:2px}
.pr-cur-note{
  font-family:var(--font-mono); font-size:11px; letter-spacing:.08em;
  text-transform:uppercase; color:var(--faint);
}

/* ===================== Stufenkarten ===================== */
.pr-grid{display:grid; grid-template-columns:repeat(3,1fr); gap:clamp(14px,1.6vw,20px); align-items:start}
@media (max-width:900px){ .pr-grid{grid-template-columns:1fr; max-width:460px; margin:0 auto} }
.pr-card{padding:clamp(22px,2.4vw,30px); display:flex; flex-direction:column; position:relative; height:100%}
/* Die empfohlene Stufe wird nicht durch eine Rahmenfarbe hervorgehoben, sondern durch
   Höhe und Sättigung — sonst kämpft ein grüner Rahmen mit der violetten Sektionsfarbe. */
.pr-card.featured{background:var(--surface-2); border-color:var(--line-2); box-shadow:0 18px 50px rgba(31,31,31,.09)}
:root[data-theme="dark"] .pr-card.featured{box-shadow:0 18px 50px rgba(0,0,0,.4)}
@media (prefers-color-scheme: dark){ :root:not([data-theme="light"]) .pr-card.featured{box-shadow:0 18px 50px rgba(0,0,0,.4)} }
@media (min-width:901px){ .pr-card.featured{margin-top:-14px; padding-top:calc(clamp(22px,2.4vw,30px) + 14px)} }

.pr-top{display:flex; align-items:center; gap:0; margin-bottom:4px}   /* Abstand kommt aus .tier-badge{margin-left:5px} — sonst addiert er sich */
.pr-name{font-size:clamp(21px,2.4vw,26px); font-weight:700; letter-spacing:-.02em}
/* Stufen-Chips — 1:1 aus der App übernommen (css/styles.css, .tier-badge). Bewusst kopiert
   statt nachempfunden: derselbe Chip hängt im Journal an jedem gesperrten Element, und genau
   diese Wiedererkennung ist sein Zweck. Ändert er sich dort, muss er hier mitziehen. */
.tier-badge{
  font-size:9px; font-weight:750; padding:1px 5px; border-radius:4px;
  color:#fff; margin-left:5px; vertical-align:middle; letter-spacing:0.3px;
  display:inline-block; pointer-events:none;
}
.tier-badge-free{background:linear-gradient(135deg,#9aa1ad 0%,#5b6472 100%); box-shadow:0 1px 5px rgba(90,100,114,0.35)}
.tier-badge-plus{background:linear-gradient(135deg,#34d058 0%,#15803d 100%); box-shadow:0 1px 6px rgba(34,180,80,0.4)}
.tier-badge-pro{background:linear-gradient(135deg,#6ea8ff 0%,#3a6fe0 100%); box-shadow:0 1px 6px rgba(70,120,255,0.4)}
.pr-popular{
  margin-left:auto; font-family:var(--font-mono); font-size:9.5px; font-weight:500; letter-spacing:.12em;
  text-transform:uppercase; color:var(--acc-price);
  border:1px solid color-mix(in srgb,var(--acc-price) 34%,transparent);
  background:color-mix(in srgb,var(--acc-price) 12%,transparent);
  border-radius:99px; padding:3px 9px;
}
.pr-promise{color:var(--dim); font-size:13.5px; line-height:1.5; margin-bottom:18px; min-height:2.6em}
.pr-price{display:flex; align-items:baseline; gap:8px; margin-bottom:6px}
.pr-amount{font-size:clamp(34px,4.2vw,46px); font-weight:700; letter-spacing:-.035em; font-variant-numeric:tabular-nums}
/* Auch die Zahl im Tabellenkopf läuft beim Umschalten durch — ohne feste Ziffernbreite
   würde die Spalte dabei zappeln. */
.pr-table [data-price]{font-variant-numeric:tabular-nums}
.pr-per{font-family:var(--font-mono); font-size:12.5px; color:var(--faint); letter-spacing:.06em}
.pr-billnote{font-family:var(--font-mono); font-size:11px; color:var(--faint); letter-spacing:.05em; min-height:1.5em; margin-bottom:20px}
.pr-cta{width:100%; margin-bottom:22px}

.pr-list{list-style:none; display:flex; flex-direction:column; gap:11px; margin-top:auto}
.pr-list li{display:flex; align-items:flex-start; gap:10px; font-size:13.5px; line-height:1.45}
.pr-ico{width:17px; height:17px; flex:none; margin-top:1px}
.pr-list li.off{color:var(--faint)}
.pr-list li b{font-weight:600}
.pr-listhead{
  font-family:var(--font-mono); font-size:10px; letter-spacing:.16em; text-transform:uppercase;
  color:var(--faint); margin-top:2px;
}

/* ===================== Zusatzprodukte ===================== */
/* ===================== Trennlinien =====================
 * Die Haarlinie trägt den Satz, der die Entscheidung erklärt — deshalb Text IN der
 * Linie statt darüber: zwei getrennte Elemente lesen sich wie Überschrift und Regel,
 * eines wie eine Zwischenbemerkung. */
.pr-rule{display:flex; align-items:center; gap:clamp(16px,3vw,34px)}
.pr-rule::before, .pr-rule::after{content:""; flex:1 1 auto; height:1px; background:var(--line)}
/* flex:0 0 auto, nicht 0 1: sonst schrumpft der Satz zwischen den beiden Linien und
   bricht mit einem einzelnen Wort in die zweite Zeile. Die Linien geben nach, der Text
   nicht — erst die max-width zwingt ihn zum Umbruch. */
.pr-rule span{
  flex:0 0 auto; text-align:center; max-width:min(72ch, 78%);
  color:var(--dim); font-size:13.5px; line-height:1.5;
}
/* Die Linie bringt den Abstand mit; die Überschrift dahinter gibt ihren eigenen ab,
   sonst stünde er doppelt. */
.pr-rule-ext{margin-top:clamp(46px,7vh,72px)}
.pr-rule-ext + .pr-ext-head{margin-top:clamp(20px,3vh,30px)}
.pr-rule-suite{margin:clamp(30px,4vh,44px) 0 clamp(18px,2.5vh,26px)}
.pr-rule + .pr-suite-frame{margin-top:0}

/* ===================== Pro Extensions =====================
 * Bewusst dieselbe senkrechte Ordnung wie die Stufenkarten (Kopf → Versprechen →
 * Preis → Liste), damit der Abschnitt EIN System bleibt. Was sie als andere
 * Produktklasse ausweist, ist nicht ein anderes Layout, sondern der schwarze
 * .tier-badge-ext der App plus die eigene Marke im Kopf. */
.pr-ext-head{display:flex; align-items:baseline; gap:12px; margin:clamp(46px,7vh,72px) 0 16px; flex-wrap:wrap}
.pr-ext-head h3{font-size:clamp(18px,2.2vw,24px); font-weight:700; letter-spacing:-.02em}
.pr-ext-head span{color:var(--dim); font-size:13.5px}
.pr-exts{display:grid; grid-template-columns:repeat(2,1fr); gap:clamp(14px,1.6vw,20px); align-items:stretch}
/* Derselbe Umbruchpunkt wie .pr-grid (900px). Bei 760 stünden zwischen 761 und 900 px
   die Stufenkarten schon gestapelt in einer 460er Spalte, die Extensions daneben aber
   noch zweispaltig über die volle Breite — zwei Rhythmen in einem Abschnitt. */
@media (max-width:900px){ .pr-exts{grid-template-columns:1fr; max-width:460px; margin:0 auto} }
.pr-ext{padding:clamp(22px,2.4vw,30px); display:flex; flex-direction:column; height:100%}

.pr-ext-top{display:flex; align-items:center; gap:0; margin-bottom:12px}
/* Kachelmaß nach der Tinte, nicht nach dem viewBox: die Kopier-Marke sind drei
   überlagerte Scheiben mit dem Blitz darin. Damit der Blitz nicht zuläuft, braucht
   sie Fläche — 34 px Zeichenbreite in einer 50er Kachel. Kleiner ginge nur mit der
   vereinfachten Fassung, und die ist nicht die Marke. */
.pr-ext-mark{
  width:50px; height:50px; border-radius:13px; display:grid; place-items:center; flex:none;
  border:1px solid var(--line-2); margin-right:13px; color:var(--ink);
}
.pr-ext-mark svg{width:34px; height:auto; display:block}
.pr-ext-name{font-size:19px; font-weight:700; letter-spacing:-.02em}

.pr-ext-promise{color:var(--dim); font-size:13.5px; line-height:1.55; margin-bottom:18px}
.pr-ext-price{margin-bottom:6px}
.pr-ext-amount{font-size:clamp(28px,3vw,34px); font-weight:700; letter-spacing:-.03em; font-variant-numeric:tabular-nums}
/* Voraussetzung als Zeile, nicht als Fußnote: sie entscheidet, ob man das Produkt
   überhaupt buchen kann. Die Chips sind die der App. */
.pr-ext-needs{
  display:flex; align-items:center; margin-bottom:20px;
  font-family:var(--font-mono); font-size:10.5px; letter-spacing:.14em; text-transform:uppercase;
  color:var(--faint);
}
/* Die Häkchenliste der Stufenkarten schiebt sich per margin-top:auto nach unten —
   hier trägt der Paket-Streifen diese Rolle, die Liste sitzt direkt am Preis. */
.pr-ext .pr-list{margin-top:0; margin-bottom:22px}
.pr-pack{margin-top:auto; padding-top:16px; border-top:1px dashed var(--line-2)}
.pr-pack-label{
  display:block; margin-bottom:6px;
  font-family:var(--font-mono); font-size:9.5px; letter-spacing:.16em; text-transform:uppercase; color:var(--faint);
}
.pr-pack p{font-size:12.5px; line-height:1.5; color:var(--dim)}
.pr-pack b{color:var(--ink); font-weight:600}

/* Extension-Chip + Kombination — ebenfalls 1:1 aus css/styles.css des Journals.
   Einzige Anpassung: der Themenwähler, die App hängt ihn an body[data-theme]. */
.tier-badge-ext{
  background:linear-gradient(135deg, #3d3d3d 0%, #0b0b0b 52%, #262626 100%);
  border:1px solid rgba(255,255,255,0.10);
  box-shadow:0 1px 6px rgba(0,0,0,0.42), inset 0 1px 0 rgba(255,255,255,0.14);
  letter-spacing:0.5px;
}
:root[data-theme="dark"] .tier-badge-ext{
  background:linear-gradient(135deg, #525252 0%, #171717 52%, #343434 100%);
  border-color:rgba(255,255,255,0.16);
}
@media (prefers-color-scheme: dark){ :root:not([data-theme="light"]) .tier-badge-ext{
  background:linear-gradient(135deg, #525252 0%, #171717 52%, #343434 100%);
  border-color:rgba(255,255,255,0.16);
} }
/* Eigener Chip, weil die App für dieses Bündel keinen hat. Form, Größe und Gewicht
   kommen unverändert aus .tier-badge — nur der Verlauf ist neu. */
.tier-badge-suite{
  background:linear-gradient(135deg, #b06bff 0%, #ff2d92 52%, #ff8a1f 100%);
  box-shadow:0 1px 7px rgba(255,45,146,0.42);
}
.tier-badge-combo{display:inline-flex; align-items:center; margin-left:5px; vertical-align:middle}
.tier-badge-combo .tier-badge{margin-left:0}
/* „+" heißt: beides nötig. Im Journal steht er auf dunklem Grund, hier auf hellem — die
   App hat dafür dieselbe Ausnahme unter body[data-theme="light"]. */
.tier-badge-and{font-size:10px; font-weight:500; color:rgba(20,30,60,0.5); padding:0 4px}
:root[data-theme="dark"] .tier-badge-and{color:rgba(255,255,255,0.6)}
@media (prefers-color-scheme: dark){ :root:not([data-theme="light"]) .tier-badge-and{color:rgba(255,255,255,0.6)} }

/* ===================== Full Suite =====================
 * Das einzige Element im Abschnitt, das quer über die volle Breite geht — es fasst
 * die drei Produkte darüber zusammen, also darf es sie auch optisch überspannen.
 * Die Farbe (Violett → Pink → Orange) gibt es sonst nirgends auf der Seite; sie
 * beginnt beim Violett der Preissektion und läuft warm aus. Getragen wird sie von
 * der Haarlinie, nicht von der Fläche — eine bunte Fläche würde die ruhigen Karten
 * darüber erschlagen. */
.prsec{ --suite-1:#b06bff; --suite-2:#ff2d92; --suite-3:#ff8a1f; }
.pr-suite-frame{
  margin-top:clamp(14px,1.6vw,20px); border-radius:15px; padding:1px;
  background:linear-gradient(115deg, var(--suite-1) 0%, var(--suite-2) 48%, var(--suite-3) 100%);
}
.pr-suite{
  /* NICHT --surface-2: das ist durchscheinend, und der Verlauf des Rahmens läge dann
     unter der ganzen Fläche statt nur an der Kante. --pr-solid ist der deckende
     Zwilling, den auch die klebende Tabellenspalte benutzt. */
  border-radius:14px; background:var(--pr-solid);
  padding:clamp(22px,2.4vw,30px);
  display:flex; align-items:center; gap:clamp(20px,3vw,44px);
}
/* Der Preis darf nicht schrumpfen, der Text darf umbrechen. */
.pr-suite-left{flex:1 1 auto; min-width:0}
.pr-suite-right{flex:0 0 auto; text-align:right; min-width:210px}
@media (max-width:820px){
  .pr-suite{flex-direction:column; align-items:stretch; gap:22px}
  .pr-suite-right{text-align:left; min-width:0}
}
.pr-suite-top{display:flex; align-items:center; gap:0; margin-bottom:8px}
.pr-suite-name{font-size:clamp(19px,2.2vw,23px); font-weight:700; letter-spacing:-.02em; margin-left:9px}
/* Der Chip steht hier VOR dem Namen, also trägt er ausnahmsweise keinen linken Rand. */
.pr-suite-top .tier-badge{margin-left:0}
.pr-suite-promise{color:var(--dim); font-size:13.5px; line-height:1.55; max-width:62ch; margin-bottom:14px}
.pr-suite-parts{list-style:none; display:flex; flex-wrap:wrap; gap:8px 22px}
.pr-suite-parts li{display:flex; align-items:center; gap:7px; font-size:13px; color:var(--dim)}
.pr-suite-parts .tier-badge{margin-left:0}
/* Was im Bündel steckt, steht dort, wo es gebraucht wird — die Extension-Karten sagen
   dasselbe in ihrem „One package"-Streifen. */
.pr-suite-note{margin-top:12px; font-size:12.5px; line-height:1.5; color:var(--faint); max-width:62ch}

.pr-suite-priceline{display:flex; align-items:baseline; justify-content:flex-end; gap:8px; flex-wrap:wrap}
@media (max-width:820px){ .pr-suite-priceline{justify-content:flex-start} }
.pr-suite-was{
  font-size:15px; color:var(--faint); text-decoration:line-through; font-variant-numeric:tabular-nums;
}
.pr-suite-amount{font-size:clamp(30px,3.4vw,38px); font-weight:700; letter-spacing:-.035em; font-variant-numeric:tabular-nums}
.pr-suite .pr-billnote{margin-bottom:4px}
.pr-suite-save{
  font-family:var(--font-mono); font-size:11px; letter-spacing:.05em; margin-bottom:8px;
  color:var(--suite-2);
}
/* Der eigentliche Verkaufssatz: das Bündel kostet weniger als zwei seiner drei Teile.
   Steht direkt über dem Knopf, weil es das letzte Argument vor dem Klick ist. Das
   Skript blendet ihn aus, sobald die Preise das nicht mehr hergeben — siehe site.js. */
.pr-suite-beat{font-size:12.5px; font-weight:600; line-height:1.4; margin-bottom:16px}
.pr-suite-beat[hidden]{display:none}
.pr-suite-cta{width:100%}

/* ===================== Hover auf allen Preiskarten =====================
 * Extrem zurückhaltend: 3 px anheben, 0,8 % größer, Schatten eine Spur tiefer.
 * Nur bei echtem Zeiger — auf Touch bliebe der Zustand nach dem Tippen kleben. */
@media (hover:hover) and (pointer:fine){
  .pr-card, .pr-ext, .pr-suite-frame{transition:transform .22s cubic-bezier(.2,.7,.3,1), box-shadow .22s ease, border-color .22s ease}
  .pr-card:hover, .pr-ext:hover{transform:translateY(-3px) scale(1.008); border-color:var(--line-2)}
  /* Der Suite-Balken ist gut dreimal so breit — 0,8 % wären dort ein Sprung von 9 px.
     Halber Faktor, gleiche gefühlte Bewegung. */
  .pr-suite-frame:hover{transform:translateY(-3px) scale(1.004); box-shadow:var(--pr-hover-ext)}
  .pr-card:hover{box-shadow:var(--pr-hover-karte)}
  .pr-card.featured:hover{box-shadow:var(--pr-hover-featured)}
  .pr-ext:hover{box-shadow:var(--pr-hover-ext)}
}
@media (prefers-reduced-motion: reduce){
  .pr-card, .pr-ext, .pr-suite-frame{transition:none}
  .pr-card:hover, .pr-ext:hover, .pr-suite-frame:hover{transform:none}
}

/* Verweis am Kartenfuß auf die vollständige Tabelle. */
.pr-more{
  display:block; margin-top:14px; text-align:center; text-decoration:none;
  font-family:var(--font-mono); font-size:10.5px; letter-spacing:.12em; text-transform:uppercase;
  color:var(--faint); transition:color .16s;
}
.pr-more:hover{color:var(--acc-price)}

/* ===================== Mitlaufender Tabellenkopf =====================
 * Feste Leiste mit einer Kopie der Tabelle; sichtbar ist nur der Kopf plus die
 * Upsell-Reihe. Die Höhe steuert die Kopie selbst — kein fester Wert, sonst müsste
 * man ihn bei jeder Änderung an den Kopfzeilen nachziehen. */
.pr-sticky{
  position:fixed; z-index:20;            /* unter der Navigation (30), über dem Inhalt */
  overflow:hidden; pointer-events:none; opacity:0;
  border-bottom:1px solid var(--line-2); border-radius:0 0 14px 14px;
  background:var(--pr-solid);            /* deckend: darunter läuft die Tabelle durch */
  box-shadow:0 10px 26px rgba(31,31,31,.10);
  transform:translateY(-8px); transition:opacity .18s ease, transform .18s ease;
}
:root[data-theme="dark"] .pr-sticky{box-shadow:0 10px 26px rgba(0,0,0,.42)}
@media (prefers-color-scheme: dark){ :root:not([data-theme="light"]) .pr-sticky{box-shadow:0 10px 26px rgba(0,0,0,.42)} }
.pr-sticky.on{opacity:1; transform:none; pointer-events:auto}
/* Die Kopie enthält die ganze Tabelle; die Höhe der Laufbahn setzt das Skript auf die
   Kopfzeile, alles darunter wird abgeschnitten.
   WICHTIG: der Rumpf steht auf visibility, NICHT auf display:none. Die Spaltenbreiten
   einer Tabelle ergeben sich aus allen Zeilen — mit display:none fiele der Rumpf aus
   dem Layout und die Kopie wäre um einige Pixel gegen das Original verschoben. */
.pr-sticky-track{overflow:hidden}
.pr-sticky-track .pr-table tbody{visibility:hidden}
.pr-sticky-track .pr-table thead th{position:static; background:none; -webkit-backdrop-filter:none; backdrop-filter:none}
@media (max-width:640px){ .pr-sticky{display:none} }

/* Abschlusszeile der Tabelle: die Knöpfe sitzen in den Spalten, zu denen sie gehören —
   wer sich unten durch die Zeilen gearbeitet hat, muss nicht zurückscrollen. */
.pr-cta-row td{padding:18px; border-bottom:0}
.pr-cta-row td:first-child{border-bottom:0}
.pr-tbl-btn{
  display:block; text-align:center; text-decoration:none; white-space:nowrap;
  font-family:var(--font-mono); font-size:10.5px; letter-spacing:.12em; text-transform:uppercase;
  border-radius:99px; padding:8px 12px; border:1px solid var(--line-2); color:var(--dim);
  transition:color .16s, border-color .16s, background .16s, opacity .16s;
}
.pr-tbl-btn:hover{color:var(--ink); border-color:var(--ink)}
/* Plus ist die empfohlene Stufe — sie trägt als einzige eine Fläche. */
.pr-tbl-btn.plus{background:var(--ink); border-color:var(--ink); color:var(--bg)}
.pr-tbl-btn.plus:hover{opacity:.86; color:var(--bg)}

/* ===================== Vergleichstabelle ===================== */
.pr-table-head{display:flex; align-items:baseline; gap:12px; margin:clamp(46px,7vh,72px) 0 16px; flex-wrap:wrap}
.pr-table-head h3{font-size:clamp(18px,2.2vw,24px); font-weight:700; letter-spacing:-.02em}
.pr-table-head span{color:var(--dim); font-size:13.5px}
.pr-table-wrap{
  overflow-x:auto; border:1px solid var(--line); border-radius:14px; background:var(--surface-2);
  -webkit-backdrop-filter:blur(16px); backdrop-filter:blur(16px);
}
table.pr-table{width:100%; border-collapse:collapse; min-width:620px; font-size:13.5px}
.pr-table thead th{
  position:sticky; top:0; z-index:2; background:var(--surface-2);
  -webkit-backdrop-filter:blur(16px); backdrop-filter:blur(16px);
  text-align:left; padding:14px 18px; border-bottom:1px solid var(--line-2);
  font-family:var(--font-mono); font-size:10.5px; letter-spacing:.16em; text-transform:uppercase;
  color:var(--faint); font-weight:600; white-space:nowrap;
}
.pr-table thead th.c{text-align:center; width:132px}
.pr-table thead th.c b{display:block; font-size:12.5px; letter-spacing:.06em; color:var(--ink); font-weight:700; margin-bottom:2px}
.pr-table tbody td{padding:12px 18px; border-bottom:1px solid var(--line); vertical-align:middle}
.pr-table tbody tr:last-child td{border-bottom:0}
.pr-table td.c{text-align:center; font-variant-numeric:tabular-nums; font-weight:600; white-space:nowrap}
.pr-table td.lbl{color:var(--ink)}
.pr-table td.lbl small{display:block; color:var(--faint); font-size:11.5px; font-weight:400; margin-top:2px; line-height:1.4}
.pr-grp td{
  background:color-mix(in srgb,var(--ink) 3.5%,transparent);
  font-family:var(--font-mono); font-size:10px; letter-spacing:.18em; text-transform:uppercase;
  color:var(--dim); font-weight:600; padding:9px 18px;
}
/* Der einzige Ort, an dem die Sektionsfarbe in der Tabelle auftaucht: die Zeile,
   an der sich die Gratis-Stufe entscheidet. */
.pr-table tr.key td.lbl{font-weight:600}
.pr-table tr.key td.c.hit{color:var(--acc-price)}
.pr-inf{color:var(--faint); font-weight:500}
.pr-dash{color:var(--faint)}

/* Auf schmalen Schirmen scrollt die Tabelle in ihrem eigenen Rahmen seitwärts. Ohne
   mitlaufende Beschriftung sieht man dort eine Spalte voller Punkte und weiß nicht mehr,
   zu welcher Stufe sie gehören — die erste Spalte bleibt deshalb stehen.
   Der Hintergrund muss DECKEND sein, sonst scheint die wegscrollende Zeile durch:
   --surface-2 ist durchscheinend, --pr-solid ist dieselbe Farbe fertig überlagert. */
.prsec { --pr-solid: #fbfbfb; }
:root[data-theme="dark"] .prsec { --pr-solid: #262a32; }
@media (max-width: 760px) {
  .pr-table thead th:first-child,
  .pr-table td.lbl { position: sticky; left: 0; background: var(--pr-solid); z-index: 1; }
  .pr-table thead th:first-child { z-index: 3; }
  /* Kante statt Schatten — die Tabelle arbeitet ohnehin mit Haarlinien. */
  .pr-table td.lbl::after, .pr-table thead th:first-child::after {
    content: ""; position: absolute; top: 0; right: 0; bottom: 0; width: 1px; background: var(--line-2);
  }
  .pr-table td.lbl, .pr-table thead th:first-child { position: sticky; }
}

/* ===================== FAQ ===================== */
.pr-faq{margin-top:clamp(46px,7vh,72px)}
.pr-faq h3{font-size:clamp(18px,2.2vw,24px); font-weight:700; letter-spacing:-.02em; margin-bottom:16px}
.pr-faq details{border:1px solid var(--line); border-radius:12px; background:var(--surface-2); margin-bottom:10px;
                -webkit-backdrop-filter:blur(16px); backdrop-filter:blur(16px)}
.pr-faq summary{
  list-style:none; cursor:pointer; padding:16px 20px; display:flex; align-items:center; gap:14px;
  font-size:14.5px; font-weight:600; letter-spacing:-.01em;
}
.pr-faq summary::-webkit-details-marker{display:none}
:root.kbd .pr-faq summary:focus-visible{outline:2px solid var(--acc-price); outline-offset:-2px; border-radius:12px}
.pr-faq .pr-q-ico{
  margin-left:auto; flex:none; width:15px; height:15px; color:var(--faint);
  transition:transform .18s cubic-bezier(.4,0,.2,1);
}
.pr-faq details[open] .pr-q-ico{transform:rotate(45deg); color:var(--acc-price)}
/* Während des Zuklappens bleibt `open` gesetzt (sonst wäre der Text sofort weg) —
   das Zeichen soll trotzdem gleich zurückdrehen. */
.pr-faq details.faq-zu .pr-q-ico{transform:none; color:var(--faint)}
.pr-faq .pr-a{padding:0 20px 18px; color:var(--dim); font-size:13.5px; line-height:1.6; max-width:76ch}
@media (prefers-reduced-motion: reduce){ .pr-faq .pr-q-ico, .pr-knob{transition:none} }

/* -------------------------------------------------------------------------
 * 6. Page head — the shared banner on the product pages
 * ---------------------------------------------------------------------- */
.phead { max-width: 1320px; margin: 0 auto; padding: clamp(40px, 7vh, 78px) clamp(20px, 4vw, 48px) clamp(20px, 3vh, 34px); text-align: center; }
.phead h1 { margin-top: 12px; font-family: var(--font-display); font-size: clamp(34px, 5.6vw, 68px); font-weight: 700; letter-spacing: -0.04em; line-height: 0.98; color: var(--ink); }
.phead p { margin: 14px auto 0; max-width: 560px; font-size: clamp(14px, 1.6vw, 17px); line-height: 1.55; color: var(--dim); }
.pback { display: inline-flex; align-items: center; gap: 7px; font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--faint); text-decoration: none; transition: color .14s; }
.pback:hover { color: var(--ink); }
.pback svg { width: 14px; height: 14px; }

/* -------------------------------------------------------------------------
 * 7. Responsive + reduced motion
 * ---------------------------------------------------------------------- */
/* Tablet and phone: no pin — a sticky element taller than the viewport just fights
   the scroll. The frame stays, but unscaled: the app has its own responsive layout
   below 760px, and showing that is more honest than shrinking a desktop grid until
   it's unreadable. Taller box, because a phone-shaped app in a 16:10 slot is mostly
   empty space. */
@media (max-width: 1024px) {
  .run { display: none; }                           /* no runway, nothing pins */
  /* later in source than the @supports dvh override above, so this wins */
  .over { margin-top: 0; border-radius: 0; box-shadow: none; border-bottom: 0; }
  .hero-stage { position: static; height: auto; }
  .hero-devices { position: static; display: block; transform: none; }
  .hero-window { position: static; height: auto; width: min(760px, 94vw); transform: none; margin: 0 auto; }
  .hw-screen { flex: none; aspect-ratio: 4 / 5; }   /* height no longer comes from the pin */
  .hero-anno { display: none; }                     /* no pin → no arrows to draw */
  /* the device beat is pure choreography; without the pin there is nothing to
     choreograph, and the visitor is already holding one of the devices */
  .dev-frame, .dev-head, .dev-sub, .hw-wipe-line { display: none; }
  .anno-static {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
    gap: clamp(18px, 3vw, 30px);
    max-width: 760px; margin: clamp(28px, 5vh, 44px) auto 0;
    padding: 0 clamp(20px, 4vw, 48px);
  }
  .hw-frame { width: 100%; height: 100%; transform: none; }
  /* The poster is a 1440px desktop capture; here the app renders its own responsive
     layout, so showing it would make the handover a redesign rather than a swap. The
     window simply sits empty in the page's own colour until the app reports ready. */
  .hw-poster { display: none; }
}
@media (max-width: 640px) {
  .nav-drop-panel { display: none; }          /* the burger overlay owns navigation here */
  .hero-window { width: 94vw; border-radius: 12px; }
  .hw-bar { padding: 8px 11px; }
  .hw-screen { aspect-ratio: 3 / 4; }
  /* .kh-row switches to display:block here (site.css), which drops justify-content —
     and .kh-slot (10em, sized for the longest word) is wider than that block's own
     column, so margin:auto has nothing to center within (auto resolves to 0 once the
     child exceeds the container). Centering against the page instead, same trick as
     an unknown-width element: shift the near edge to the midpoint, then pull back by
     half its own (dynamic) width. .kh-slot is already position:relative (site.css). */
  .hv .kh-slot { left: 50%; transform: translateX(-50%); }
  /* That centers the reserved BOX (sized for "strategies"), not the word actually
     showing inside it — .kw sits left:0 in that box (site.css), so a shorter word
     like "edge" still renders hard against the box's left edge. Same centering trick,
     one level in. Uses the standalone `translate` property (not `transform`) because
     JS writes an inline transform for the vertical spin — translate composes with it
     instead of being overwritten by it. */
  .hv .kw { left: 50%; translate: -50% 0; }
}

@media (prefers-reduced-motion: reduce) {
  .run { display: none; }
  .over { margin-top: 0; border-radius: 0; box-shadow: none; border-bottom: 0; }
  /* the window's height normally comes from the pin; with the pin gone the screen
     has nothing to fill and would collapse to just the title bar */
  .hero-stage { position: static; height: auto; }
  .hero-devices { position: static; display: block; transform: none; }
  .hero-window { position: static; height: auto; transform: none; margin: 0 auto; }
  .hw-screen { flex: none; aspect-ratio: 1440 / 880; }
  /* a sequence that only exists as motion has nothing to offer here — swap in the
     plain block instead of animating it away */
  .hero-anno { display: none; }
  .dev-frame, .dev-head, .dev-sub, .hw-wipe-line { display: none; }
  .anno-static {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
    gap: 26px; max-width: 1180px; margin: 40px auto 0; padding: 0 clamp(20px, 4vw, 48px);
  }
  .nav-drop-panel, .prod-chip-go, .pcard, .hw-frame, .hw-poster, .hw-hint { transition: none; }
}

/* Keyboard users must always be able to see where they are — aber WIRKLICH nur die.
 * `:focus-visible` allein reicht dafür nicht: die Heuristik dahinter gehört dem
 * Browser, und manche Fassungen zeigen den Ring auch nach einem Mausklick (etwa wenn
 * zuvor einmal mit Tab navigiert wurde oder eine Bedienungshilfe aktiv ist). Dann
 * steht um jedes angeklickte Element ein farbiger Kasten.
 * `:root.kbd` ist die eigene, überprüfbare Fassung derselben Aussage: die Klasse
 * setzt js/site.js beim ersten Tab und nimmt sie bei jedem Zeigereingriff wieder weg.
 * Tastaturbedienung bleibt damit vollständig sichtbar, die Maus bleibt ruhig. */
:root.kbd :where(a, button, [tabindex]):focus-visible {
  outline: 2px solid var(--acc-feat); outline-offset: 3px; border-radius: 4px;
}

/* ===================== Seed-Programm: Keimling unten links =====================
 * Der Knopf wächst nach drei Sekunden aus der Ecke, die Blätter entfalten sich
 * nacheinander, dann öffnet die Blase. Danach bleibt der Knopf als Wiedereinstieg.
 *
 * Grün ist hier bewusst die EINZIGE Farbe außerhalb der Sektionsakzente: das Seed-
 * Programm ist kein Produkt, sondern eine Einladung, und die soll sich vom Rest der
 * Seite absetzen. Beide Grüntöne sind die der Plus-Stufe aus der App — dieselbe
 * Familie, kein neuer Farbwert. */
.seedling {
  --seed-1: #34d058;
  --seed-2: #15803d;
  position: fixed; left: clamp(14px, 3vw, 28px); bottom: clamp(14px, 3vw, 28px);
  z-index: 60;                /* über dem Inhalt, unter dem Menü-Overlay (100) */
  display: flex; align-items: flex-end; gap: 12px;
}

/* ---------- der Knopf ---------- */
.seedling-btn {
  position: relative; flex: 0 0 auto;
  width: 56px; height: 56px; padding: 0; border: 0; border-radius: 50%;
  cursor: pointer; color: #fff;
  background: linear-gradient(140deg, var(--seed-1) 0%, var(--seed-2) 100%);
  box-shadow: 0 10px 28px rgba(21, 128, 61, .34);
  /* Startzustand: nicht da. Erst .wach holt ihn herein. */
  transform: scale(.2) translateY(14px); opacity: 0;
  transition: transform .5s cubic-bezier(.2,1.5,.4,1), opacity .3s ease, box-shadow .2s ease;
}
.seedling.wach .seedling-btn { transform: none; opacity: 1; }
.seedling-btn:hover { box-shadow: 0 14px 34px rgba(21, 128, 61, .45); }
:root.kbd .seedling-btn:focus-visible { outline: 3px solid var(--seed-1); outline-offset: 3px; }

.seedling-mark { width: 38px; height: 38px; display: block; margin: auto; }
.seedling-mark .seed-ring { display: none; }   /* die Fläche ist der Knopf selbst */
.seedling-mark .seed-soil,
.seedling-mark .seed-leaf-r,
.seedling-mark .seed-leaf-l { fill: currentColor; }
.seedling-mark .seed-stem {
  fill: none; stroke: currentColor; stroke-width: 3.4; stroke-linecap: round;
}

/* Das Entfalten: Erde steht zuerst, dann schiebt sich der Stängel hoch, dann klappen
   die Blätter auf — jedes um seinen eigenen Ansatzpunkt, sonst wachsen sie aus der
   Bildmitte statt aus dem Trieb. */
.seedling-mark .seed-soil  { transform-origin: 32px 51px; }
.seedling-mark .seed-stem  { transform-origin: 32px 47px; }
.seedling-mark .seed-leaf-r{ transform-origin: 34px 30px; }
.seedling-mark .seed-leaf-l{ transform-origin: 30px 36px; }
.seedling-mark .seed-soil,
.seedling-mark .seed-stem,
.seedling-mark .seed-leaf-r,
.seedling-mark .seed-leaf-l { transform: scale(0); opacity: 0; }
.seedling.wach .seedling-mark .seed-soil,
.seedling.wach .seedling-mark .seed-stem,
.seedling.wach .seedling-mark .seed-leaf-r,
.seedling.wach .seedling-mark .seed-leaf-l {
  transform: none; opacity: 1;
  transition: transform .46s cubic-bezier(.2,1.4,.4,1), opacity .26s ease;
}
.seedling.wach .seedling-mark .seed-soil   { transition-delay: .10s; }
.seedling.wach .seedling-mark .seed-stem   { transition-delay: .22s; }
.seedling.wach .seedling-mark .seed-leaf-l { transition-delay: .38s; }
.seedling.wach .seedling-mark .seed-leaf-r { transition-delay: .50s; }

/* Ein einziger Puls beim Auftauchen — nicht dauerhaft. Ein pulsierender Punkt, der
   nie aufhört, ist eine Werbebanner-Geste; einmal heißt „hier bin ich". */
.seedling-pulse {
  position: absolute; inset: -4px; border-radius: 50%;
  border: 2px solid var(--seed-1); opacity: 0; pointer-events: none;
}
.seedling.wach .seedling-pulse { animation: seedPuls 1.6s ease-out .7s 2; }
@keyframes seedPuls {
  from { transform: scale(.9); opacity: .55; }
  to   { transform: scale(1.5); opacity: 0; }
}

/* ---------- die Blase ---------- */
.seedling-bubble {
  position: relative; width: min(340px, calc(100vw - 84px));
  padding: 20px 20px 18px; border-radius: 18px;
  background: var(--surface-2); border: 1px solid var(--line);
  -webkit-backdrop-filter: blur(18px); backdrop-filter: blur(18px);
  box-shadow: 0 22px 60px rgba(31, 31, 31, .16);
  /* Ursprung unten links: sie wächst aus dem Keimling heraus, nicht aus dem Nichts. */
  transform-origin: 0 100%;
  transform: scale(.86) translate(-8px, 12px); opacity: 0; visibility: hidden;
  transition: transform .34s cubic-bezier(.2,.9,.25,1), opacity .24s ease, visibility 0s linear .34s;
}
.seedling.offen .seedling-bubble {
  transform: none; opacity: 1; visibility: visible;
  transition: transform .42s cubic-bezier(.2,1.1,.3,1), opacity .28s ease, visibility 0s;
}
:root[data-theme="dark"] .seedling-bubble { box-shadow: 0 22px 60px rgba(0, 0, 0, .55); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .seedling-bubble { box-shadow: 0 22px 60px rgba(0, 0, 0, .55); }
}

/* Der Zipfel zeigt auf den Keimling. */
.seedling-bubble::after {
  content: ""; position: absolute; left: -7px; bottom: 20px;
  width: 14px; height: 14px; transform: rotate(45deg);
  background: var(--surface-2); border-left: 1px solid var(--line); border-bottom: 1px solid var(--line);
}

.seedling-x {
  position: absolute; top: 10px; right: 10px; width: 28px; height: 28px;
  display: grid; place-items: center; cursor: pointer;
  background: transparent; border: 0; border-radius: 8px; color: var(--faint);
  transition: color .15s, background .15s;
}
.seedling-x svg { width: 15px; height: 15px; }
.seedling-x:hover { color: var(--ink); background: color-mix(in srgb, var(--ink) 7%, transparent); }

.seedling-eyebrow {
  display: inline-block; font-family: var(--font-mono);
  font-size: 9.5px; font-weight: 700; letter-spacing: .16em; text-transform: uppercase;
  color: var(--seed-2); margin-bottom: 6px;
}
:root[data-theme="dark"] .seedling-eyebrow { color: var(--seed-1); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .seedling-eyebrow { color: var(--seed-1); }
}
.seedling-title { font-size: 19px; font-weight: 700; letter-spacing: -.02em; margin: 0 0 10px; }
.seedling-bubble p { font-size: 13px; line-height: 1.55; color: var(--dim); margin: 0 0 9px; }
.seedling-bubble p b { color: var(--ink); font-weight: 600; }
.seedling-aside { font-size: 12.5px !important; color: var(--faint) !important; }

/* Der Dauervorteil bekommt eine eigene Zeile mit Haken — er ist das Argument, das
   über die drei Freimonate hinaus trägt. */
.seedling-keep {
  display: flex; gap: 9px; align-items: flex-start;
  margin: 12px 0 16px; padding: 11px 12px; border-radius: 12px;
  background: color-mix(in srgb, var(--seed-1) 11%, transparent);
  border: 1px solid color-mix(in srgb, var(--seed-1) 30%, transparent);
  font-size: 12.5px; line-height: 1.5; color: var(--dim);
}
.seedling-keep b { color: var(--ink); font-weight: 600; }
.seedling-keep i { font-style: normal; color: var(--faint); }
.seedling-keep-mark { flex: 0 0 auto; color: var(--seed-2); }
:root[data-theme="dark"] .seedling-keep-mark { color: var(--seed-1); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .seedling-keep-mark { color: var(--seed-1); }
}
.seedling-keep-mark svg { width: 18px; height: 18px; display: block; }

.seedling-cta {
  display: block; text-align: center; text-decoration: none; white-space: nowrap;
  font-size: 13.5px; font-weight: 600; letter-spacing: .01em; color: #fff;
  padding: 12px 16px; border-radius: 11px;
  background: linear-gradient(120deg, var(--seed-1) 0%, var(--seed-2) 100%);
  box-shadow: 0 8px 20px rgba(21, 128, 61, .3);
  transition: transform .16s ease, box-shadow .16s ease, filter .16s ease;
}
.seedling-cta:hover { transform: translateY(-1px); box-shadow: 0 12px 26px rgba(21, 128, 61, .42); filter: saturate(1.1); }
.seedling-cta:active { transform: none; }

/* Auf schmalen Schirmen sitzt die Blase über dem Knopf statt daneben. */
@media (max-width: 620px) {
  .seedling { flex-direction: column-reverse; align-items: flex-start; gap: 10px; }
  .seedling-bubble { transform-origin: 0 100%; }
  .seedling-bubble::after { left: 20px; bottom: -7px; transform: rotate(-45deg); }
}

@media (prefers-reduced-motion: reduce) {
  .seedling-btn, .seedling-bubble,
  .seedling-mark .seed-soil, .seedling-mark .seed-stem,
  .seedling-mark .seed-leaf-r, .seedling-mark .seed-leaf-l { transition: none !important; }
  .seedling.wach .seedling-pulse { animation: none; }
  .seedling-cta:hover { transform: none; }
}

/* ===================== Seed-Seite =====================
 * Eigene Sektionsfarbe: Grün. Sie gehört keinem Produkt, sondern der Einladung —
 * deshalb taucht sie sonst nirgends auf der Seite auf. Beide Töne sind die der
 * Plus-Stufe aus der App, also kein neuer Farbwert im System. */
.sdsec, .sdhead { --seed-1: #34d058; --seed-2: #15803d; }
.sdhead .feat-eyebrow, .sdsec .feat-eyebrow { color: var(--seed-2); }
:root[data-theme="dark"] .sdhead .feat-eyebrow,
:root[data-theme="dark"] .sdsec .feat-eyebrow { color: var(--seed-1); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .sdhead .feat-eyebrow,
  :root:not([data-theme="light"]) .sdsec .feat-eyebrow { color: var(--seed-1); }
}

/* Der Keimling über der Überschrift — dieselbe Zeichnung wie im Eck-Widget. */
.sd-mark {
  display: grid; place-items: center; width: 76px; height: 76px; margin: 0 auto 6px;
  border-radius: 50%; color: #fff;
  background: linear-gradient(140deg, var(--seed-1) 0%, var(--seed-2) 100%);
  box-shadow: 0 14px 34px rgba(21, 128, 61, .3);
}
.sd-mark svg { width: 50px; height: 50px; display: block; }
.sd-mark .seed-soil, .sd-mark .seed-leaf-r, .sd-mark .seed-leaf-l { fill: currentColor; }
.sd-mark .seed-stem { fill: none; stroke: currentColor; stroke-width: 3.4; stroke-linecap: round; }

/* ---------- die drei Karten ---------- */
.sd-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(14px, 1.6vw, 20px); align-items: stretch; }
@media (max-width: 900px) { .sd-grid { grid-template-columns: 1fr; max-width: 460px; margin: 0 auto; } }
.sd-card { padding: clamp(20px, 2.4vw, 28px); display: flex; flex-direction: column; height: 100%; }
/* Die Nummerierung trägt hier echte Bedeutung: es ist eine Reihenfolge — was du
   bekommst, was du gibst, was danach bleibt. */
.sd-num {
  font-family: var(--font-mono); font-size: 11px; font-weight: 700; letter-spacing: .16em;
  color: var(--seed-2); margin-bottom: 10px;
}
:root[data-theme="dark"] .sd-num { color: var(--seed-1); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .sd-num { color: var(--seed-1); } }
.sd-card h3 { font-size: 17px; font-weight: 700; letter-spacing: -.02em; margin-bottom: 8px; }
.sd-card p { font-size: 13.5px; line-height: 1.55; color: var(--dim); }
.sd-card p b { color: var(--ink); font-weight: 600; }
.sd-card p i { font-style: normal; color: var(--faint); }
/* Die erste Karte trägt das Angebot — sie darf die Farbe führen. */
.sd-card-lead {
  border-color: color-mix(in srgb, var(--seed-1) 42%, var(--line));
  background: color-mix(in srgb, var(--seed-1) 7%, var(--surface-2));
}

.sd-aside {
  margin-top: clamp(18px, 2.5vh, 26px); text-align: center;
  font-size: 13.5px; color: var(--faint);
}

/* ---------- Anmeldeteil ---------- */
.sd-join { display: grid; grid-template-columns: 1fr minmax(300px, 380px); gap: clamp(24px, 4vw, 56px); align-items: start; }
@media (max-width: 900px) { .sd-join { grid-template-columns: 1fr; } }
.sd-join-copy p { margin-top: 12px; color: var(--dim); font-size: clamp(13.5px, 1.6vw, 15px); line-height: 1.6; max-width: 52ch; }

.sd-facts { list-style: none; margin-top: 22px; display: flex; flex-direction: column; gap: 10px; }
.sd-facts li { display: flex; align-items: baseline; gap: 12px; font-size: 13.5px; color: var(--dim); }
.sd-facts b {
  flex: 0 0 68px; font-family: var(--font-mono); font-size: 10px; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase; color: var(--faint);
}

.sd-form {
  padding: clamp(20px, 2.4vw, 28px); border-radius: 16px;
  background: var(--surface-2); border: 1px solid var(--line);
  -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px);
  display: flex; flex-direction: column; gap: 14px;
}
.sd-field { display: flex; flex-direction: column; gap: 6px; }
.sd-field > span {
  font-family: var(--font-mono); font-size: 10px; font-weight: 700; letter-spacing: .14em;
  text-transform: uppercase; color: var(--faint);
}
.sd-field > span i { font-style: normal; opacity: .7; }
.sd-field input {
  font: inherit; font-size: 14px; color: var(--ink);
  padding: 11px 13px; border-radius: 10px;
  border: 1px solid var(--line-2); background: var(--bg);
  transition: border-color .16s, box-shadow .16s;
}
.sd-field input::placeholder { color: var(--faint); }
.sd-field input:focus {
  outline: none; border-color: var(--seed-1);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--seed-1) 22%, transparent);
}
.sd-check { display: flex; align-items: flex-start; gap: 9px; font-size: 12.5px; line-height: 1.45; color: var(--dim); cursor: pointer; }
.sd-check input { flex: 0 0 auto; width: 16px; height: 16px; margin-top: 1px; accent-color: var(--seed-2); }

.sd-submit {
  font: inherit; font-size: 14px; font-weight: 600; color: #fff; cursor: pointer;
  padding: 13px 18px; border-radius: 11px; border: 0;
  background: linear-gradient(120deg, var(--seed-1) 0%, var(--seed-2) 100%);
  box-shadow: 0 8px 20px rgba(21, 128, 61, .3);
  transition: transform .16s ease, box-shadow .16s ease, filter .16s ease;
}
.sd-submit:hover { transform: translateY(-1px); box-shadow: 0 12px 26px rgba(21, 128, 61, .42); filter: saturate(1.1); }
.sd-submit:disabled { opacity: .55; cursor: default; transform: none; box-shadow: none; }
.sd-note { font-size: 11.5px; line-height: 1.5; color: var(--faint); }
.sd-note.ok { color: var(--seed-2); font-weight: 600; }
.sd-note.err { color: var(--neg); font-weight: 600; }
:root[data-theme="dark"] .sd-note.ok { color: var(--seed-1); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .sd-note.ok { color: var(--seed-1); } }

@media (prefers-reduced-motion: reduce) {
  .sd-submit:hover { transform: none; }
}

/* ===================== „Coming soon" — Chip und Schleier =====================
 * Zwei Bauteile, die immer zusammen auftreten:
 *   .soon-chip   sagt es dauerhaft, klein, oben rechts, mit Erklärung im Tooltip
 *   .soon-veil   sagt es beim Darüberfahren, groß, über der ganzen Box
 * Der Schleier ist bewusst NUR im Hover da. Eine Box, die dauerhaft hinter Milchglas
 * liegt, kann man nicht lesen — und was man nicht lesen kann, will man auch später
 * nicht kaufen. Erst hinschauen dürfen, dann erfahren, dass es noch dauert. */
.soon { position: relative; }

.soon-chip {
  position: absolute; top: 14px; right: 14px; z-index: 4;   /* über dem Schleier: die
     Erklärung darf nicht mit verschwimmen, sonst erklärt sie nichts */
  font-family: var(--font-mono); font-size: 9px; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase; white-space: nowrap;
  color: var(--warn, #b7791f); cursor: help;
  border: 1px solid color-mix(in srgb, var(--warn, #b7791f) 42%, transparent);
  background: color-mix(in srgb, var(--warn, #b7791f) 13%, var(--surface-2));
  border-radius: 5px; padding: 3px 7px;
}

.soon-veil {
  position: absolute; inset: 0; z-index: 3; border-radius: inherit;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 7px;
  background: color-mix(in srgb, var(--bg) 58%, transparent);
  -webkit-backdrop-filter: blur(7px) saturate(.85); backdrop-filter: blur(7px) saturate(.85);
  color: var(--ink);
  opacity: 0; pointer-events: none;                 /* nie klickbar: der Link darunter
     soll weiter funktionieren, man darf ja lesen gehen */
  transition: opacity .2s ease;
}
.soon-veil svg { width: 26px; height: 26px; color: var(--faint); }
.soon-veil b { font-size: 14px; font-weight: 700; letter-spacing: -.01em; }
.soon-veil i {
  font-style: normal; font-family: var(--font-mono);
  font-size: 9.5px; letter-spacing: .14em; text-transform: uppercase; color: var(--faint);
}
/* Nur mit echtem Zeiger. Auf Touch bliebe der Schleier nach dem Tippen kleben und
   verdeckte genau den Inhalt, den man sehen wollte. */
@media (hover: hover) and (pointer: fine) {
  .soon:hover > .soon-veil { opacity: 1; }
}

/* Die Suite-Box hat innen ihre eigene Fläche — der Schleier gehört dorthin, nicht auf
   den Verlaufsrahmen, sonst läge er über der Haarlinie. */
.pr-suite.soon { border-radius: 14px; }
