/* likwest.com — landing-page design system (apex hub + per-app landing pages).
   Light canvas with large flat organic shapes (the "scene": drifting bands,
   cropped rings, morphing blobs), scroll reveals. Legal/support pages keep
   using shared/style.css; this file is for the marketing pages only.
   Per-page accent: override --brand / --brand-2 in a page <style>. */

/* Self-hosted Inter (latin, variable 400–800) so the type looks identical and
   crisp on every browser/OS — the system stack renders SF Pro on Apple but
   Segoe UI on Windows. Absolute path so it also loads on the app subdomains. */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("/assets/fonts/inter-latin.woff2") format("woff2");
}
/* Geist — the wordmark face: cool, technical, engineered (Vercel's type).
   Inter stays the body/UI text face. */
@font-face {
  font-family: "Geist";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/assets/fonts/geist-latin.woff2") format("woff2");
}

:root {
  --bg: #f2f3f6;
  --bg-elev: #ffffff;
  --bg-card: #ffffff;
  --text: #14161f;
  --dim: #4e5464;
  --faint: #868da0;
  --line: rgba(18, 24, 48, 0.13);
  --line-soft: rgba(18, 24, 48, 0.075);

  --brand: #3564f2;          /* deep blue — likwest default (light bg) */
  --brand-2: #6f95ff;        /* lighter blue */
  --gold: #c8860a;           /* dark gold — readable on the light canvas */

  --radius: 20px;
  --radius-lg: 28px;
  --wrap: 1120px;
  --nav-h: 64px;

  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

* { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--nav-h) + 16px);
  color-scheme: light;
}

/* ------------------------------------------------------------- dark mode
   Light is the default. /shared/theme.js stamps data-theme="dark" on <html>
   before first paint (stored choice, else system preference); the navbar
   moon toggle (site.js) flips + persists it. Accents go back to their
   brighter values — the darkened light-canvas tints would drown on dark.
   Per-app accent pages override their dark --brand in their own <style>. */
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #0c0d12;
  --bg-elev: #14161e;
  --bg-card: rgba(255, 255, 255, 0.035);
  --text: #f2f3f8;
  --dim: #a4a9ba;
  --faint: #707588;
  --line: rgba(255, 255, 255, 0.11);
  --line-soft: rgba(255, 255, 255, 0.065);
  --brand: #6f95ff;
  --brand-2: #9db8ff;
  --gold: #e8b23a;
}
[data-theme="dark"] nav.top {
  background: color-mix(in srgb, #0c0d12 70%, transparent);
}
[data-theme="dark"] body::after { opacity: 0.05; mix-blend-mode: normal; }
[data-theme="dark"] .btn.ghost { background: rgba(255, 255, 255, 0.04); }
[data-theme="dark"] .shot-nav { background: rgba(20, 22, 32, 0.6); }
[data-theme="dark"] .shot-nav:hover { background: rgba(30, 33, 46, 0.75); }
[data-theme="dark"] .float-chip {
  background: rgba(20, 22, 32, 0.6);
  box-shadow: 0 14px 34px -12px rgba(0, 0, 0, 0.7);
}
[data-theme="dark"] .card,
[data-theme="dark"] .app-card { background: var(--bg-card); box-shadow: none; }
[data-theme="dark"] .card:hover { background: rgba(255, 255, 255, 0.055); }
[data-theme="dark"] .app-card .platforms span { background: rgba(255, 255, 255, 0.03); }
[data-theme="dark"] .chip { background: rgba(255, 255, 255, 0.04); }
[data-theme="dark"] .prose .contact { background: rgba(255, 255, 255, 0.04); }
[data-theme="dark"] .phone {
  box-shadow:
    0 40px 90px -30px rgba(0, 0, 0, 0.85),
    0 0 0 1px rgba(255, 255, 255, 0.08),
    inset 0 0 0 1px rgba(0, 0, 0, 0.9);
}
/* scene: same shapes, night tones — a touch lighter than the canvas */
[data-theme="dark"] .scene .blob { background: linear-gradient(150deg, #181b26, #10121a); }
[data-theme="dark"] .scene .b2 { background: linear-gradient(200deg, #161923, #10121a); }
[data-theme="dark"] .scene .b3 { background: linear-gradient(120deg, #151823, #111319); }
[data-theme="dark"] .scene .ring { border-color: #181b27; }
[data-theme="dark"] .scene .flow stop:first-child { stop-color: #161927; }
[data-theme="dark"] .scene .flow stop:last-child { stop-color: #10121a; }
[data-theme="dark"] .gridlines {
  background-image: radial-gradient(rgba(160, 180, 255, 0.11) 1.1px, transparent 1.6px);
}
/* liquid glass, night version: faint white fill, dimmer rim */
[data-theme="dark"] .feature .copy::before {
  background:
    radial-gradient(120% 90% at 16% 0%, rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0) 44%) padding-box,
    linear-gradient(135deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.03) 55%, rgba(255, 255, 255, 0.06)) padding-box,
    linear-gradient(135deg, rgba(255, 255, 255, 0.35), rgba(255, 255, 255, 0.06) 45%, rgba(255, 255, 255, 0.28)) border-box;
  box-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.25),
    0 22px 55px -28px rgba(0, 0, 0, 0.7);
}
[data-theme="dark"] .feature:has(.row-link):hover .copy::before {
  filter: brightness(1.2);
  box-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.35),
    0 30px 70px -30px rgba(0, 0, 0, 0.8);
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 16px/1.65 "Inter", -apple-system, BlinkMacSystemFont, "SF Pro Text",
        "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

a { color: inherit; text-decoration: none; }
img { max-width: 100%; display: block; }

/* Geist is used ONLY for the "lìkwest" company wordmark (hero + nav) and its
   tagline. Everything else — including app-page headlines like "Duet with
   yourself" — stays on Inter. */
.brand-name, .brand-word, .brand-sub {
  font-family: "Geist", "Inter", sans-serif;
}
/* Wortmarke rein schwarz, auch das ì. Das <span class="acc"> um das ì
   bleibt im Markup (es steht auf ueber hundert Seiten, viele davon
   generiert), traegt aber keine eigene Farbe mehr. */
.brand-name, .brand-word { color: var(--text); font-weight: 600; }

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

::selection { background: color-mix(in srgb, var(--brand) 40%, transparent); }

/* ----------------------------------------------------------------- scene
   Large FLAT organic shapes with defined edges — sweeping SVG bands,
   cropped rings, slowly morphing blobs — in blue-grey tones across the
   WHOLE page. Each page injects <div class="scene"> right after <body>:

     <div class="scene" aria-hidden="true">
       <span class="blob b1"></span>
       <svg class="flow f1" …>   (band; inline so gradients/paths stay per-page)
       <span class="ring r1"></span>
       …
     </div>

   Motion is glacial (30–60s loops) and CSS-only. */

body { position: relative; }

.scene {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  overflow: hidden;
  animation: scene-fade 1.2s ease-out both;
}
.scene .blob,
.scene .ring,
.scene .flow { position: absolute; }

/* morphing blobs */
.scene .blob {
  background: linear-gradient(150deg, #f7f8fb, #e6e9f1);
  animation: blob-morph 30s ease-in-out infinite alternate,
             scene-drift 64s ease-in-out infinite alternate;
}
.scene .b1 {
  width: 58vw; height: 50vw;
  right: -14vw; top: -10vw;
  border-radius: 58% 42% 55% 45% / 52% 48% 60% 40%;
}
.scene .b2 {
  width: 46vw; height: 40vw;
  right: -14vw; top: 64%;
  border-radius: 47% 53% 58% 42% / 55% 46% 54% 45%;
  background: linear-gradient(200deg, #f4f6fa, #e5e8f1);
  animation-delay: -14s, -20s;
}
.scene .b3 {
  width: 44vw; height: 36vw;
  left: -16vw; top: 80%;
  border-radius: 55% 45% 44% 56% / 48% 58% 42% 52%;
  background: linear-gradient(120deg, #e9ecf3, #f2f4f8);
  animation-delay: -7s, -32s;
}

/* flat rings, cropped by the page edge */
.scene .ring {
  border-radius: 50%;
  border-style: solid;
  border-color: #e4e7ef;
  animation: ring-breathe 28s ease-in-out infinite alternate;
}
.scene .r1 {
  width: 46vw; height: 46vw;
  border-width: 7.5vw;
  left: -18vw; top: 24%;
}

/* sweeping bands (inline SVGs, full width) */
.scene .flow { left: 0; width: 100%; height: auto; }
.scene .f1 {
  top: 3vw;
  animation: flow-sway 46s ease-in-out infinite alternate;
}
.scene .f2 {
  top: 56%;
  animation: flow-sway-flip 54s ease-in-out infinite alternate;
}

@keyframes blob-morph {
  0%   { border-radius: 58% 42% 55% 45% / 52% 48% 60% 40%; }
  50%  { border-radius: 45% 55% 48% 52% / 60% 44% 52% 48%; }
  100% { border-radius: 52% 48% 60% 40% / 46% 58% 44% 56%; }
}
@keyframes ring-breathe {
  from { transform: scale(1) rotate(0deg); }
  to   { transform: scale(1.05) rotate(8deg); }
}
@keyframes scene-drift {
  from { transform: translate3d(0, 0, 0) scale(1.03); }
  to   { transform: translate3d(-3vw, 4vw, 0) scale(0.97); }
}
@keyframes flow-sway {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-2.5vw, 1vw, 0); }
}
@keyframes flow-sway-flip {
  from { transform: scaleX(-1) translate3d(0, 0, 0); }
  to   { transform: scaleX(-1) translate3d(-2.5vw, 1vw, 0); }
}
@keyframes scene-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* faint blueprint dots behind the hero, fading downwards */
.gridlines {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background-image: radial-gradient(rgba(35, 55, 120, 0.10) 1.1px, transparent 1.6px);
  background-size: 24px 24px;
  -webkit-mask-image: radial-gradient(120% 90% at 50% 0%, #000 30%, transparent 75%);
          mask-image: radial-gradient(120% 90% at 50% 0%, #000 30%, transparent 75%);
}

/* film-grain so gradients don't band — a whisper of it on light */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 60;
  opacity: 0.025;
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.6'/%3E%3C/svg%3E");
}

/* ------------------------------------------------------------------ nav */

nav.top {
  position: sticky;
  top: 0;
  z-index: 50;
  height: var(--nav-h);
  backdrop-filter: blur(18px) saturate(1.6);
  -webkit-backdrop-filter: blur(18px) saturate(1.6);
  background: color-mix(in srgb, #ffffff 72%, transparent);
  border-bottom: 1px solid var(--line-soft);
}
nav.top .wrap {
  position: relative;      /* anchors the centred .nav-context */
  height: 100%;
  display: flex;
  align-items: center;
  gap: 28px;
}

/* centred context label — JS fills it from the current section's
   data-nav-title (apex: "My Apps") and toggles .on while one is active */
.nav-context {
  position: absolute;
  left: 50%;
  transform: translate(-50%, 4px);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--dim);
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.3s var(--ease), transform 0.3s var(--ease);
}
.nav-context.on { opacity: 1; transform: translate(-50%, 0); }
@media (max-width: 720px) { .nav-context { display: none; } }

/* scrollspy: the nav link of the section being read takes the page accent */
nav.top .links a.is-current { color: var(--brand); }

/* dark-mode toggle (JS-built): moon in light mode, sun in dark mode */
button.theme-toggle {
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: transparent;
  color: var(--dim);
  cursor: pointer;
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease),
              transform 0.2s var(--ease);
}
button.theme-toggle:hover {
  color: var(--text);
  border-color: color-mix(in srgb, var(--brand) 45%, var(--line));
  transform: translateY(-1px);
}
button.theme-toggle svg { display: block; }
button.theme-toggle .sun { display: none; }
[data-theme="dark"] button.theme-toggle .moon { display: none; }
[data-theme="dark"] button.theme-toggle .sun { display: block; }

/* app pages: the sail mark ahead of the app logo — breadcrumb back to the
   likwest apex (absolute URL: app pages live on their own subdomains).
   Styled as a black rounded tile so it matches the 30px app icon next to
   it (and the apex's ~30px bare sail — no size jump between pages). */
a.parent-mark {
  position: relative;
  display: block;
  flex: none;
  margin-right: -4px;      /* tighten the .wrap gap to breadcrumb spacing */
  transition: transform 0.2s var(--ease), filter 0.2s var(--ease);
}
a.parent-mark:hover {
  transform: translateY(-1px);
  filter: brightness(1.06);
}
a.parent-mark::after {
  content: "/";
  position: absolute;
  right: -16px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--faint);
  font-size: 15px;
}
.logo {
  display: flex;
  align-items: center;
  gap: 11px;
  font-weight: 700;
  font-size: 19px;
  letter-spacing: -0.02em;
}
.logo img, .logo svg { width: 30px; height: 30px; border-radius: 8px; }
/* Das Segel ist schmal und steht als offene Kontur da - mit dem Abstand der
   quadratischen App-Icons faellt es vom Schriftzug ab. Die 1,2-fache Breitung
   frisst davon nochmal gut einen Pixel je Seite auf, weil sie das Zeichen
   ueber seine Layout-Box hinaus malt. `:has()` grenzt es auf die
   Segel-Lockups ein, die App-Seiten behalten ihre 11px. */
.logo:has(> svg.v2-nav-mark),
.logo:has(> svg.v2-foot-mark) { gap: 0.316em; align-items: baseline; }
/* the tile-less lìkwest mark: taller than the glyphs so the slim boat has
   the same visual presence as the 30px square app icons next to their names */
.logo img.likwest-mark { height: 1.4em; width: auto; border-radius: 0; }
/* THE likwest nav tile — one shared class for every navbar appearance of the
   sail (apex logo lockup + app pages' breadcrumb). 30px liquid glass: white
   frosted fill, bright rim, soft shadow; the white mainsail gets a hair of
   drop-shadow so it reads on the light glass. Markup:
   <span class="likwest-tile"><img src="/assets/likwest-mark.svg" …></span> */
.likwest-tile {
  width: 30px;
  height: 30px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: 8px;
  background: linear-gradient(160deg, rgba(255, 255, 255, 0.65), rgba(255, 255, 255, 0.3));
  border: 1px solid rgba(255, 255, 255, 0.9);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  box-shadow: 0 4px 12px -6px rgba(24, 32, 64, 0.3);
}
.likwest-tile img {
  width: auto;
  height: 22px;
  border-radius: 0;
  display: block;
  filter: drop-shadow(0 1px 1.5px rgba(24, 32, 64, 0.3));
}
[data-theme="dark"] .likwest-tile {
  background: linear-gradient(160deg, rgba(255, 255, 255, 0.16), rgba(255, 255, 255, 0.06));
  border-color: rgba(255, 255, 255, 0.3);
}
.logo .app-ico {
  border-radius: 8px;
  box-shadow: 0 4px 12px -6px rgba(24, 32, 64, 0.3);   /* same lift as .likwest-tile */
}

nav.top .links {
  display: flex;
  gap: 26px;
  margin-left: auto;
  align-items: center;
  font-size: 14.5px;
  color: var(--dim);
}
nav.top .links a:hover { color: var(--text); }
nav.top .links a.cta {
  color: #fff;
  background: var(--brand);
  padding: 7px 16px;
  border-radius: 999px;
  font-weight: 600;
  transition: transform 0.2s var(--ease), box-shadow 0.2s var(--ease);
}
nav.top .links a.cta:hover {
  transform: translateY(-1px);
  box-shadow: 0 8px 24px -6px color-mix(in srgb, var(--brand) 55%, transparent);
}
/* Phone navbar: logo + dark-mode toggle + language switch, nothing else.
   The section anchors go, and so does the Download CTA — nothing is lost, the
   App Store badge sits in the hero directly below and again at the foot of
   every page. The language switch survives via its own override further down;
   on a phone it is the only route to the other language.

   Dropping the CTA alone does NOT stop the app wordmark from wrapping — that
   was measured, not assumed. At 375px the budget is: 327px content (375 − 2×24
   padding) − 30px parent mark − ~86px toggle+switch − 2×28px .wrap gap = 139px
   for a logo that wants 169px (30 icon + 11 gap + 128 label). So the gaps come
   down here too, and nowrap makes the one-line guarantee explicit rather than a
   coincidence of arithmetic.

   The "/" between the lìkwest mark and the app icon has to go with them: it is
   an absolutely positioned ::after at `right: -16px`, i.e. it lives *inside*
   the 28px .wrap gap. Shrink that gap and the slash slides under the app icon
   instead of disappearing politely. Two adjacent icons 16px apart read fine
   without it, and the mark keeps its aria-label, so nothing is lost but ink. */
@media (max-width: 640px) {
  nav.top .links a { display: none; }
  nav.top .wrap { gap: 16px; }
  nav.top .links { gap: 10px; }
  nav.top .logo { white-space: nowrap; }
  nav.top .parent-mark::after { content: none; }
}

/* ----------------------------------------------------------------- type */

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--dim);
}
.eyebrow::before {
  content: "";
  width: 7px; height: 7px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--brand), var(--brand-2));
}

h1.display {
  margin: 18px 0 0;
  font-size: clamp(42px, 7.4vw, 88px);
  line-height: 1.02;
  letter-spacing: -0.045em;
  font-weight: 800;
}
/* a smaller second line under a big first line — kept to one line */
h1.display .line-2 {
  display: inline-block;
  margin-top: 0.12em;
  font-size: clamp(28px, 4.7vw, 56px);
  letter-spacing: -0.03em;
  white-space: nowrap;
}

/* apex brand hero: the wordmark alone, type-first — the sail mark stays in
   the nav. All white in Geist, one blue accent on the ì; no frame, no box.
   The soft hero-glow aurora behind it is the only stage light. */
.brand-hero {
  width: fit-content;
  margin-bottom: 34px;
}
.brand-name {
  font-size: clamp(54px, 6.2vw, 82px);
  letter-spacing: -0.03em;
  line-height: 0.98;
  white-space: nowrap;
}
.brand-sub {
  margin-top: -0.08em;
  font-size: clamp(15px, 1.78vw, 21px);
  color: var(--dim);
  white-space: nowrap;
  /* tracked out so the tagline spans the same width as "lìkwest" above */
  letter-spacing: 0.235em;
  text-indent: 0.235em;   /* balance the trailing letter-spacing → centred track */
}
@media (max-width: 880px) {
  /* mobile: centre the whole logo lockup (lede stays left) */
  .brand-hero { margin-left: auto; margin-right: auto; }
}
.grad {
  background: linear-gradient(100deg, var(--brand) 0%, var(--brand-2) 55%, var(--brand) 110%);
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent;
}
.grad-gold {
  background: linear-gradient(100deg, #e0a52f, var(--gold) 45%, #9a6607);
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent;
}

.lede {
  margin: 22px 0 0;
  max-width: 560px;
  font-size: clamp(17px, 2vw, 20px);
  line-height: 1.6;
  color: var(--dim);
}
.lede strong { color: var(--text); font-weight: 600; }

h2.title {
  margin: 14px 0 0;
  font-size: clamp(30px, 4.4vw, 48px);
  line-height: 1.08;
  letter-spacing: -0.035em;
  font-weight: 750;
}
.sub {
  margin: 16px 0 0;
  color: var(--dim);
  font-size: 17px;
  max-width: 520px;
}
h2.title .pro {
  display: inline-block;
  vertical-align: middle;
  margin-left: 14px;
  padding: 4px 12px;
  border: 1px solid color-mix(in srgb, var(--gold) 55%, transparent);
  border-radius: 999px;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--gold);
  transform: translateY(-4px);
}

/* "Beta" badge — for apps not yet on the App Store (e.g. IPTV Pano). Tinted in
   the app's own accent (the card's --card-accent, else the page --brand) so it
   reads as part of the brand. Sits inline next to a title or in an eyebrow. */
.beta {
  --beta-c: var(--card-accent, var(--brand));
  display: inline-block;
  vertical-align: middle;
  margin-left: 10px;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--beta-c);
  background: color-mix(in srgb, var(--beta-c) 15%, transparent);
  border: 1px solid color-mix(in srgb, var(--beta-c) 50%, transparent);
  transform: translateY(-1px);
}

.sub strong { color: var(--text); font-weight: 600; }

/* icon chip ahead of a feature/mode title (app pages) — a rounded tile
   tinted in the page brand, holding a small stroke icon */
h2.title.with-ico {
  display: flex;
  align-items: center;
  gap: 14px;
}
h2.title .t-ico {
  flex: none;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border-radius: 12px;
  /* deliberately neutral, not the accent: a coloured tile per feature title
     reads as a row of badges competing with the headline. Grey keeps the
     structure and lets the accent stay on links, buttons and the .grad text. */
  color: var(--dim);
  background: color-mix(in srgb, var(--text) 5%, transparent);
  border: 1px solid var(--line);
}
h2.title .t-ico svg { display: block; width: 22px; height: 22px; }
@media (max-width: 880px) {
  h2.title .t-ico { width: 36px; height: 36px; border-radius: 10px; }
  h2.title .t-ico svg { width: 19px; height: 19px; }
}

/* -------------------------------------------------------------- buttons */

.actions {
  display: flex;
  align-items: center;
  gap: 18px;
  margin-top: 34px;
  flex-wrap: wrap;
}
.btn {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 13px 24px;
  border-radius: 999px;
  font-size: 15.5px;
  font-weight: 600;
  transition: transform 0.2s var(--ease), box-shadow 0.2s var(--ease),
              background 0.2s var(--ease), border-color 0.2s var(--ease);
}
.btn.primary {
  background: var(--brand);
  color: #fff;
}
.btn.primary:hover {
  transform: translateY(-2px);
  box-shadow: 0 12px 32px -8px color-mix(in srgb, var(--brand) 55%, transparent);
}
.btn.ghost {
  border: 1px solid var(--line);
  color: var(--text);
  background: #fff;
}
.btn.ghost:hover {
  border-color: color-mix(in srgb, var(--brand) 60%, var(--line));
  transform: translateY(-2px);
}
.aside-note { font-size: 13.5px; color: var(--faint); }

/* App Store badge (official look, HTML+CSS) */
/* Apples Original-Badge (Marketing Resources, US/UK schwarz). Die
   Richtlinien verlangen das unveraenderte Artwork - deshalb liegt es als
   Bild vor und wird nicht mehr aus Glyph + Inter nachgebaut. Das Markup
   bleibt wie es war: der Text darin traegt weiter den Linknamen fuer
   Screenreader und wird nur optisch entfernt.
   Groesse: natives Verhaeltnis 119.66 x 40, Mindesthoehe 40px, dazu der
   von Apple geforderte Freiraum von einem Zehntel der Hoehe. */
a.store-badge {
  display: inline-block;
  width: 140px;
  height: 47px;
  margin: 5px;
  background: url("/assets/app-store-badge.svg") left center / contain no-repeat;
  border: 0;
  border-radius: 0;
  transition: transform 0.2s var(--ease);
}
a.store-badge:hover { transform: translateY(-2px); }
/* Optisch weg, fuer Screenreader da. */
a.store-badge > * {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ----------------------------------------------------------------- hero */

header.hero {
  position: relative;
  overflow: hidden;
  padding: clamp(44px, 6vh, 76px) 0 clamp(32px, 4.5vh, 56px);
}
header.hero .wrap {
  position: relative;
  z-index: 1;
}
.hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
  gap: clamp(36px, 5vw, 72px);
  align-items: start;            /* top-align so the tall phone doesn't push the copy down */
}
/* nudge the copy column down a touch so it isn't glued to the nav */
.hero-grid > div:first-child { padding-top: clamp(12px, 5vh, 56px); }
@media (max-width: 880px) {
  .hero-grid { grid-template-columns: 1fr; }
  .hero-grid > div:first-child { padding-top: 0; }
  /* On mobile the hero phone sits BELOW the copy, so there's nothing to align
     to — let the brand lockup start high, close under the (sticky) nav, and
     trim the bottom so the first section follows without a big void. */
  header.hero { padding-top: clamp(10px, 2vh, 24px); padding-bottom: clamp(16px, 3vh, 36px); }
  /* Musaic: drop the hero phone on mobile — the "Mosaic Layers" section right
     below shows the same shot, so it's just extra scroll before the content.
     Videcho opts out with .keep-mobile: its hero video is the only visual, so
     it must stay on phones. */
  .hero-visual:not(.keep-mobile) { display: none; }
  .hero-visual.keep-mobile { margin-top: 6px; }
  .hero-visual.keep-mobile .phone { width: min(280px, 72vw); }
  /* Musaic: keep the note beside the App Store badge, not on its own line. */
  .actions-inline { flex-wrap: nowrap; align-items: center; gap: 14px; }
  .actions-inline .store-badge { flex: 0 0 auto; }   /* keep badge at natural size, don't squash it */
  .actions-inline .aside-note { flex: 1 1 auto; min-width: 0; margin: 0; font-size: 12px; line-height: 1.35; }
  /* Keep the apex's two hero buttons side by side (equal halves) instead of
     stacking — saves the wasted vertical space. Scoped so app-page heroes
     (store badge + note) keep wrapping. */
  .actions-duo { flex-wrap: nowrap; gap: 12px; }
  .actions-duo .btn {
    flex: 1 1 0;
    min-width: 0;
    justify-content: center;
    padding: 13px 12px;
    font-size: 14px;
    white-space: nowrap;
  }
  .actions-duo .btn .btn-ico { width: 18px; height: 18px; }
}

/* ---------------------------------------------------------- phone frame */

.phone {
  position: relative;
  width: min(320px, 78vw);
  margin: 0 auto;
  border-radius: 54px;
  padding: 12px;
  background: linear-gradient(160deg, #3a3a46, #17171d 40%, #101016);
  box-shadow:
    0 34px 80px -30px rgba(24, 32, 64, 0.45),
    0 0 0 1px rgba(255, 255, 255, 0.4),
    inset 0 0 0 1px rgba(0, 0, 0, 0.85);
}
.phone::before {
  /* Dynamic-Island pill */
  content: "";
  position: absolute;
  top: 24px; left: 50%;
  width: 88px; height: 26px;
  transform: translateX(-50%);
  background: #000;
  border-radius: 999px;
  z-index: 2;
}
.phone::after {
  /* accent glow behind the device */
  content: "";
  position: absolute;
  inset: -12%;
  z-index: -1;
  border-radius: 50%;
  background: radial-gradient(closest-side,
      color-mix(in srgb, var(--brand) 20%, transparent), transparent 72%);
  filter: blur(30px);
}
.phone img,
.phone video {
  display: block;
  border-radius: 42px;
  width: 100%;
  height: auto;   /* the width/height attrs otherwise stretch the media */
}
.phone video { object-fit: cover; }

/* crossfading screenshot slideshow inside a phone frame */
.phone .shots {
  position: relative;
  width: 100%;
  aspect-ratio: 840 / 1827;
  border-radius: 42px;
  overflow: hidden;
}
.phone .shots .shot {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 42px;
  opacity: 0;
  transition: opacity 0.9s var(--ease);
}
.phone .shots .shot.is-on { opacity: 1; }
/* no-JS: show only the first shot */
html:not(.js) .phone .shots .shot:not(.is-on) { display: none; }
@media (prefers-reduced-motion: reduce) {
  .phone .shots .shot { transition: none; }
}

/* app icon inside a button (e.g. the hero "Musaic" ghost button) */
.btn .btn-ico {
  width: 22px;
  height: 22px;
  border-radius: 6px;
  margin-right: -1px;
}

/* slideshow prev/next controls under the hero phone (JS-only) */
.shot-controls {
  display: flex;
  justify-content: center;
  gap: 16px;
  margin-top: 24px;
}
html:not(.js) .shot-controls { display: none; }
.shot-nav {
  width: 46px;
  height: 46px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  color: var(--text);
  background: rgba(255, 255, 255, 0.78);
  border: 1px solid var(--line);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  cursor: pointer;
  transition: transform 0.2s var(--ease), border-color 0.2s var(--ease),
              background 0.2s var(--ease);
}
.shot-nav:hover {
  transform: translateY(-2px);
  border-color: color-mix(in srgb, var(--brand) 55%, var(--line));
  background: #fff;
}
.shot-nav:active { transform: translateY(0); }
.shot-nav svg { display: block; }

.shot-dots {
  display: flex;
  align-items: center;
  gap: 9px;
}
.shot-dot {
  width: 8px;
  height: 8px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: var(--faint);
  opacity: 0.55;
  cursor: pointer;
  transition: width 0.28s var(--ease), background 0.28s var(--ease),
              opacity 0.28s var(--ease);
}
.shot-dot:hover { opacity: 0.85; }
.shot-dot.is-on {
  width: 22px;
  background: var(--brand);
  opacity: 1;
}
.phone.tilt { transform: rotate(2.4deg); }
.phone.tilt-l { transform: rotate(-2.4deg); }

/* ------------------------------------------------------------- tv frame */
/* 16:9 sibling of .phone, for apps whose screenshots are TV-shaped.
   Same slideshow markup (.shots / .shot / .shot-controls) - site.js finds
   the controls via the .visual / .hero-media wrapper, so no JS changes. */

.tv {
  position: relative;
  width: min(620px, 100%);
  margin: 0 auto;
  border-radius: 20px;
  padding: 10px;
  background: linear-gradient(160deg, #3a3a46, #17171d 40%, #101016);
  box-shadow:
    0 34px 80px -30px rgba(24, 32, 64, 0.45),
    0 0 0 1px rgba(255, 255, 255, 0.4),
    inset 0 0 0 1px rgba(0, 0, 0, 0.85);
}
.tv::before {
  /* stand */
  content: "";
  position: absolute;
  left: 50%; bottom: -9px;
  width: 24%; height: 9px;
  transform: translateX(-50%);
  border-radius: 0 0 7px 7px;
  background: linear-gradient(180deg, #23232b, #14141a);
}
.tv::after {
  /* accent glow behind the device */
  content: "";
  position: absolute;
  inset: -14%;
  z-index: -1;
  border-radius: 50%;
  background: radial-gradient(closest-side,
      color-mix(in srgb, var(--brand) 20%, transparent), transparent 72%);
  filter: blur(30px);
}
.tv img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 11px;
}
.tv .shots {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: 11px;
  overflow: hidden;
}
.tv .shots .shot {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 11px;
  opacity: 0;
  transition: opacity 0.9s var(--ease);
}
.tv .shots .shot.is-on { opacity: 1; }
html:not(.js) .tv .shots .shot:not(.is-on) { display: none; }
@media (prefers-reduced-motion: reduce) {
  .tv .shots .shot { transition: none; }
}
.tv.tilt { transform: rotate(1.4deg); }
.tv.tilt-l { transform: rotate(-1.4deg); }

/* a TV is wider than a phone, so a hero-sized one takes more of the column.
   Deliberately not the full width: at 940px the hero screenshot dominated
   everything above it. */
.tv-wide { width: min(705px, 100%); }

/* ------------------------------------------- crossfade between two devices */
/* Same feature shown on a TV and on a phone. The slides here are the FRAMES,
   not the images - a 16:9 shot and a 9:19.5 shot can't share one box.
   Both frames are taken out of flow and stacked, and site.js sets the
   container height to whichever is showing. Sizing the box to the TALLEST
   frame instead would park the switch a phone-height below a TV shot; here the
   box (and the switch under it) follows the active device. */
.device-shots {
  position: relative;
  transition: height 0.45s var(--ease);
}
.device-shots > .shot {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  opacity: 0;
  transition: opacity 0.55s var(--ease);
  pointer-events: none;
}
.device-shots > .shot.is-on { opacity: 1; }
/* no JS = no height sync and nothing to switch: show the first frame in flow */
html:not(.js) .device-shots > .shot { position: static; transform: none; }
html:not(.js) .device-shots > .shot:not(.is-on) { display: none; }
@media (prefers-reduced-motion: reduce) {
  .device-shots,
  .device-shots > .shot { transition: none; }
}
/* inside a feature column the phone stays a bit narrower, so the two frames
   read as roughly the same object rather than the phone dwarfing the TV */
.device-shots > .phone { width: min(268px, 68vw); }
/* pin the ratios so a frame has its final height before the (lazy) image
   loads - otherwise the first height measurement can land on the bezel only */
.device-shots > .tv img { aspect-ratio: 16 / 9; }
.device-shots > .phone img { aspect-ratio: 840 / 1826; }

/* the TV / phone switch under a .device-shots crossfade (JS-only, like
   .shot-controls - without JS there is nothing to switch between) */
.device-switch {
  display: flex;
  justify-content: center;
  gap: 6px;
  margin-top: 20px;
  padding: 5px;
  width: fit-content;
  margin-inline: auto;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: color-mix(in srgb, var(--bg-elev) 70%, transparent);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
}
html:not(.js) .device-switch { display: none; }
.dev-btn {
  width: 40px;
  height: 32px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: none;
  color: var(--faint);
  cursor: pointer;
  transition: color 0.2s var(--ease), background 0.2s var(--ease);
}
.dev-btn:hover { color: var(--dim); }
.dev-btn.is-on {
  color: #fff;
  background: var(--brand);
}
.dev-btn svg { display: block; }

/* floating glass chips around the hero phone */
.float-chip {
  position: absolute;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 15px;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  background: rgba(255, 255, 255, 0.75);
  border: 1px solid var(--line);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: 0 14px 34px -14px rgba(20, 30, 60, 0.25);
  white-space: nowrap;
}
.float-chip b { font-weight: 700; color: var(--gold); }
@media (max-width: 880px) { .float-chip { display: none; } }

/* Notiz wie im App-Store-Promo: goldener Punkt auf der Stelle im Bild,
   eine duenne Linie und das Label rechts neben dem Telefon. --x/--y sind
   Prozent des .phone-Rahmens, --reach wie weit das Label hinausragt. */
.v2-note {
  position: absolute;
  z-index: 3;
  top: var(--y);
  left: var(--x);
  right: calc(-1 * var(--reach, 150px));
  display: flex;
  align-items: center;
  transform: translateY(-50%);
  pointer-events: none;
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
}
.v2-note::before {
  content: "";
  flex: none;
  width: 10px; height: 10px;
  margin-left: -5px;
  border-radius: 50%;
  background: var(--brand);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 35%, transparent);
}
.v2-note i { flex: 1; height: 1px; margin-right: 8px; background: var(--brand); }
@media (max-width: 880px) { .v2-note { display: none; } }

/* ------------------------------------------------------------- sections */

section.block {
  position: relative;
  padding: clamp(52px, 7.5vh, 92px) 0;
}
section.block.hairline { border-top: 1px solid var(--line-soft); }

.section-head { max-width: 680px; }
.section-head.center { margin: 0 auto; text-align: center; }
.section-head.center .sub { margin-left: auto; margin-right: auto; }

/* feature rows: copy ↔ phone, alternating */
.feature {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(40px, 6vw, 96px);
  align-items: center;
  padding: clamp(34px, 5.5vh, 68px) 0;
}
.feature .visual { order: 2; }
.feature.flip .visual { order: -1; }  /* beat the copy's implicit order:0 */
@media (max-width: 880px) {
  /* Tighter section rhythm on mobile — the desktop padding leaves a huge gap,
     most visible on pages whose hero phone is hidden (Musaic). Placed after the
     base section.block rule so it actually wins (media queries add no specificity). */
  section.block { padding: clamp(24px, 3.5vh, 40px) 0; }
  .feature { grid-template-columns: 1fr; padding: 20px 0; gap: 18px; }
  /* Screenshot slots directly under the title, above the feature copy.
     display:contents promotes the copy's children into the feature grid so the
     visual (a sibling of .copy) can be ordered between the title and the rest. */
  .feature .copy { display: contents; }
  .feature .copy > h2.title { order: 0; margin-top: 0; }
  .feature .visual, .feature.flip .visual { order: 1; }
  .feature .copy > :not(h2.title) { order: 2; }
  /* gap now owns the vertical rhythm — drop the stacked top margins */
  .feature .copy > .sub,
  .feature .copy > .ticks,
  .feature .copy > .chips { margin-top: 0; }
  /* a touch smaller than the desktop phone, still centered */
  .feature .visual .phone { width: min(238px, 62vw); }
}
.feature .copy .eyebrow { margin-bottom: 2px; }

/* Feature rows sit on a permanent liquid-glass panel behind the copy column —
   mostly-clear glass with a refractive rim over the scene shapes. On rows that
   are one click target (apex: they carry a stretched .row-link) hovering lifts
   the panel. Desktop only — on mobile .copy is display:contents. */
@media (min-width: 881px) {
  .feature .copy { position: relative; }
  .feature .copy::before {
    content: "";
    position: absolute;
    inset: -42px -46px;
    z-index: -1;
    border-radius: 34px;
    /* liquid glass: a mostly-clear fill (the scene shimmers through the
       saturated blur), a specular highlight top-left, and a refractive rim —
       a gradient painted into the border box so the edge catches light
       unevenly instead of being one flat hairline */
    border: 1.5px solid transparent;
    background:
      radial-gradient(120% 90% at 16% 0%, rgba(255, 255, 255, 0.6), rgba(255, 255, 255, 0) 44%) padding-box,
      linear-gradient(135deg, rgba(255, 255, 255, 0.4), rgba(255, 255, 255, 0.14) 55%, rgba(255, 255, 255, 0.32)) padding-box,
      linear-gradient(135deg, rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0.18) 45%, rgba(255, 255, 255, 0.85)) border-box;
    backdrop-filter: blur(14px) saturate(1.8);
    -webkit-backdrop-filter: blur(14px) saturate(1.8);
    box-shadow:
      inset 0 1px 1px rgba(255, 255, 255, 0.9),
      0 22px 55px -28px rgba(24, 32, 64, 0.3);
    transition: transform 0.3s var(--ease), box-shadow 0.3s var(--ease),
                filter 0.3s var(--ease);
    pointer-events: none;
  }
  .feature:has(.row-link):hover .copy::before {
    transform: translateY(-3px);
    filter: brightness(1.045);
    box-shadow:
      inset 0 1px 1px #fff,
      0 30px 70px -30px rgba(24, 32, 64, 0.4);
  }
}

.feature ul.ticks {
  list-style: none;
  padding: 0;
  margin: 22px 0 0;
  display: grid;
  gap: 12px;
  color: var(--dim);
  font-size: 15.5px;
}
.feature ul.ticks li {
  position: relative;
  padding-left: 21px;
}
.feature ul.ticks li::before {
  content: "";
  position: absolute;
  left: 1px;
  top: 0.5em;
  width: 7px; height: 7px;
  border-radius: 2px;
  transform: rotate(45deg);
  background: linear-gradient(135deg, var(--brand), var(--brand-2));
}
.feature ul.ticks b { color: var(--text); font-weight: 600; }

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 9px;
  margin-top: 22px;
}
.chip {
  padding: 7px 13px;
  font-size: 13px;
  font-weight: 500;
  color: var(--dim);
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--bg-card);
}

/* ---------------------------------------------------------------- cards */

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 18px;
  margin-top: 44px;
}
.card {
  position: relative;
  padding: 26px 26px 28px;
  border-radius: var(--radius);
  background: var(--bg-card);
  border: 1px solid var(--line-soft);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  transition: transform 0.25s var(--ease), border-color 0.25s var(--ease),
              background 0.25s var(--ease);
}
.card:hover {
  transform: translateY(-4px);
  border-color: var(--line);
  background: #fff;
}
.card .num {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.1em;
  color: var(--brand);
}
.card h3 { margin: 10px 0 8px; font-size: 19px; letter-spacing: -0.015em; }
.card p { margin: 0; color: var(--dim); font-size: 15px; }
/* A card with more than one paragraph needs air between them; the rule
   above zeroes every margin, which only ever suited a single-paragraph card. */
.card p + p { margin-top: 14px; }

/* apex: app showcase cards — the whole card is clickable via a stretched
   .more link; interactive children (store badge) sit above the overlay. */
.app-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0;
  padding: 30px;
  border-radius: var(--radius-lg);
  background: var(--bg-card);
  border: 1px solid var(--line-soft);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  transition: transform 0.25s var(--ease), border-color 0.25s var(--ease),
              box-shadow 0.25s var(--ease);
}
.app-card:hover {
  transform: translateY(-5px);
  border-color: color-mix(in srgb, var(--card-accent, var(--brand)) 45%, var(--line));
  box-shadow: 0 30px 80px -30px color-mix(in srgb, var(--card-accent, var(--brand)) 30%, transparent);
}
.app-card .head {
  display: flex;
  align-items: center;
  gap: 16px;
}
.app-card .head img {
  width: 62px; height: 62px;
  border-radius: 15px;
  border: 1px solid var(--line);
}
.app-card .head h3 {
  margin: 0;
  font-size: 22px;
  letter-spacing: -0.02em;
}
.app-card .head .kind {
  margin-top: 3px;
  font-size: 13px;
  color: var(--card-accent, var(--brand));
  font-weight: 600;
}
.app-card p.blurb {
  margin: 16px 0 0;
  color: var(--dim);
  font-size: 15.5px;
  flex-grow: 1;
}
.app-card .platforms {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
  margin-top: 16px;
}
.app-card .platforms span {
  font-size: 12px;
  font-weight: 600;
  color: var(--dim);
  padding: 4px 10px;
  border-radius: 999px;
  border: 1px solid var(--line-soft);
  background: rgba(18, 24, 48, 0.035);
}
.app-card .foot {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-top: 22px;
  flex-wrap: wrap;
}
.app-card .foot .more {
  font-size: 14.5px;
  font-weight: 600;
  color: var(--card-accent, var(--brand));
}
.app-card .foot .more::after {
  /* stretch the link over the whole card */
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--radius-lg);
}
.app-card:has(.more:hover) {
  transform: translateY(-5px);
  border-color: color-mix(in srgb, var(--card-accent, var(--brand)) 45%, var(--line));
}
.app-card .foot .more:hover { text-decoration: underline; }
.app-card a.store-badge {
  position: relative;
  z-index: 1;                 /* stays clickable above the stretched link */
  width: 120px;
  height: 40px;               /* Apples Mindesthoehe */
}

.apps-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 20px;
  margin-top: 44px;
}
@media (max-width: 820px) { .apps-grid { grid-template-columns: 1fr; } }

/* app groups on the apex — a colour-accented label per category */
.app-group { margin-top: 46px; }
.app-group .apps-grid { margin-top: 18px; }
.apps-grid.single { grid-template-columns: 1fr; }
.group-head {
  display: flex;
  align-items: center;
  gap: 13px;
  margin: 0;
  padding-left: 2px;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--group-accent, var(--brand));
}
.group-head::before {
  content: "";
  flex: none;
  width: 22px;
  height: 3px;
  border-radius: 2px;
  background: currentColor;
}

/* -------------------------------------------------------------- pricing */

.plans {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 20px;
  margin-top: 44px;
  max-width: 760px;
  margin-left: auto;
  margin-right: auto;
}
.plan {
  padding: 30px;
  border-radius: var(--radius-lg);
  background: var(--bg-elev);
  border: 1px solid var(--line-soft);
}
.plan.pro {
  background:
    linear-gradient(var(--bg-elev), var(--bg-elev)) padding-box,
    linear-gradient(140deg, var(--brand), var(--brand-2) 50%, var(--gold)) border-box;
  border: 1px solid transparent;
}
.plan h3 { margin: 0; font-size: 20px; letter-spacing: -0.02em; }
.plan .price { margin: 12px 0 2px; font-size: 34px; font-weight: 750; letter-spacing: -0.03em; }
.plan .price span { font-size: 15px; font-weight: 500; color: var(--dim); }
.plan .alt { font-size: 13.5px; color: var(--faint); }
.plan ul {
  list-style: none;
  padding: 0;
  margin: 20px 0 0;
  display: grid;
  gap: 10px;
  color: var(--dim);
  font-size: 15px;
}
.plan ul li { padding-left: 26px; position: relative; }
.plan ul li::before {
  content: "✓";
  position: absolute;
  left: 0;
  color: var(--brand);
  font-weight: 700;
}
.plan.pro ul li::before { color: var(--gold); }

/* --------------------------------------------------------------- footer */

footer.site {
  border-top: 1px solid var(--line-soft);
  padding: 44px 0 56px;
  color: var(--faint);
  font-size: 14px;
}
footer.site .wrap {
  display: flex;
  align-items: center;
  gap: 24px;
  flex-wrap: wrap;
}
footer.site .logo { font-size: 16px; color: var(--dim); }
footer.site .logo img { width: 24px; height: 24px; }
footer.site nav {
  margin-left: auto;
  display: flex;
  gap: 22px;
  flex-wrap: wrap;
}
footer.site a:hover { color: var(--text); }

/* ------------------------------------------------- legal / prose pages */

main.prose {
  position: relative;
  z-index: 1;
  max-width: 780px;
  margin: 0 auto;
  padding: clamp(40px, 7vh, 72px) 24px 96px;
}
.prose h1 {
  margin: 0 0 6px;
  font-size: clamp(32px, 5vw, 44px);
  letter-spacing: -0.03em;
  font-weight: 750;
}
.prose .updated {
  margin: 0 0 30px;
  padding-bottom: 22px;
  border-bottom: 1px solid var(--line-soft);
  color: var(--faint);
  font-size: 14px;
}
.prose h2 {
  margin: 36px 0 10px;
  font-size: 20px;
  letter-spacing: -0.015em;
}
.prose p, .prose li { color: var(--dim); font-size: 15.5px; }
.prose li { margin: 6px 0; }
.prose strong { color: var(--text); }
.prose a { color: var(--brand); }
.prose a:hover { text-decoration: underline; }
.prose .muted { color: var(--faint); }
.prose .contact {
  display: inline-block;
  padding: 9px 16px;
  border-radius: 11px;
  border: 1px solid var(--line);
  background: var(--bg-card);
  color: var(--text);
  font-size: 15px;
}
.prose .card {
  background: var(--bg-card);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  padding: 22px 24px;
  margin: 18px 0;
}

/* -------------------------------------------------------------- reveals */

html.js .reveal {
  opacity: 0;
  transform: translateY(26px);
  transition:
    opacity 0.8s var(--ease) var(--d, 0ms),
    transform 0.8s var(--ease) var(--d, 0ms);
  will-change: opacity, transform;
}
html.js .reveal.in {
  opacity: 1;
  transform: none;
}

/* Phones fly in from below: feature visuals and the hero device get a longer
   travel + duration than plain copy reveals. :where() keeps specificity level
   with html.js .reveal, so the reduced-motion override below still wins. */
html.js :where(.feature .visual, .hero-media, .hero-visual).reveal {
  transform: translateY(96px);
  transition-duration: 1.05s;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .scene,
  .scene .blob,
  .scene .ring,
  .scene .flow { animation: none; }
  html.js .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* ------------------------------------------------------- language switch */

/* EN ⇄ DE in the navbar. Outlined pill, deliberately NOT the filled .cta
   treatment — a language switch must not compete with the download button.
   `order: 1` parks it at the far right of the bar: site.js appends the
   dark-mode toggle to .links at runtime, so it is always the last DOM child
   and markup order alone cannot win. Everything else defaults to order 0. */
nav.top .links a.lang {
  order: 1;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 4px 11px;
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.04em;
}
nav.top .links a.lang:hover { border-color: var(--brand); color: var(--brand); }
/* The 640px rule above hides every non-.cta nav link; the language switch is the
   one exception worth keeping — on a phone it is the only route to the other
   language, and the footer link is a whole page away. */
@media (max-width: 640px) {
  nav.top .links a.lang { display: inline-block; }
}

/* Language menu on the Musaic pages (since 2026-09-26, Marco's call): a
   globe with the current code, opening a small menu with the full names.
   A <details>, so it works without JS; site.js only closes it on an outside
   click or Escape. Same `order` trick as a.lang, and kept visible on phones. */
nav.top .links .lang-menu { order: 1; position: relative; }
nav.top .links .lang-menu summary {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--dim);
  cursor: pointer;
  list-style: none;
}
nav.top .links .lang-menu summary::-webkit-details-marker { display: none; }
nav.top .links .lang-menu summary:hover,
nav.top .links .lang-menu[open] summary { border-color: var(--brand); color: var(--brand); }
nav.top .links .lang-menu summary svg { width: 14px; height: 14px; }
nav.top .links .lang-menu summary .chev { width: 10px; height: 10px; transition: transform 0.2s var(--ease); }
nav.top .links .lang-menu[open] summary .chev { transform: rotate(180deg); }
nav.top .links .lang-menu ul {
  position: absolute;
  right: 0;
  top: calc(100% + 8px);
  min-width: 150px;
  margin: 0;
  padding: 4px;
  list-style: none;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--bg-card);
  box-shadow: 0 12px 32px -12px rgba(24, 32, 64, 0.35);
  z-index: 50;
}
nav.top .links .lang-menu li a {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 8px 10px;
  border-radius: 8px;
  font-size: 14px;
  color: var(--text);
}
nav.top .links .lang-menu li a:hover { background: color-mix(in srgb, var(--brand) 10%, transparent); }
nav.top .links .lang-menu li a[aria-current="page"] { color: var(--brand); font-weight: 600; }
nav.top .links .lang-menu li a[aria-current="page"]::after { content: "✓"; }
@media (max-width: 640px) {
  nav.top .links .lang-menu a { display: flex; }
}

/* ------------------------------------------------------------------ faq */

/* Question-shaped copy, collapsed. <details> is deliberate: the answers stay
   in the DOM and in the accessibility tree whether open or shut, so crawlers
   and LLMs read every one of them while the page keeps its short silhouette.
   Never swap this for JS-mounted panels — that hides the text we came for.
   Pair each block with FAQPage JSON-LD in the page head. */
.faq {
  display: grid;
  gap: 12px;
  margin-top: 40px;
}
.faq details {
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  background: var(--bg-card);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  transition: border-color 0.25s var(--ease), background 0.25s var(--ease);
}
.faq details:hover { border-color: var(--line); }
.faq details[open] { border-color: var(--line); }
.faq summary {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  padding: 20px 24px;
  cursor: pointer;
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text);
  list-style: none;
}
/* both spellings — WebKit still wants the pseudo-element */
.faq summary::-webkit-details-marker { display: none; }
.faq summary::marker { content: ''; }
.faq summary:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
  border-radius: var(--radius);
}
/* chevron, drawn from borders so it needs no asset */
.faq summary::after {
  content: '';
  flex: none;
  width: 8px;
  height: 8px;
  margin: 6px 0 0 auto;
  border-right: 2px solid var(--faint);
  border-bottom: 2px solid var(--faint);
  transform: rotate(45deg);
  transition: transform 0.25s var(--ease), border-color 0.25s var(--ease);
}
.faq details[open] summary::after {
  transform: rotate(-135deg);
  border-color: var(--brand);
}
.faq .answer {
  padding: 0 24px 22px;
  margin: 0;
  max-width: 62ch;
  color: var(--dim);
  font-size: 15.5px;
  line-height: 1.62;
}
.faq .answer + .answer { padding-top: 12px; }
.faq .answer a { color: var(--brand); }
@media (max-width: 640px) {
  .faq summary { padding: 17px 18px; font-size: 16px; gap: 12px; }
  .faq .answer { padding: 0 18px 19px; }
}

/* --------------------------------------------------------- support matrix */

/* A real <table> rather than a chip cloud: agents and search engines extract
   rows and headers reliably, a bag of <span>s not at all. Scrolls inside its
   own box so a narrow viewport never scrolls the page sideways. */
.matrix-scroll {
  margin-top: 34px;
  overflow-x: auto;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  background: var(--bg-card);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}
table.matrix {
  width: 100%;
  border-collapse: collapse;
  font-size: 15px;
  text-align: left;
}
table.matrix caption {
  padding: 18px 22px 0;
  text-align: left;
  font-size: 13px;
  color: var(--faint);
}
table.matrix th,
table.matrix td {
  padding: 13px 22px;
  border-bottom: 1px solid var(--line-soft);
  vertical-align: top;
  white-space: nowrap;
}
table.matrix thead th {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--faint);
}
table.matrix tbody th {
  font-weight: 600;
  color: var(--text);
}
table.matrix td { color: var(--dim); }
table.matrix tbody tr:last-child th,
table.matrix tbody tr:last-child td { border-bottom: 0; }
@media (max-width: 640px) {
  table.matrix th,
  table.matrix td { padding: 11px 15px; }
  table.matrix caption { padding: 15px 15px 0; }
}


/* =====================================================================
   Ueberarbeitete Startseite und App-Seiten (August 2026)

   Kam als preview.css dazu und ist beim Livegang hierher gewandert.
   Enthaelt: Metazeilen auf der System-Monospace, das Lockup aus Zeichen
   und Wortmarke samt Zeichen-Sequenz, den angeschnittenen bzw. vollen
   Hero-Shot, die Apps nebeneinander, die Feature-Karussells, die
   getippte Ueberschrift und die Cross-Document View Transitions.
   ===================================================================== */

/* =====================================================================
   DELTA gegen landing.css. Alles hier ist neu; nichts wird überschrieben,
   was andere Seiten benutzen. Beim Übernehmen wandert es 1:1 in
   landing.css und die v2-Präfixe fallen weg.
   ===================================================================== */

/* --- 0 · Labels laufen auf der System-Monospace ----------------------
   Der einzige Typo-Eingriff. Kostet keine Datei, kein Byte Ladezeit, und
   nimmt den Metazeilen den Sperrsatz-Look, den jede generierte Seite hat. */
:root { --meta: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; }

.v2-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 18px;
  font-family: var(--meta);
  font-size: 11.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--dim);
}
.v2-meta i { font-style: normal; color: var(--faint); }
/* In der Metazeile stehen gelegentlich Links - der Sprachwechsel im
   Impressum zum Beispiel. Grau und ohne Unterstrich sieht der aus wie
   Fliesstext. */
.v2-meta a {
  color: var(--brand);
  border-bottom: 1px solid color-mix(in srgb, var(--brand) 45%, transparent);
  padding-bottom: 1px;
}
.v2-meta a:hover { border-bottom-color: var(--brand); }

/* --- 1 · Hero: Satzspiegel statt Bühne ------------------------------
   Gleiche Bausteine wie heute (Wortmarke, Lede, Gerät), andere Ordnung:
   Metazeile oben, eine Farbe, und das Gerät läuft rechts aus dem Bild. */
.v2-hero { position: relative; padding-top: calc(var(--nav-h) + 8px); }
.v2-hero .wrap { position: relative; z-index: 1; }

.v2-hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  align-items: center;
  min-height: min(62vh, 520px);
}
/* Frueher stand hier align-items:end plus 6vh Vorlauf - auf einem Laptop
   waren das ueber 200px Leerraum, bevor die Seite anfing. */
.v2-hero-col { padding: 18px 52px 40px 0; container-type: inline-size; }

.v2-hero .lede { margin-top: 22px; max-width: 44ch; }

/* Beide Aktionen sprechen dieselbe Sprache und unterscheiden sich nur im
   Gewicht: gleiche Schrift, gleiche Größe, gleicher Abstand zur Linie -
   die eine trägt den Akzent, die andere eine Haarlinie. Vorher standen
   hier zwei verschiedene Systeme nebeneinander. */
.v2-act { margin-top: 32px; display: flex; align-items: baseline; gap: 30px; flex-wrap: wrap; }
.v2-lnk {
  font-family: var(--meta);
  font-size: 12.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--dim);
  border-bottom: 1px solid var(--line);
  padding-bottom: 5px;
  transition: color 0.2s ease, border-color 0.2s ease;
}
.v2-lnk:hover { color: var(--text); border-color: var(--dim); }
.v2-lnk.is-primary {
  color: var(--text);
  border-bottom-width: 2px;
  border-color: var(--brand);
}
.v2-lnk.is-primary:hover { color: var(--brand); }
.v2-quiet { font-size: 13.5px; color: var(--faint); }

/* Das Gerät sitzt in der Spalte und wird rechts vom Seitenrand
   angeschnitten - gleichmäßige Ränder rundum sind das Template-Signal. */
.v2-shot {
  align-self: stretch;
  position: relative;
  overflow: hidden;
  min-height: 320px;
}
/* Oben ganz, unten und rechts vom Seitenrand angeschnitten: die runden
   Ecken und die Island bleiben sichtbar, damit es ein Gerät bleibt und
   nicht zur Screenshot-Platte wird. */
.v2-shot > .phone {
  position: absolute;
  left: 0;
  top: 34px;
  width: 340px;
  max-width: none;
}
@media (max-width: 880px) {
  .v2-hero-grid { grid-template-columns: 1fr; }
  .v2-hero-col { padding-right: 0; padding-bottom: 32px; }
  .v2-shot { min-height: 0; overflow: visible; }
  .v2-shot > .phone { position: static; top: auto; width: min(300px, 78vw); margin: 0 auto; }
  /* Das ganze Telefon kostete auf dem Handy ueber 600px Scrollweg, und die
     Aussage steht in den oberen 55%: Ordner auf, drei Apps drin. Also
     derselbe Griff wie auf dem Desktop, nur nach unten statt nach rechts -
     angeschnitten, nicht abgeschnitten. Die Maske laesst das Geraet
     auslaufen; eine harte Kante saehe aus wie ein Ladefehler.
     aspect-ratio statt fester Hoehe: so ist der Ausschnitt bei jeder
     Bildschirmbreite derselbe Anteil des Screenshots. */
  .v2-shot > .phone {
    aspect-ratio: 300 / 342;
    overflow: hidden;
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
    -webkit-mask-image: linear-gradient(180deg, #000 78%, transparent 100%);
    mask-image: linear-gradient(180deg, #000 78%, transparent 100%);
  }
}

/* --- 2 · Die Apps nebeneinander -------------------------------------
   Heute sind es drei abwechselnde Zeilen über die volle Breite: man
   scrollt lang und sieht nie alle drei zusammen. Nebeneinander sind sie
   ein Bild. Die Detailargumente stehen auf den App-Seiten, wo sie
   hingehören. */
.v2-apps-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 24px;
}
.v2-apps-head h2 { font-size: clamp(28px, 3.4vw, 38px); letter-spacing: -0.035em; font-weight: 800; }
.v2-apps-head .v2-meta { flex: none; }

.v2-apps {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 36px;
  margin-top: 18px;
}
/* Untereinander verschwimmen die drei Karten zu einem langen Band - der
   Blick findet den Anfang der naechsten App nicht. Eine Haarlinie zwischen
   ihnen macht die Grenze sichtbar; die Luft wandert vom gap in ein
   Innenpolster, damit die Linie mittig zwischen den Karten steht. */
@media (max-width: 900px) {
  .v2-apps { grid-template-columns: 1fr; gap: 0; }
  .v2-app { padding-bottom: 40px; }
  .v2-app + .v2-app { border-top: 1px solid var(--line); padding-top: 40px; }
  .v2-app:last-child { padding-bottom: 0; }
}

/* "Learn more" steht per auto-Rand am unteren Ende: die drei Eintraege
   sind verschieden lang, die Schlusszeile soll trotzdem auf einer Linie
   stehen. */
.v2-app { display: flex; flex-direction: column; }

/* Quadratischer Kasten, Gerät oben bündig: so ist fast das ganze Telefon
   zu sehen und wird nur unten angeschnitten. Bei 4:3 sieht man nur den
   oberen Drittel und das Bild wirkt wie ein zufälliger Ausschnitt. */
/* Bühne, kein Kasten: der dunkle Block stand auf dem hellen Grund wie ein
   Fremdkörper. Jetzt trägt nur der Schatten das Gerät. */
/* Feste Höhe statt Seitenverhältnis: sonst wächst der Kasten mit der
   Spaltenbreite mit und das Gerät wird auf einer Spalte (Handy) riesig.
   So ist es überall gleich groß - das Bild richtet sich nach der Höhe. */
/* Die Breite steuert die Groesse, nicht die Hoehe des Kastens: height:100%
   auf einem <img> mit width/height-Attributen wird von der intrinsischen
   Groesse ueberstimmt - das Bild lief dann 400px aus dem Kasten heraus und
   legte sich ueber die Textzeilen darunter. */
/* Gemeinsame Grundlinie: die drei Bilder sind verschieden hoch (zwei
   Telefone, ein Geraetestapel). Ein Mindestmass plus Ausrichtung nach
   unten stellt sie auf dieselbe Linie, statt sie um ihre Mitten
   auszurichten. */
.v2-app-shot { display: grid; place-items: center; background: none; min-height: 300px; }
/* Nur das nackte Bild bekommt Breite und Schatten - ein Geraetestapel
   bringt beides selbst mit und wuerde hier auf 132px zusammenfallen. */
.v2-app-shot > img {
  width: 132px;
  height: auto;
  border-radius: 14px;
  box-shadow:
    0 22px 44px -18px rgba(24, 32, 64, 0.45),
    0 0 0 1px rgba(18, 24, 48, 0.10);
  transition: transform 0.35s var(--ease);
}
/* Alternative für IPTV: class="v2-app-shot wide" plus das Fernseh-Bild,
   dann wird das Querformat selbst zur Aussage ("läuft auch am Fernseher"). */
.v2-app-shot.wide img { width: 100%; border-radius: 8px; }
.v2-app-shot > .v2-stack { transition: transform 0.35s var(--ease); }
.v2-app:hover .v2-app-shot > img,
.v2-app:hover .v2-app-shot > .v2-stack,
.v2-app:focus-visible .v2-app-shot > img,
.v2-app:focus-visible .v2-app-shot > .v2-stack { transform: translateY(-10px); }

/* Kategorie und Name stehen ueber dem Bild - auf dem Handy sah man sonst
   erst ein Geraet und musste scrollen, um zu erfahren, welche App das
   ist. Der Satz dazu steht wieder unter dem Bild.

   Der Kopf steht mittig ueber dem Geraet, das ohnehin mittig in seiner
   Spalte steht; der Satz darunter bleibt linksbuendig, weil zentrierter
   Fliesstext mit jeder Zeile einen neuen Anfang bekommt. */
.v2-app .v2-meta { margin-top: 0; justify-content: center; }
.v2-app .v2-app-title { justify-content: center; }
.v2-app h3 { margin: 10px 0 0; font-size: clamp(21px, 2.3vw, 26px); line-height: 1.2; letter-spacing: -0.03em; }
.v2-app p { margin: 10px 0 0; color: var(--dim); font-size: 15px; line-height: 1.55; }
.v2-app .v2-app-shot { margin-top: 22px; }
.v2-app .v2-app-shot + p { margin-top: 18px; }
.v2-app .v2-more {
  margin-top: auto;
  padding-top: 20px;
  align-self: flex-start;
  font-size: 14px;
  border-bottom: 1px solid var(--line);
  padding-bottom: 3px;
  transition: border-color 0.2s ease;
}
.v2-app:hover .v2-more { border-color: var(--brand); }

/* --- 3 · Die Marke im Hero ------------------------------------------
   Ein schiefergraues Quadrat, aus dem das Segel ausgestanzt ist (durch
   das Segel scheint die Szene, die auf der Startseite dafuer schneller
   wandert, index.html), und ein eckiger Rahmen im selben Grau, der um
   "lìkwest" weiterlaeuft. Der Schriftzug steht in Textfarbe auf dem
   Seitengrund. Das SVG steht inline in index.html und kommt aus
   tools/likwest-tile.py - die Buchstaben sind Geist-Pfade, einzeln, damit
   sie sich einzeln bewegen koennen.

   Beim Laden: das Quadrat faehrt auf, ein Windstoss laesst das Segel
   schwanken, dann zieht sich der Rahmen nach rechts auf und der Wind
   blaest die Buchstaben einzeln aus dem Segel. Zum Schluss laeuft einmal
   ein Glanz ueber das Quadrat. Alle Startzustaende stehen nur in den
   Keyframes (fill-mode backwards): ohne JS und bei reduced motion steht
   die Marke sofort fertig da.
   Laengen in px sind hier Einheiten der viewBox (1/1000 em), die Werte
   --rest, --sq, --sh0 und --sh1 setzt das Skript am SVG. */
:root {
  --tile-0: #434957;
  --tile-1: #2a2e37;
  --tile-edge: #363b46;
  --tile-sheen: rgba(255, 255, 255, 0.2);
}
:root[data-theme="dark"] {
  --tile-0: #e3e6ec;
  --tile-1: #b9bec8;
  --tile-edge: #cfd3db;
  --tile-sheen: rgba(255, 255, 255, 0.55);
}
.v2-lockup {
  /* Rund ein Drittel unter dem Schriftgrad von .brand-name: mit Quadrat
     und Rahmen wirkte die Marke in voller Groesse zu wuchtig. Ecken
     0,03em, das Quadrat nur links rund, wo es auf den Rahmen trifft. */
  font-size: clamp(38px, 4.4vw, 58px);
  margin-top: 18px;
}
.v2-tile { display: block; height: auto; max-width: 100%; overflow: visible; }
.v2-tile .t0 { stop-color: var(--tile-0); }
.v2-tile .t1 { stop-color: var(--tile-1); }
.v2-tile .s0 { stop-color: var(--tile-sheen); stop-opacity: 0; }
.v2-tile .s1 { stop-color: var(--tile-sheen); }
.v2-tile .frame { fill: none; stroke: var(--tile-edge); }
.v2-tile .word { fill: var(--text); }
/* In Ruhe steht der Glanz rechts neben dem Quadrat, die Maske schneidet
   ihn weg. */
.v2-tile .sheen { transform: translateX(var(--sh1)); }

html.js .v2-lockup.draw .fill {
  animation: v2-square .5s cubic-bezier(.22,1,.36,1) .1s backwards;
}
html.js .v2-lockup.draw .sail {
  animation: v2-gust 1.3s cubic-bezier(.3,0,.2,1) .5s backwards;
}
html.js .v2-lockup.draw .frame {
  animation: v2-frame .8s cubic-bezier(.65,0,.25,1) 1.35s backwards;
}
html.js .v2-lockup.draw .ch {
  animation: v2-blow .75s cubic-bezier(.2,1.25,.35,1) backwards;
  animation-delay: calc(1.45s + var(--i) * 60ms);
}
html.js .v2-lockup.draw .sheen {
  animation: v2-sheen 1s cubic-bezier(.4,0,.2,1) 2.4s backwards;
}
/* inset() bezieht sich auf die Huellbox des Elements. Beschnitten wird
   deshalb nur die Flaeche des Quadrats, nicht die Gruppe mit dem Glanz -
   der liegt waehrend des Auffahrens links ausserhalb und wuerde die Box
   verbreitern. Ausserhalb der Maske ist er ohnehin unsichtbar. */
@keyframes v2-square { from { clip-path: inset(0 100% 0 0); } }
@keyframes v2-frame {
  0%   { opacity: 0; clip-path: inset(0 var(--rest) 0 0); }
  1%   { opacity: 1; }
  100% { opacity: 1; clip-path: inset(0 0 0 0); }
}
@keyframes v2-gust {
  0%   { transform: skewX(0); }
  30%  { transform: skewX(-11deg); }
  62%  { transform: skewX(4deg); }
  85%  { transform: skewX(-1.5deg); }
  100% { transform: skewX(0); }
}
@keyframes v2-blow {
  0%   { opacity: 0; transform: translateX(var(--from)) skewX(-16deg); }
  25%  { opacity: 1; }
  100% { opacity: 1; transform: none; }
}
@keyframes v2-sheen { from { transform: translateX(var(--sh0)); } }

/* Navigation: das nackte Zeichen statt der dunklen Kachel. Als Inline-SVG,
   damit es dieselben Farben nimmt wie die Marke im Hero - ein <img> ließe
   sich per CSS nicht umfärben und bliebe im Dark Mode falsch. */
/* Die Groesse hing an `.logo .v2-nav-mark` - auf den Unter- und
   Rechtsseiten steckt das Zeichen aber in `a.parent-mark`, nicht in
   `.logo`. Dort blieb es damit ohne Hoehe und war unsichtbar; sichtbar
   war nur der Schraegstrich aus parent-mark::after. Die Groesse gehoert
   also an die Klasse selbst. */
/* Spezifitaet: `.logo img, .logo svg` aus dem Bestand setzt 30x30 und ist
   0,1,1 - eine blosse Klasse (0,1,0) verliert dagegen. Deshalb beide
   Kontexte mit Element + Klasse, das schlaegt sie sicher. */
.logo svg.v2-nav-mark,
a.parent-mark svg.v2-nav-mark {
  width: auto; height: 0.85em; border-radius: 0; display: block;
  /* Die viewBox liegt bis auf die Einheit an der Kontur - der Rumpf endet
     bei y=406, und da hoert der Kasten auf. Ein SVG-Viewport schneidet per
     Vorgabe (`overflow: hidden` aus dem UA-Stylesheet), also fiel je nach
     Subpixel-Lage die unterste Reihe des Striches weg: der Rumpf sah duenner
     aus als der Rest der Kontur oder war ganz abgeschnitten. Sichtbar wird
     das erst bei genauem Hinsehen, aber es ist ein echter Schnitt, keine
     Taeuschung. `visible` nimmt den Beschnitt heraus; das Zeichen malt dann
     einen halben Pixel ueber seine Box hinaus, was in der Flex-Zeile mit
     Abstand niemanden stoert. */
  overflow: visible;
  /* Das Segel ist von Haus aus sehr schmal; etwas breiter steht es
     besser neben der Wortmarke.
     0,85em hoch und mittig zum l: `align-items: baseline` an .logo
     setzt die Unterkante des SVG auf die Grundlinie, und der Versatz
     schiebt das Segel um die Haelfte seines Ueberstands nach unten. Das l
     ist in Geist 0,71em hoch. Ragt das Segel nur oben heraus, wirkt das
     Zeichen hoeher gesetzt als die Schrift; je die Haelfte oben und unten
     wirkt ausgerichtet.
     Der Strich ist 31,5 statt 28 Einheiten, damit er in Pixeln so dick
     bleibt wie bei der frueheren Hoehe von 0,95em. Er ragt dadurch oben
     und unten je 1,75 Einheiten (0,0047em) ueber die viewBox hinaus. Die
     Aussenkontur ist also 0,8594em hoch, der Ueberstand 0,1494em, die
     Haelfte 0,0747em; davon liegen 0,0047em schon unter der Grundlinie,
     bleiben 0,07em Versatz. Nachgemessen im Browser: je 0,0747em. */
  transform: translateY(0.07em) scaleX(1.2);
  transform-origin: center;
}
a.parent-mark svg.v2-nav-mark { height: 16px; transform: translateY(-0.5px) scaleX(1.2); }
/* In der Navigation nur das Grosssegel, grau, als offene Kontur. Der Fock
   liegt am Mast an und erzeugt bei dieser Groesse eine zweite Linie mitten
   im Zeichen - deshalb bleibt er hier weg. */
.v2-nav-mark .m { fill: none; stroke: var(--dim); stroke-width: 31.5; stroke-linejoin: round; }
a.parent-mark .v2-nav-mark .m { stroke-width: 28; }
.v2-nav-mark .j { display: none; }

/* --- 4 · Cross-document view transitions ----------------------------
   Der Klick auf eine App-Karte laesst das Icon in den Kopf der App-Seite
   wandern. Beide Dokumente brauchen dafuer @view-transition UND denselben
   view-transition-name - beides steht hier, also gilt es fuer alle vier
   Preview-Seiten. Der Name haengt am data-app-Attribut, damit dieselbe
   Regel auf der Karte (Uebersicht) und im Kopf (App-Seite) greift.
   Firefox kennt @view-transition noch nicht und navigiert normal. */
@view-transition { navigation: auto; }
::view-transition-group(*) { animation-duration: 420ms; }
[data-app="musaic"]  { view-transition-name: ic-musaic; }
[data-app="videcho"] { view-transition-name: ic-videcho; }
[data-app="iptv"]    { view-transition-name: ic-iptv; }
/* Zusaetzlich wandert das Geraet: ein 34px-Icon ueber eine kurze Strecke
   sieht man kaum, ein Telefon, das von der Karte in den Hero waechst,
   schon. */
[data-shot="musaic"]  { view-transition-name: sh-musaic; }
[data-shot="videcho"] { view-transition-name: sh-videcho; }
[data-shot="iptv"]    { view-transition-name: sh-iptv; }
/* Das Icon gehört zum Namen, nicht zur Metazeile - es benennt die App,
   es sortiert sie nicht ein. */
.v2-app-icon { width: 34px; height: 34px; border-radius: 9px; display: block; flex: none; }
.v2-app-title { display: flex; align-items: center; gap: 12px; margin-top: 10px; }
.v2-app-title h3 { margin: 0; }

@media (prefers-reduced-motion: reduce) {
  .v2-app:hover .v2-app-shot > img,
  .v2-app:hover .v2-app-shot > .v2-stack { transform: none; }
  /* @view-transition darf NICHT in @media stehen - die At-Regel ist nur auf
     oberster Ebene gültig, und Chrome nimmt sie trotzdem: "navigation: none"
     hat die Transition dann immer abgeschaltet. Stattdessen bleibt die
     Navigation weich und nur die Bewegung fällt weg. */
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }
}


/* =====================================================================
   App-Seiten
   ===================================================================== */

/* --- Kopf: Icon und Name gehören zusammen --------------------------- */
.v2-app-head { display: flex; align-items: center; gap: 14px; margin-top: 22px; }
.v2-app-ico { width: 48px; height: 48px; border-radius: 12px; display: block; flex: none; }
.v2-app-name { font-size: 23px; font-weight: 600; letter-spacing: -0.02em; }
.v2-badge {
  font-family: var(--meta); font-size: 10px; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--faint);
  border: 1px solid var(--line); border-radius: 4px; padding: 3px 7px;
  align-self: center;
}

.v2-hero h1 {
  margin: 20px 0 0; font-size: clamp(40px, 5.6vw, 72px); line-height: 0.98;
  letter-spacing: -0.045em; font-weight: 800; max-width: 14ch;
}

/* --- Abschnitte ------------------------------------------------------ */
.v2-sec { padding: clamp(56px, 8vh, 96px) 0; position: relative; z-index: 1; }
/* Jeder Abschnitt, nicht erst der zweite: der erste steht direkt unter
   dem Hero und hatte als einziger keine Linie ueber sich. */
.v2-sec { border-top: 1px solid var(--line-soft); }
.v2-sec-head {
  display: flex; justify-content: space-between; align-items: baseline; gap: 24px;
}
/* Auf dem Handy nebeneinander heisst: die Ueberschrift bekommt die halbe
   Breite und bricht in vier Zwei-Wort-Zeilen. Untereinander liest sie
   sich in einem Zug, die Statuszeile steht darunter. */
@media (max-width: 640px) {
  .v2-sec-head { display: block; }
  .v2-sec-head .v2-meta { margin-top: 10px; }
}
.v2-sec-head h2 { font-size: clamp(26px, 3.2vw, 36px); letter-spacing: -0.035em; font-weight: 800; }

/* Die Abschnitte hiessen bisher nur in einer 11.5px-Monozeile "Modes" oder
   "Features". Das hat man selbst am Desktop uebersehen, und im Dokument
   hatten die Abschnitte gar keine Ueberschrift. Jetzt dieselbe Sprache -
   Mono, Versalien, gesperrt -, nur in Groesse und Gewicht so weit
   hochgezogen, dass sie die Zeile allein haelt. Kein Strich dahinter:
   direkt unter der Abschnittslinie standen sonst zwei Striche
   uebereinander. Der erste Buchstabe steht in der Textfarbe und setzt den
   Anfang, der Rest bleibt grau.

   ::first-letter statt eines Markup-Knotens - das geht aber nur an einem
   Block, deshalb hier kein display:flex. */
.v2-sec-title {
  display: block;
  margin: 0 0 clamp(10px, 1.8vh, 20px);
  font-family: var(--meta);
  font-size: clamp(20px, 2.2vw, 24px);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--dim);
  line-height: 1.25;
}
.v2-sec-title::first-letter { color: var(--text); }

/* Zwischen Ueberschrift und Reiterleiste liegt eine ganze Folie - oben
   steht "Modes", unten stehen die Reiter, und dazwischen hat man den
   Bezug verloren. Die Leiste nennt ihn deshalb selbst. Als ::before,
   nicht als Kindknoten: role="tablist" vertraegt nur Reiter als Kinder. */
.v2-tabs[data-label]::before {
  content: attr(data-label);
  align-self: center;
  font-family: var(--meta);
  font-size: 11.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
  padding-right: clamp(10px, 1.4vw, 16px);
  border-right: 1px solid var(--line);
}

/* --- Feature-Zeile: Text und Bild wechseln die Seite ---------------- */
.v2-row {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(28px, 5vw, 64px); align-items: center;
  padding: clamp(34px, 5vh, 56px) 0;
}
.v2-row + .v2-row { border-top: 1px solid var(--line-soft); }   /* nur ausserhalb der Karussells */
.v2-row.flip .v2-row-media { order: -1; }
@media (max-width: 880px) {
  .v2-row { grid-template-columns: 1fr; gap: 26px; }
  .v2-row.flip .v2-row-media { order: 0; }
}
.v2-row h3 { margin: 10px 0 0; font-size: clamp(23px, 2.6vw, 30px); letter-spacing: -0.03em; }
.v2-row p { margin: 12px 0 0; color: var(--dim); font-size: 15.5px; line-height: 1.62; max-width: 44ch; }
.v2-row p + p { margin-top: 10px; }
.v2-row ul {
  margin: 16px 0 0; padding: 0; list-style: none;
  display: flex; flex-direction: column; gap: 8px;
  color: var(--dim); font-size: 15px; line-height: 1.55; max-width: 44ch;
}
.v2-row ul li { padding-left: 18px; position: relative; }
.v2-row ul li::before {
  content: ""; position: absolute; left: 0; top: 0.62em;
  width: 6px; height: 1px; background: var(--brand);
}
.v2-row ul b { color: var(--text); font-weight: 600; }

/* Medien: Höhe deckeln, damit ein Telefon nicht die halbe Seite füllt. */
.v2-row-media { display: grid; place-items: center; }
/* Kleiner als im Hero: in einer Feature-Zeile steht das Geraet neben drei
   Stichpunkten, nicht neben der Ueberschrift der Seite. Zu gross, und die
   Zeile wird 700px hoch fuer zwei Saetze Text. */
.v2-row-media .phone { width: min(228px, 62vw); margin: 0; }
/* Der Fernseher fuellt die Spalte, statt bei 620px stehenzubleiben - in
   einer Feature-Zeile ist die Spalte das Mass. */
.v2-row-media .tv { width: 100%; margin: 0; }
.v2-row-media .wide {
  width: 100%; border-radius: 12px; display: block; height: auto;
  box-shadow: 0 26px 50px -18px rgba(24, 32, 64, 0.45), 0 0 0 1px rgba(18, 24, 48, 0.10);
}

/* --- Plattform-Tabelle (IPTV) --------------------------------------- */
.v2-grid {
  /* Fuenf Plattformen in drei Spalten lassen in der zweiten Zeile ein Loch -
     die letzte Zelle spannt es zu (.v2-cell.wide). Bei zwei oder einer
     Spalte klemmt der Browser die Spanne von selbst auf die Spaltenzahl. */
  display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 1px; margin-top: 28px; background: var(--line-soft);
  border: 1px solid var(--line-soft); border-radius: 12px; overflow: hidden;
}
.v2-cell { background: var(--bg-elev); padding: 18px 20px; }
/* Die letzte Zeile bleibt rechts leer, statt die letzte Zelle auf zwei
   Spalten zu ziehen: eine doppelt breite "Windows"-Kachel las sich wie
   die wichtigste Plattform. Die Luecke ist eine leere Zelle, damit das
   Raster ein sauberes Rechteck bleibt. */
.v2-cell.is-empty { background: var(--bg-elev); }
[data-theme="dark"] .v2-cell.is-empty { background: rgba(255, 255, 255, 0.02); }
@media (max-width: 647px) { .v2-cell.is-empty { display: none; } }
[data-theme="dark"] .v2-cell { background: rgba(255, 255, 255, 0.02); }
.v2-cell h4 { margin: 8px 0 0; font-size: 16px; letter-spacing: -0.01em; }
.v2-cell p { margin: 6px 0 0; color: var(--dim); font-size: 14px; line-height: 1.5; }
/* Das Zeichen steht in der Metazeile, wo sonst nur der Herstellername
   stand - man findet die eigene Plattform damit im Ueberfliegen. */
.v2-cell .v2-meta { align-items: center; gap: 8px; }
.v2-cell-ico { width: 17px; height: 17px; display: block; flex: none; color: var(--faint); }
/* Wo es den Store schon gibt, fuehrt die Kachel auch hin - sonst muesste
   man dafuer ans Seitenende scrollen. */
.v2-cell-go {
  display: inline-flex; align-items: center; gap: 6px;
  margin-top: 12px; font-size: 13.5px; font-weight: 600; color: var(--brand);
  border-bottom: 1px solid color-mix(in srgb, var(--brand) 40%, transparent);
  padding-bottom: 2px;
}
.v2-cell-go:hover { border-bottom-color: var(--brand); }
.v2-cell-go svg { width: 13px; height: 13px; display: block; }

/* --- Preise (Musaic) ------------------------------------------------- */
.v2-plans {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 22px; margin-top: 30px;
}
.v2-plan { border-top: 2px solid var(--line); padding-top: 20px; }
.v2-plan.is-pro { border-top-color: var(--brand); }
.v2-plan h3 { margin: 0; font-size: 21px; letter-spacing: -0.02em; }
.v2-plan .price { margin-top: 6px; font-family: var(--meta); font-size: 13px; color: var(--dim); }
.v2-plan ul {
  margin: 16px 0 0; padding: 0; list-style: none;
  display: flex; flex-direction: column; gap: 8px;
  font-size: 15px; line-height: 1.5; color: var(--dim);
}
.v2-plan ul li { padding-left: 18px; position: relative; }
.v2-plan ul li::before {
  content: ""; position: absolute; left: 0; top: 0.62em;
  width: 6px; height: 1px; background: var(--brand);
}

/* --- FAQ ------------------------------------------------------------- */
.v2-faq { margin-top: 8px; }
.v2-faq details { border-bottom: 1px solid var(--line-soft); }
.v2-faq summary {
  list-style: none; cursor: pointer; padding: 20px 34px 20px 0; position: relative;
  font-size: 17px; font-weight: 600; letter-spacing: -0.015em;
}
.v2-faq summary::-webkit-details-marker { display: none; }
.v2-faq summary::after {
  content: ""; position: absolute; right: 4px; top: 50%;
  width: 9px; height: 9px; margin-top: -6px;
  border-right: 1.6px solid var(--faint); border-bottom: 1.6px solid var(--faint);
  transform: rotate(45deg); transition: transform 0.2s ease;
}
.v2-faq details[open] summary::after { transform: rotate(225deg); margin-top: -2px; }
.v2-faq p { margin: 0 0 22px; color: var(--dim); font-size: 15.5px; line-height: 1.65; max-width: 68ch; }

/* --- Schluss --------------------------------------------------------- */
.v2-end { text-align: left; }
.v2-end h2 { font-size: clamp(30px, 4vw, 46px); letter-spacing: -0.04em; font-weight: 800; max-width: 16ch; }
.v2-end .v2-act { margin-top: 26px; }


/* --- App-Seiten: das Geraet ganz zeigen, mit den Anmerkungen ---------
   Auf der Uebersicht ist der Anschnitt richtig - dort ist das Geraet Beiwerk.
   Auf der App-Seite ist es das Produkt: da steht es vollstaendig, und die
   float-chips aus landing.css benennen, was man sieht. */
.v2-hero-grid.is-app { grid-template-columns: minmax(0, 1fr) 340px; align-items: center; }
.v2-hero-grid.is-app .v2-shot {
  overflow: visible;
  min-height: 0;
  display: grid;
  place-items: center;
  padding: 24px 0 40px;
}
.v2-hero-grid.is-app .v2-shot > .phone {
  position: static;
  width: min(300px, 74vw);
  margin: 0;
}
/* --- Hero mit dem Fernseher in der Textspalte ------------------------
   Der Fernseher steht nicht neben dem Satz, sondern schiebt sich von
   rechts ein Stueck in die Spalte hinein: er wird dadurch gross genug,
   um etwas zu zeigen, ohne die halbe Zeile zu verbrauchen. Ein
   vollstaendiges Geraet, kein Anschnitt - eine weiche Kante hat den
   Rahmen aufgefressen und den Screenshot wie ein Wasserzeichen wirken
   lassen. */
/* Die Bildspalte waechst mit dem Fenster, aber nur bis 620px: fest
   gesetzt wird der Satz auf mittelbreiten Schirmen so schmal, dass die
   Ueberschrift (sie misst sich an der Spalte) auf Fliesstextgroesse
   zusammenfaellt. */
.v2-hero-grid.is-app.has-tv { grid-template-columns: minmax(0, 1fr) clamp(360px, 46vw, 620px); }
.v2-hero-grid.is-app.has-tv .v2-hero-col {
  position: relative;
  z-index: 2;
  /* Knapper Abstand: der Fernseher soll direkt hinter dem Satz anfangen,
     nicht nach einer Handbreit Leerraum. */
  padding-right: clamp(24px, 2.6vw, 40px);
}
.v2-hero-grid.is-app.has-tv .v2-shot {
  position: relative;
  display: block;
  overflow: visible;
  padding: 0;
  /* Das Geraet haengt absolut in der Spalte und spannt die Zeile nicht
     auf. Ohne Mindesthoehe waere der Hero nur so hoch wie der Text - bei
     einer kurzen Ueberschrift raegte der Fernseher in den naechsten
     Abschnitt. 620px Deckel x 16:9 plus Gehaeuse und Fuss. */
  min-height: 380px;
}
.v2-hero-tv {
  position: absolute;
  top: 50%;
  /* Ganzes Geraet in der Spalte, ohne weiche Kante: erst war es zu gross
     und musste zum Rand hin ausgeblendet werden, damit es nicht ueber
     den Satz lief. In dieser Groesse passt es hin, und ein vollstaendiger
     Fernseher ist die bessere Aussage als ein Wasserzeichen. */
  left: 0;
  right: 0;
  max-width: 620px;
  transform: translateY(-50%);
  z-index: 0;
}
.v2-hero-tv .tv { width: 100%; margin: 0; }
@media (max-width: 880px) {
  .v2-hero-grid.is-app.has-tv { grid-template-columns: 1fr; }
  .v2-hero-grid.is-app.has-tv .v2-shot { min-height: 0; padding: 6px 0 24px; }
  /* Auf dem Handy gibt es keine Spalte, in die etwas hineinlaufen
     koennte: dort steht das Geraet einfach unter dem Text. */
  .v2-hero-tv {
    position: static;
    transform: none;
    width: min(560px, 100%);
    max-width: 100%;
    margin: 0 auto;
  }
}

/* Ein Fernseher ist breiter als ein Telefon: traegt der Hero einen
   Geraetestapel, bekommt die Spalte mehr Platz - sonst schrumpft der
   16:9-Screenshot auf Briefmarkengroesse. */
.v2-hero-grid.is-app.has-stack { grid-template-columns: minmax(0, 1fr) 440px; }
.v2-hero-grid.is-app.has-stack .v2-shot { padding: 16px 0 28px; }
.v2-hero-grid.is-app .v2-stack { width: min(440px, 82vw); }
@media (max-width: 880px) {
  .v2-hero-grid.is-app { grid-template-columns: 1fr; }
  .v2-hero-grid.is-app .v2-shot { padding: 8px 0 24px; }
  .v2-hero-grid.is-app.has-stack { grid-template-columns: 1fr; }
  .v2-hero-grid.is-app .v2-stack { width: min(440px, 90vw); }
  /* Der Anschnitt oben gilt der Uebersichtsseite. Laeuft im Geraet ein Video,
     fehlen unten weggeblendet genau die Timeline und die Tab-Leiste - die
     Haelfte der Aussage. Also steht das Telefon ganz, aber nur dann: bei einem
     Standbild (Videcho, IPTV Pano) bleibt der Anschnitt, der spart Scrollweg.
     Ohne :has() faellt es auf den Anschnitt zurueck, nicht auf Kaputt. */
  .v2-hero-grid.is-app .v2-shot > .phone:has(video) {
    aspect-ratio: auto;
    overflow: visible;
    border-radius: 54px;
    -webkit-mask-image: none;
    mask-image: none;
  }
}


/* --- Ueberschrift auf den App-Seiten --------------------------------
   Der Satz taucht Wort fuer Wort auf. Vorher lief hier eine Tippanimation
   ueber eine in JS gemessene Endbreite - bei einer Proportionalschrift
   landet die Schrittanimation nie sauber auf dem letzten Buchstaben, das
   sah hakelig aus. Jetzt reicht CSS: jedes Wort traegt seinen Index, die
   Verzoegerung faellt daraus. Ohne JS und bei reduced motion steht der
   Satz sofort. */
.v2-hero h1.v2-type {
  max-width: none;
  /* Deckel nach der Spaltenbreite: 0.55em ist die mittlere Zeichenbreite
     von Inter im Schriftschnitt der Ueberschrift. Kurze Zeilen bleiben
     dadurch gross, lange werden von selbst kleiner. */
  font-size: min(clamp(30px, 5.6vw, 72px), calc(100cqw / var(--n) / 0.55));
}
.v2-hero h1.v2-type .t {
  display: inline-block;
  white-space: nowrap;
}
.v2-hero h1.v2-type .w { display: inline-block; }
/* Eine halbe Sekunde Vorlauf, dann zwei Sekunden fuer den Satz: der
   laengste Titel hat fuenf Woerter, 4 x 250ms Versatz + 1s fuer das
   letzte Wort. Kuerzere Titel sind entsprechend frueher fertig. */
html.js .v2-hero h1.v2-type .w {
  animation: v2-word-in 1s cubic-bezier(.22, 1, .36, 1)
             calc(0.5s + var(--i) * 250ms) backwards;
}
@keyframes v2-word-in { from { opacity: 0; transform: translateY(0.18em); } }

@media (prefers-reduced-motion: reduce) {
  html.js .v2-hero h1.v2-type .w { animation: none; }
}


/* Der Schriftzug kommt von links aus dem Zeichen, das Geraet von rechts
   aus dem Seitenrand - zwei Bewegungen gegeneinander, gleichzeitig. Nur
   auf der Uebersicht: dort ist das Geraet angeschnitten, die Bewegung
   endet also am Rand statt im Nichts. */
html.js .v2-shot > .phone { transform: none; }
html.js .v2-hero.draw .v2-shot > .phone {
  animation: v2-slide-in .72s cubic-bezier(.22,1,.36,1) 1.5s backwards;
}
@keyframes v2-slide-in {
  from { transform: translateX(64px); opacity: 0; }
}

/* =====================================================================
   Karussell fuer Modes und Features

   Nativ gescrollt mit scroll-snap statt nachgebauter Slides: Trackpad,
   Wischen und Tastatur funktionieren dadurch von selbst, und der Inhalt
   bleibt vollstaendig im DOM (Crawler, llms.txt). Der automatische
   Wechsel scrollt nur - er nimmt dem Leser das Scrollen nicht ab.
   ===================================================================== */
.v2-carousel { margin-top: 18px; }

.v2-tabs {
  display: flex; flex-wrap: wrap; gap: 8px;
  justify-content: center;
  margin-top: clamp(14px, 2.4vh, 26px);
}
.v2-tab {
  position: relative;
  border: 0; background: none; cursor: pointer;
  font-family: var(--meta);
  font-family: var(--meta);
  font-size: 11.5px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--faint);
  padding: 7px 13px 9px;
  border-radius: 8px;
  transition: color 0.2s ease, background 0.2s ease;
  overflow: hidden;
}
.v2-tab:hover { color: var(--text); background: var(--line-soft); }
/* Ohne sichtbaren Fokus weiss niemand, dass die Pfeiltasten jetzt wirken. */
.v2-tab:focus-visible,
.v2-arrow:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 3px;
}
.v2-tab[aria-selected="true"] { color: var(--text); background: var(--line-soft); }
/* Die Linie unter dem aktiven Reiter laeuft mit: sie zeigt, wieviel Zeit
   bis zum Wechsel bleibt - ein Karussell, das ohne Vorwarnung springt,
   ist der eigentliche Aerger. */
.v2-tab .bar {
  position: absolute; left: 0; bottom: 0; height: 2px; width: 100%;
  background: var(--brand); transform: scaleX(0); transform-origin: left;
}
.v2-tab[aria-selected="true"] .bar { transform: scaleX(1); transition: transform 0.2s linear; }
.v2-tab[aria-selected="true"].is-timing .bar {
  transform: scaleX(1); transition: transform var(--dwell, 6s) linear;
}
.v2-tab[aria-selected="true"].is-timing-reset .bar { transform: scaleX(0); transition: none; }
.v2-carousel.is-paused .v2-tab .bar { transition: none; }

/* Der Rahmen clippt, nicht der Track: overflow:hidden am Track schneidet
   an dessen eigener Box ab - und die wandert beim Verschieben mit. Genau
   deshalb war ab Folie 2 alles leer. */
.v2-stage { position: relative; }
.v2-viewport { position: relative; overflow: hidden; }
.v2-track {
  display: flex;
  /* Der Streifen links und rechts ist Fahrbahn der Pfeile und Guckloch
     zugleich. Bei 14% war er zu breit: die Ueberschrift der naechsten
     Folie stand direkt neben dem Bild der aktuellen, das las sich wie
     zwei Folien uebereinander statt wie "es geht weiter". Schmaler bleibt
     die Andeutung, ohne dass ein zweiter Inhalt lesbar wird. */
  padding-inline: clamp(56px, 8%, 110px);
  margin-top: 6px;
  transform: translateX(0);
  transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1);
}
.v2-carousel.is-instant .v2-track { transition: none; }
/* Die naechste Folie schaut rechts herein - das ist der ehrlichste
   Hinweis darauf, dass es weitergeht: man sieht es, statt es aus einem
   Pfeil zu schliessen. Die Nachbarn stehen zurueck, damit klar bleibt,
   was gerade dran ist. */
.v2-slide {
  /* 100% der Content-Box - die ist durch das Innenmass des Tracks schon
     um den Guckstreifen schmaler. So bleibt die aktive Folie mittig, ohne
     dass irgendwo eine Breite ausgerechnet werden muss. */
  flex: 0 0 100%;
  min-width: 0;
  opacity: 0.10;
  transition: opacity 0.45s var(--ease);
}
.v2-slide.is-on { opacity: 1; }
/* Ausnahme: Karussells, in denen ein Fernseher steht. Der fuellt seine
   Spalte bis an die Kante, also klebt die angedeutete Nachbarfolie
   direkt am Gehaeuse - zwei Inhalte ohne Luft dazwischen. Beim Telefon
   ist rechts und links Platz, dort bleibt die Andeutung. Die Fahrbahn
   der Pfeile bleibt in beiden Faellen stehen. */
.v2-carousel:has(.v2-row-media .tv) .v2-slide:not(.is-on) { opacity: 0; }
/* Ohne JS setzt niemand is-on - dann stehen alle Folien untereinander da,
   statt dass die Seite an dieser Stelle leer bleibt. */
html:not(.js) .v2-slide { opacity: 1; }
/* Weiche Kante statt harter Schnitt an der angeschnittenen Folie. */
.v2-carousel.has-next .v2-viewport {
  -webkit-mask-image: linear-gradient(90deg, #000 94%, transparent 100%);
          mask-image: linear-gradient(90deg, #000 94%, transparent 100%);
}
.v2-carousel.has-prev .v2-viewport {
  -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 6%);
          mask-image: linear-gradient(90deg, transparent 0%, #000 6%);
}
.v2-carousel.has-prev.has-next .v2-viewport {
  -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 6%, #000 94%, transparent 100%);
          mask-image: linear-gradient(90deg, transparent 0%, #000 6%, #000 94%, transparent 100%);
}

/* Die Folien 2..n liegen seitlich versetzt - der IntersectionObserver in
   site.js sieht sie nie und laesst .reveal auf opacity:0 stehen. Genau
   deshalb wirkten "Live Studio" und "Twin" leer. Im Karussell sind die
   Einblendungen also aus; das Karussell blendet selbst um. */
html.js .v2-slide .reveal { opacity: 1; transform: none; }

/* Pfeile ausserhalb der Folie - sie sagen auf den ersten Blick, dass hier
   mehr liegt als das, was man sieht. */
.v2-arrow {
  position: absolute; top: 50%; z-index: 3;
  width: 44px; height: 44px; margin-top: -22px;
  border: 1px solid var(--line); border-radius: 50%;
  background: var(--bg-elev); color: var(--text);
  display: grid; place-items: center; cursor: pointer;
  /* Der Pfeil steht ueber der abgeblendeten Nachbarfolie - ohne
     kraeftigen Schatten verschwindet er darin. */
  box-shadow: 0 2px 6px rgba(24, 32, 64, 0.10), 0 10px 28px -6px rgba(24, 32, 64, 0.28);
  transition: color 0.2s ease, border-color 0.2s ease, transform 0.2s ease,
              box-shadow 0.2s ease;
}
.v2-arrow:hover {
  color: var(--brand); border-color: var(--brand);
  box-shadow: 0 2px 6px rgba(24, 32, 64, 0.12), 0 14px 34px -6px rgba(24, 32, 64, 0.34);
}
/* Innen, nicht aussen: der Rahmen clippt jetzt, und aussen sitzende
   Pfeile waeren halb abgeschnitten. Platz ist da - Text und Geraet
   stehen als Gruppe in der Mitte. */
.v2-arrow.prev { left: 6px; }
.v2-arrow.next { right: 6px; }
.v2-arrow.prev:hover { transform: translateX(-2px); }
.v2-arrow.next:hover { transform: translateX(2px); }
.v2-arrow svg { display: block; }
[data-theme="dark"] .v2-arrow { background: #1b1f2a; }

/* In der Reihe wechseln Text und Bild die Seite; im Karussell nicht -
   sonst springt das Geraet bei jedem Wechsel hin und her. */
.v2-slide .v2-row {
  border-top: 0;
  padding-top: clamp(26px, 4vh, 44px);
  /* Enger zusammen: bei 1fr blieb rechts neben dem Text Leerraum, den
     die Spalte trotzdem beanspruchte - Text und Geraet standen an den
     aeusseren Raendern. Jetzt ist die Textspalte so breit wie der Text,
     und das Paar steht als Gruppe mittig. */
  /* 460 statt 300: die Bildspalte war auf ein Telefon zugeschnitten, im
     Karussell steht dort aber ein Fernseher - links blieb Platz liegen,
     den niemand brauchte. */
  grid-template-columns: minmax(0, 46ch) minmax(0, 460px);
  justify-content: center;
  gap: clamp(24px, 3.2vw, 60px);
  align-items: center;
}
.v2-slide .v2-row.flip .v2-row-media { order: 0; }
.v2-slide .v2-row-media { place-items: center start; }
@media (max-width: 880px) {
  .v2-slide .v2-row { grid-template-columns: 1fr; }
  .v2-slide .v2-row-media { place-items: center; }
}

@media (prefers-reduced-motion: reduce) {
  /* Kein Autoplay, und die Folien wechseln ohne Bewegung. */
  .v2-track { transition: none; }
  .v2-tab .bar { display: none; }
}

/* --- Feedback -------------------------------------------------------
   Stand auf den echten App-Seiten und war beim Nachbau untergegangen. */
.v2-feedback { margin-top: 16px; }
.v2-feedback h3 {
  margin: 0; font-size: clamp(24px, 3vw, 34px);
  letter-spacing: -0.03em; font-weight: 800;
}
.v2-feedback p { margin: 12px 0 0; color: var(--dim); font-size: 16px; line-height: 1.6; max-width: 46ch; }
/* The feedback block now offers three routes (bug, idea, mail), so the links
   sit side by side and need room; they wrap to their own line on a phone. */
.v2-feedback .v2-lnk + .v2-lnk { margin-left: 26px; }
@media (max-width: 560px) {
  .v2-feedback .v2-lnk { display: block; margin-top: 16px; }
  .v2-feedback .v2-lnk + .v2-lnk { margin-left: 0; }
}
.v2-feedback .v2-lnk { display: inline-block; margin-top: 22px; text-transform: none; letter-spacing: 0.02em; font-size: 14px; }


/* Die Anmerkungen am Geraet nehmen die Farbe der App, nicht das globale
   Gold aus landing.css - sonst steht auf der pinken IPTV-Seite ein
   goldenes Wort. */
.float-chip b { color: var(--brand); }


/* Im Fuss dasselbe schlichte Zeichen wie in der Navigation und im
   Favicon - nicht die Verlaufsfassung aus /assets. */
/* .logo img, .logo svg aus landing.css setzt 30x30 und ist spezifischer
   als eine blosse Klasse - deshalb steht hier .logo davor, sonst wird das
   Zeichen im Fuss doppelt so gross wie gewollt. */
.logo .v2-foot-mark {
  height: 0.85em; width: auto; display: block; border-radius: 0;
  overflow: visible;                              /* siehe .v2-nav-mark */
  transform: translateY(0.07em) scaleX(1.2);    /* siehe .v2-nav-mark */
  transform-origin: center;
}
.v2-foot-mark .m {
  fill: none; stroke: var(--faint); stroke-width: 31.5; stroke-linejoin: round;
}


/* --- Karussell auf schmalen Schirmen: Liste statt Folien ------------
   Eine Folie ist hoeher als ein Handy-Display - man sieht nie den ganzen
   Eintrag und weiss nicht, dass rechts noch mehr liegt. Untereinander
   gelesen stimmt beides wieder. Reiter und Pfeile entfallen, weil es
   nichts mehr zu blaettern gibt; carousel.js haelt hier ausserdem den
   automatischen Wechsel an. */
@media (max-width: 880px) {
  .v2-carousel .v2-track {
    flex-direction: column;
    padding-inline: 0;   /* der Guckstreifen gehoert zur Folienansicht */
    transform: none !important;
    transition: none;
  }
  .v2-carousel .v2-viewport { overflow: visible; }
  /* In der Liste ist jeder Eintrag gleichwertig - die Dimmung der
     Nachbarn gilt nur fuer die Folienansicht, sonst waeren hier acht von
     neun Eintraegen kaum lesbar. */
  .v2-carousel .v2-slide { flex: 0 0 auto; opacity: 1; }
  .v2-carousel .v2-viewport { -webkit-mask-image: none; mask-image: none; }
  .v2-carousel .v2-tabs,
  .v2-carousel .v2-arrow { display: none; }
  /* Zwischen den Eintraegen wieder eine Trennung - in der Folienansicht
     uebernahm das der Wechsel selbst. */
  .v2-carousel .v2-slide + .v2-slide .v2-row {
    border-top: 1px solid var(--line-soft);
  }
}


/* --- Die drei Karten wippen nacheinander an -------------------------
   Nicht einfahren, sondern anheben: dieselbe Bewegung wie beim
   Ueberfahren - das Bild geht kurz hoch und wieder runter - laeuft einmal
   von links nach rechts durch, sobald der Abschnitt im Bild ist.

   Damit es EINE Bewegung bleibt, blendet die Karte selbst nur noch auf
   (kein zusaetzliches Einfahren von unten); das Wippen ist die Bewegung.
   Bei prefers-reduced-motion faellt beides weg. */
html.js .v2-app.reveal {
  transform: none;
  transition: opacity 0.5s var(--ease) var(--d, 0ms);
}
html.js .v2-app.reveal.in .v2-app-shot > img,
html.js .v2-app.reveal.in .v2-app-shot > .v2-stack {
  /* backwards, nicht both: mit `both` bleibt der Endzustand der Keyframes
     als Animation stehen, und eine Animation schlaegt jede normale
     Deklaration - das Ueberfahren hatte danach keine Wirkung mehr.
     `backwards` haelt nur den Startwert waehrend der Verzoegerung und gibt
     die Eigenschaft am Ende wieder frei. */
  animation: v2-card-bob 0.72s cubic-bezier(0.34, 1.2, 0.64, 1) var(--d, 0ms) backwards;
}
@keyframes v2-card-bob {
  0%   { transform: translateY(0); }
  42%  { transform: translateY(-12px); }
  100% { transform: translateY(0); }
}
/* Waehrend das Wippen laeuft, soll das Ueberfahren nicht dagegenhalten. */
@media (prefers-reduced-motion: reduce) {
  html.js .v2-app.reveal.in .v2-app-shot > img,
  html.js .v2-app.reveal.in .v2-app-shot > .v2-stack { animation: none; }
}


/* --- Feedback: drei Zeilen statt drei Links nebeneinander -----------
   Die Links standen als unterstrichene Woerter in einer Reihe und
   klebten aneinander - man sah nicht, dass zwei davon zu GitHub fuehren
   und einer ins Mailprogramm. Jetzt eine Zeile pro Weg, mit dem Zeichen
   des Ziels davor. */
.v2-fb-list {
  margin: 26px 0 0;
  padding: 0;
  list-style: none;
  max-width: 480px;
  border-top: 1px solid var(--line-soft);
}
.v2-fb-row {
  display: grid;
  grid-template-columns: 20px minmax(0, 1fr) auto;
  align-items: center;
  gap: 14px;
  padding: 15px 4px;
  border-bottom: 1px solid var(--line-soft);
  transition: padding-left 0.2s var(--ease), color 0.2s ease;
}
.v2-fb-row:hover { padding-left: 10px; color: var(--brand); }
.v2-fb-row .ico { width: 20px; height: 20px; display: block; fill: currentColor; opacity: 0.65; }
.v2-fb-row:hover .ico { opacity: 1; }
.v2-fb-row .lbl { font-size: 16px; font-weight: 600; letter-spacing: -0.01em; }
.v2-fb-row .hint {
  font-family: var(--meta);
  font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--faint);
}
.v2-fb-row:hover .hint { color: var(--brand); }


/* --- Support- und Rechtsseiten im gleichen Satz wie die Landingpages -
   Sie teilen sich landing.css ohnehin; hier bekommen sie die Metazeile,
   die grosse Ueberschrift und den ruhigen Textsatz. Die Frageliste steht
   offen da - auf einer Support-Seite will man die Antwort lesen, nicht
   erst aufklappen. */
.v2-doc { padding: calc(var(--nav-h) + clamp(28px, 6vh, 64px)) 0 clamp(48px, 8vh, 96px); }
.v2-doc .wrap { max-width: 760px; }
.v2-doc h1 {
  margin: 18px 0 0;
  font-size: clamp(38px, 5.4vw, 60px);
  line-height: 1;
  letter-spacing: -0.045em;
  font-weight: 800;
}
.v2-doc .v2-lede { margin-top: 18px; font-size: 17px; line-height: 1.62; color: var(--dim); max-width: 52ch; }
.v2-doc .v2-lede a { color: var(--brand); text-decoration: underline; text-underline-offset: 3px; }

.v2-doc section { margin-top: clamp(40px, 6vh, 68px); }
.v2-doc section > .v2-meta { margin-bottom: 14px; }

.v2-qa { margin: 0; padding: 0; list-style: none; border-top: 1px solid var(--line-soft); }
.v2-qa > li { padding: 22px 0; border-bottom: 1px solid var(--line-soft); }
.v2-qa h3 { margin: 0; font-size: 17px; font-weight: 600; letter-spacing: -0.015em; }
.v2-qa p { margin: 9px 0 0; color: var(--dim); font-size: 15.5px; line-height: 1.62; }
.v2-qa a { color: var(--brand); text-decoration: underline; text-underline-offset: 3px; }


/* --- Rechtstexte im gleichen Satz ------------------------------------
   Der Inhalt bleibt Wort fuer Wort wie er war - nur der Rahmen und der
   Satz sind die der uebrigen Seiten. Ein Rechtstext wird gelesen, nicht
   ueberflogen: schmales Mass, ruhige Zwischenueberschriften, klare
   Abstaende statt Kasten. */
.v2-prose { font-size: 16px; line-height: 1.68; color: var(--dim); }
.v2-prose > h2 {
  margin: clamp(34px, 5vh, 52px) 0 0;
  padding-top: clamp(20px, 3vh, 28px);
  border-top: 1px solid var(--line-soft);
  font-size: 21px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--text);
}
.v2-prose > h2:first-of-type { border-top: 0; padding-top: 0; }
.v2-prose > h3 {
  margin: 26px 0 0;
  font-size: 17px; font-weight: 600; letter-spacing: -0.015em; color: var(--text);
}
.v2-prose > p { margin: 12px 0 0; max-width: 68ch; }
.v2-prose > ul,
.v2-prose > ol { margin: 14px 0 0; padding-left: 0; list-style: none; max-width: 68ch; }
.v2-prose > ul > li,
.v2-prose > ol > li { position: relative; padding-left: 20px; margin-top: 8px; }
.v2-prose > ul > li::before {
  content: ""; position: absolute; left: 0; top: 0.72em;
  width: 7px; height: 1px; background: var(--brand);
}
.v2-prose > ol { counter-reset: v2ol; }
.v2-prose > ol > li { counter-increment: v2ol; }
.v2-prose > ol > li::before {
  content: counter(v2ol) "."; position: absolute; left: 0; top: 0;
  font-family: var(--meta); font-size: 12px; color: var(--faint);
}
.v2-prose strong { color: var(--text); font-weight: 600; }
.v2-prose a { color: var(--brand); text-decoration: underline; text-underline-offset: 3px; }
.v2-prose .contact {
  display: inline-block; font-family: var(--meta); font-size: 14px;
  color: var(--text); border-bottom: 1px solid var(--line); padding-bottom: 2px;
}


/* =====================================================================
   Geraetestapel: Fernseher hinten, Telefon davor
   =====================================================================
   Pano laeuft auf allem - ein einzelner Screenshot behauptet immer nur
   eine Plattform. Der Fernseher traegt die Buehne (dort liegt der
   Hauptfokus), das Telefon steht davor und schneidet ihn unten links an:
   zwei Geraete in einem Bild, ohne dass eine Plattform-Wolke noetig ist.

   Der Kasten misst sich selbst (container-type), damit beide Geraete in
   cqw rechnen und das Verhaeltnis zueinander bei jeder Spaltenbreite
   dasselbe bleibt. Die feste aspect-ratio ist noetig, weil das Telefon
   absolut sitzt und den Kasten sonst nicht mit aufspannen wuerde. */
.v2-stack {
  position: relative;
  container-type: inline-size;
  width: 100%;
  /* Hoehe = Fernseher (ca. 50cqw) plus der Teil des Telefons, der unten
     herausragt. Fest, weil das Telefon absolut sitzt. */
  aspect-ratio: 100 / 73;
}
.v2-stack .tv {
  position: absolute;
  right: 0;
  top: 0;
  width: 94cqw;
  margin: 0;
  border-radius: 2.6cqw;
  padding: 1.3cqw;
}
.v2-stack .tv::before { bottom: -1.2cqw; height: 1.2cqw; }
.v2-stack .tv img { border-radius: 1.5cqw; }
/* Das Telefon traegt seine Masse als Anteil seiner eigenen Breite: die
   Werte aus .phone (54px Radius, 12px Polster, 88x26 Island) sind fuer
   ein 320px-Geraet gemacht und saehen bei 100px wie ein Spielzeug aus. */
.v2-stack .phone {
  --pw: 27cqw;
  position: absolute;
  left: 0;
  bottom: 0;
  z-index: 2;
  width: var(--pw);
  max-width: none;
  margin: 0;
  padding: calc(var(--pw) * 0.037);
  border-radius: calc(var(--pw) * 0.17);
}
.v2-stack .phone::before {
  top: calc(var(--pw) * 0.075);
  width: calc(var(--pw) * 0.275);
  height: calc(var(--pw) * 0.081);
}
.v2-stack .phone img { border-radius: calc(var(--pw) * 0.132); }
/* Der Schein hinter dem Telefon liegt sonst ueber dem Fernsehbild. */
.v2-stack .phone::after { display: none; }


/* =====================================================================
   Bilder gross ansehen (Lightbox)
   =====================================================================
   Auf den Detailseiten sind die Screenshots das Argument, und in einer
   Feature-Zeile sind sie 300px breit. Ein Klick legt denselben Bildpfad
   ueber die Seite - kein zweites Asset, keine Galerie, kein Router.
   Ohne JS passiert nichts: der Cursor kommt aus JS (.js), das Bild bleibt
   ein Bild. */
html.js [data-zoom] img { cursor: zoom-in; }
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(16px, 4vw, 56px);
  background: color-mix(in srgb, #05060a 82%, transparent);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.24s var(--ease);
  cursor: zoom-out;
}
.lightbox.is-on { opacity: 1; pointer-events: auto; }
.lightbox img {
  display: block;
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  border-radius: 10px;
  box-shadow: 0 40px 90px -30px rgba(0, 0, 0, 0.8);
  transform: scale(0.97);
  transition: transform 0.24s var(--ease);
}
.lightbox.is-on img { transform: scale(1); }
.lightbox-close {
  position: absolute;
  top: 14px;
  right: 16px;
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  font-size: 26px;
  line-height: 1;
  color: #fff;
  background: rgba(255, 255, 255, 0.12);
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 999px;
  cursor: pointer;
}
.lightbox-close:hover { background: rgba(255, 255, 255, 0.22); }
.lightbox-cap {
  position: absolute;
  left: 50%;
  bottom: 18px;
  transform: translateX(-50%);
  max-width: min(760px, 88vw);
  text-align: center;
  font-size: 13.5px;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.82);
}
@media (prefers-reduced-motion: reduce) {
  .lightbox, .lightbox img { transition: none; }
  .lightbox img { transform: none; }
}

/* ---------------------------------------------------- cookie consent */
/* Built by shared/consent.js. Accept and Decline share one style on purpose:
   neither choice may be the visually easier one. */

.consent {
  position: fixed;
  left: 16px;
  right: 16px;
  bottom: 16px;
  z-index: 80;
  max-width: 640px;
  margin: 0 auto;
  padding: 18px 20px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px 20px;
  background: var(--bg-elev);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 18px 48px -12px rgba(10, 14, 30, 0.28);
  font-size: 14.5px;
  line-height: 1.5;
}
.consent[hidden] { display: none; }
.consent p { margin: 0; flex: 1 1 320px; color: var(--dim); }
.consent-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-left: auto;
}
.consent-more { color: var(--dim); text-decoration: underline; margin-right: 6px; }
.consent .btn { padding: 9px 18px; font-size: 14.5px; cursor: pointer; font-family: inherit; }
[data-theme="dark"] .consent { box-shadow: 0 18px 48px -12px rgba(0, 0, 0, 0.6); }

/* Hero-Reel auf der Musaic-Seite: 9:16 ohne Geraeterahmen, nicht
   angeschnitten, mit Ton-Schalter (Autoplay laeuft stumm). */
.hero-reel {
  position: relative;
  width: min(340px, 78vw);
  aspect-ratio: 9 / 16;
  margin: 0 auto;
  border-radius: 28px;
  overflow: hidden;
  background: #000;
  box-shadow: 0 34px 80px -30px rgba(24, 32, 64, 0.45), 0 0 0 1px rgba(0, 0, 0, 0.12);
}
.hero-reel video { display: block; width: 100%; height: 100%; object-fit: cover; cursor: pointer; }
.hero-reel-snd {
  position: absolute; left: 14px; bottom: 14px;
  padding: 9px 16px; border: 0; border-radius: 999px;
  font: 600 13px/1 var(--meta, system-ui);
  letter-spacing: 0.04em;
  color: #fff; background: rgba(0, 0, 0, 0.55);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  cursor: pointer;
}
.hero-reel-snd.is-on { background: var(--brand); }
.hero-reel-snd:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.v2-hero-grid.is-app .v2-shot:has(.hero-reel) { overflow: visible; display: flex; align-items: center; }
