/* ============================================
   Orbit Landing — Dark theme
   Palette: #0C0C0E / #F2F2F7 / #0A84FF / #30D158
   ============================================ */

/* ---------- Self-hosted fonts (was: Google Fonts) ---------- */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(fonts/dm-sans-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(fonts/dm-sans-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(fonts/dm-sans-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(fonts/dm-sans-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(fonts/dm-sans-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(fonts/dm-sans-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Outfit';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(fonts/outfit-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Outfit';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(fonts/outfit-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Outfit';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(fonts/outfit-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Outfit';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(fonts/outfit-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Outfit';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(fonts/outfit-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Outfit';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(fonts/outfit-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  --bg: #0C0C0E;
  --bg-elevated: #131316;
  --surface: #1C1C1E;
  --surface-2: #28282B;
  --text: #F2F2F7;
  --text-2: #AEAEB6;
  --text-3: #80808A;
  --accent: #0A84FF;
  --accent-hover: #0066d6;
  --accent-soft: rgba(10, 132, 255, 0.14);
  --accent-glow: rgba(10, 132, 255, 0.28);
  --green: #30D158;
  --red: #FF453A;
  --border: rgba(255, 255, 255, 0.07);
  --border-strong: rgba(255, 255, 255, 0.12);
  --radius: 16px;
  --radius-lg: 24px;
  --font-display: 'Outfit', sans-serif;
  --font-body: 'DM Sans', -apple-system, BlinkMacSystemFont, sans-serif;
  --font-mono: ui-monospace, 'Cascadia Mono', 'SFMono-Regular', Consolas, monospace;
  --nav-h: 72px;
  --max: 1120px;
  --ease: cubic-bezier(0.22, 1, 0.36, 1);

  /* ---- Semantic surfaces ----
     The dark palette used to be hardcoded at ~50 call sites, which is why the
     page could not be themed. Everything below is overridden wholesale in
     [data-theme="light"] further down; nothing else in this file hardcodes a
     surface colour. */
  --card-bg: rgba(28, 28, 30, 0.55);          /* feature / faq / download cards */
  --card-bg-hover: rgba(28, 28, 30, 0.82);
  --card-bg-soft: rgba(28, 28, 30, 0.35);     /* trust strip, need-callout */
  --card-bg-mid: rgba(28, 28, 30, 0.45);      /* spec panel, walkthrough steps */
  --card-bg-strong: rgba(28, 28, 30, 0.65);

  --glass: rgba(12, 12, 14, 0.62);            /* navbar */
  --glass-strong: rgba(12, 12, 14, 0.88);     /* scrolled navbar, open menu */
  --glass-solid: rgba(28, 28, 30, 0.95);      /* floating controls over media */
  --scrim: rgba(8, 8, 10, 0.78);              /* lightbox backdrop */

  --overlay-softer: rgba(255, 255, 255, 0.04); /* eyebrows, pills, tab strip */
  --overlay-soft: rgba(255, 255, 255, 0.05);
  --overlay-strong: rgba(255, 255, 255, 0.1);
  --overlay-stronger: rgba(255, 255, 255, 0.12);
  --grid-line: rgba(255, 255, 255, 0.028);     /* hero grid */

  --shadow-sm: 0 24px 64px rgba(0, 0, 0, 0.35);
  --shadow-md: 0 8px 24px rgba(0, 0, 0, 0.4);
  --shadow-xl: 0 40px 100px rgba(0, 0, 0, 0.55);

  --link-hover: #60b0ff;
  --heading-grad: linear-gradient(135deg, #F2F2F7 10%, #0A84FF 100%);
  --accent-border: rgba(10, 132, 255, 0.28);
  --accent-border-strong: rgba(10, 132, 255, 0.35);
  --banner-bg: linear-gradient(90deg, rgba(10, 132, 255, 0.16) 0%, rgba(10, 132, 255, 0.06) 45%, rgba(48, 209, 88, 0.10) 100%);

  /* Device / window chrome around the app screenshots. Deliberately NOT
     themed: the screenshots inside are dark-mode captures, so a dark bezel is
     correct in both themes (same call Apple makes on its own site). */
  --frame-bg: #101013;
  --frame-bg-alt: #161618;
  --accent-rgb: 10, 132, 255;
  --green-rgb: 48, 209, 88;
  --shadow-rgb: 0, 0, 0;
}

/* ---------- Light theme ----------
   Applied by setting data-theme on <html>. The inline script in index.html
   resolves "system" to light/dark before first paint, so this block is the
   only place the light palette lives. */
[data-theme="light"] {
  color-scheme: light;

  --bg: #F6F6F8;
  --bg-elevated: #FFFFFF;
  --surface: #FFFFFF;
  --surface-2: #EFEFF2;
  --text: #0E0E12;
  --text-2: #55555F;
  --text-3: #83838E;
  --accent: #0A7AE8;
  --accent-hover: #005CC4;
  --accent-soft: rgba(10, 122, 232, 0.10);
  --accent-glow: rgba(10, 122, 232, 0.20);
  --green: #17913D;
  --red: #D70015;
  --border: rgba(0, 0, 0, 0.10);
  --border-strong: rgba(0, 0, 0, 0.17);

  --card-bg: rgba(255, 255, 255, 0.92);
  --card-bg-hover: #FFFFFF;
  --card-bg-soft: rgba(255, 255, 255, 0.72);
  --card-bg-mid: rgba(255, 255, 255, 0.82);
  --card-bg-strong: #FFFFFF;

  --glass: rgba(246, 246, 248, 0.72);
  --glass-strong: rgba(246, 246, 248, 0.94);
  --glass-solid: rgba(255, 255, 255, 0.96);
  --scrim: rgba(20, 20, 24, 0.62);

  --overlay-softer: rgba(0, 0, 0, 0.04);
  --overlay-soft: rgba(0, 0, 0, 0.05);
  --overlay-strong: rgba(0, 0, 0, 0.08);
  --overlay-stronger: rgba(0, 0, 0, 0.11);
  --grid-line: rgba(0, 0, 0, 0.035);

  --shadow-sm: 0 20px 50px rgba(15, 23, 42, 0.10);
  --shadow-md: 0 8px 22px rgba(15, 23, 42, 0.12);
  --shadow-xl: 0 36px 80px rgba(15, 23, 42, 0.18);

  --link-hover: #005CC4;
  --heading-grad: linear-gradient(135deg, #0E0E12 10%, #0A7AE8 100%);
  --accent-border: rgba(10, 122, 232, 0.32);
  --accent-border-strong: rgba(10, 122, 232, 0.45);
  --banner-bg: linear-gradient(90deg, rgba(10, 122, 232, 0.10) 0%, rgba(10, 122, 232, 0.04) 45%, rgba(23, 145, 61, 0.08) 100%);
  --accent-rgb: 10, 122, 232;
  --green-rgb: 23, 145, 61;
  --shadow-rgb: 15, 23, 42;
}

*, *::before, *::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--nav-h) + 16px);
}
html.has-banner {
  scroll-padding-top: calc(var(--banner-h, 40px) + var(--nav-h) + 16px);
}

body {
  font-family: var(--font-body);
  background: var(--bg);
  color: var(--text);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* clip, not hidden: overflow-x:hidden forces overflow-y to compute to
     auto, which makes <body> a scroll container. That hijacks
     animation-timeline: view() (and position: sticky) for everything
     inside it, leaving elements frozen at their starting keyframe.
     clip blocks horizontal overflow without creating a scroll container. */
  overflow-x: clip;
}

::selection {
  background: rgba(var(--accent-rgb), 0.4);
  color: var(--text);
}

::-webkit-scrollbar { width: 8px; }
::-webkit-scrollbar-track { background: var(--bg); }
::-webkit-scrollbar-thumb {
  background: var(--text-3);
  border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover { background: var(--text-2); }

img, svg { display: block; max-width: 100%; }
button { font-family: inherit; }

a {
  color: var(--accent);
  text-decoration: none;
  transition: color 0.2s ease;
}
a:hover { color: var(--link-hover); }

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

.skip-link {
  position: absolute;
  left: 16px;
  top: -48px;
  z-index: 2000;
  background: var(--accent);
  color: #fff;
  padding: 8px 14px;
  border-radius: 8px;
  font-weight: 600;
  font-size: 0.875rem;
}
.skip-link:focus { top: 16px; color: #fff; }

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

/* ---------- Motion ---------- */
/* ==================================================================
   MOTION SYSTEM
   ------------------------------------------------------------------
   Previously every one of the 35 revealed elements ran the same
   fade + 24px rise, and the stagger stopped at the sixth child — so
   the 12-card features grid visibly lost its rhythm halfway through.

   Variety is now derived from ELEMENT TYPE in CSS rather than from
   new markup, so the existing HTML needs no per-element classes:
   section headers come in from the side, cards scale up as they
   arrive, everything else rises.
   ================================================================== */

.fade-up {
  opacity: 0;
  transform: translateY(26px);
  transition:
    opacity 0.72s var(--ease),
    transform 0.72s var(--ease),
    filter 0.72s var(--ease);
  will-change: opacity, transform;
}

.fade-up.visible {
  opacity: 1;
  transform: none;
}

/* Section headers lead, arriving from the side rather than rising —
   gives each section an entrance beat instead of a uniform lift. */
.section-header.fade-up {
  transform: translateX(-30px);
  transition-duration: 0.8s;
}

/* Cards arrive by settling into place: a small scale with the rise.
   Reads as "landing" and separates them from text blocks. */
.feature-card.fade-up,
.step-card.fade-up,
.faq-item.fade-up {
  transform: translateY(22px) scale(0.975);
  transition-duration: 0.66s;
}

/* Any imagery that reveals gets a slight blur burn-off, which hides
   the flatness of a pure opacity fade on photographic content. */
.fade-up.visible { filter: none; }
.fade-up:not(.visible) { filter: blur(2px); }

/* ------------------------------------------------------------------
   Stagger — now unlimited.
   `--stagger-i` is set per child by the reveal script, so a group of
   any length cascades evenly. The nth-child rules below stay as a
   no-JS fallback and are overridden when the variable is present.
   ------------------------------------------------------------------ */
.fade-up-stagger > .fade-up {
  transition-delay: calc(var(--stagger-i, 0) * 58ms);
}

.fade-up-stagger > .fade-up:nth-child(1) { --stagger-i: 0; }
.fade-up-stagger > .fade-up:nth-child(2) { --stagger-i: 1; }
.fade-up-stagger > .fade-up:nth-child(3) { --stagger-i: 2; }
.fade-up-stagger > .fade-up:nth-child(4) { --stagger-i: 3; }
.fade-up-stagger > .fade-up:nth-child(5) { --stagger-i: 4; }
.fade-up-stagger > .fade-up:nth-child(6) { --stagger-i: 5; }
.fade-up-stagger > .fade-up:nth-child(7) { --stagger-i: 6; }
.fade-up-stagger > .fade-up:nth-child(8) { --stagger-i: 7; }
.fade-up-stagger > .fade-up:nth-child(9) { --stagger-i: 8; }
.fade-up-stagger > .fade-up:nth-child(10) { --stagger-i: 9; }
.fade-up-stagger > .fade-up:nth-child(11) { --stagger-i: 10; }
.fade-up-stagger > .fade-up:nth-child(12) { --stagger-i: 11; }
.fade-up-stagger > .fade-up:nth-child(n+13) { --stagger-i: 12; }

/* ------------------------------------------------------------------
   Scroll-linked depth.
   `--scroll-y` is set on :root by the reveal script. Section backdrop
   art drifts against the scroll so the page has parallax instead of
   moving as one flat sheet.
   ------------------------------------------------------------------ */
.section-bg {
  transform: translate3d(0, calc(var(--scroll-y, 0px) * var(--parallax, -0.06)), 0);
  will-change: transform;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  .fade-up,
  .section-header.fade-up,
  .feature-card.fade-up,
  .step-card.fade-up,
  .faq-item.fade-up {
    opacity: 1;
    transform: none;
    filter: none;
    transition: none;
    transition-delay: 0s;
  }

  .section-bg { transform: none; }
  .orb { animation: none !important; }
  .back-to-top { transition: none; }
  .mobile-slides { transition: none; }
  .announcement-bar { animation: none; }
}


/* ==================================================================
   HERO CHOREOGRAPHY
   ------------------------------------------------------------------
   The hero is above the fold, so it must not wait for a scroll — it
   plays on load. Each piece arrives on its own beat, and the two
   headline lines come in from opposite sides.
   ================================================================== */

.hero-line {
  display: inline-block;
  opacity: 0;
}

.hero-line-l { --hero-from: -58px; }
.hero-line-r { --hero-from: 58px; }

@keyframes heroLineIn {
  from { opacity: 0; transform: translateX(var(--hero-from, 0)); }
  to   { opacity: 1; transform: translateX(0); }
}

/* The rest of the hero copy rises, so the headline is the only thing
   moving sideways and the eye follows it. */
@keyframes heroRiseIn {
  from { opacity: 0; transform: translateY(18px); }
  to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: no-preference) {
  .hero-copy { opacity: 1; transform: none; }   /* the container does not animate */

  .hero-line {
    animation: heroLineIn 0.9s var(--ease) both;
  }
  .hero-line-l { animation-delay: 0.12s; }
  .hero-line-r { animation-delay: 0.26s; }

  .hero-copy > .eyebrow,
  .hero-copy > p,
  .hero-copy > .hero-cta-row,
  .hero-copy > .os-badges {
    animation: heroRiseIn 0.8s var(--ease) both;
  }
  .hero-copy > .eyebrow        { animation-delay: 0.04s; }
  .hero-copy > p               { animation-delay: 0.42s; }
  .hero-copy > .hero-cta-row   { animation-delay: 0.56s; }
  .hero-copy > .os-badges      { animation-delay: 0.68s; }
}

@media (prefers-reduced-motion: reduce) {
  .hero-line,
  .hero-copy > .eyebrow,
  .hero-copy > p,
  .hero-copy > .hero-cta-row,
  .hero-copy > .os-badges {
    opacity: 1;
    animation: none;
    transform: none;
  }
}

/* ==================================================================
   SCROLL-DRIVEN ANIMATION
   ------------------------------------------------------------------
   The difference that matters: the old reveals were TRIGGERED by scroll
   (fire once, play for 0.7s, done). These are DRIVEN by it — the
   animation's progress IS the scroll position, so they run forward and
   backward at exactly the speed you scroll, and they run on the
   compositor instead of in a JS handler.

   Progressive enhancement: browsers without animation-timeline keep the
   existing transition + IntersectionObserver path untouched, so nothing
   regresses and content is never hidden.
   ================================================================== */

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {

    @keyframes sdRevealUp {
      from { opacity: 0; transform: translateY(30px); filter: blur(2px); }
      to   { opacity: 1; transform: translateY(0);    filter: blur(0); }
    }
    @keyframes sdRevealLeft {
      from { opacity: 0; transform: translateX(-34px); }
      to   { opacity: 1; transform: translateX(0); }
    }
    @keyframes sdCardIn {
      from { opacity: 0; transform: translateY(26px) scale(0.97); }
      to   { opacity: 1; transform: translateY(0)    scale(1); }
    }

    /* Hand these elements over to the scroll timeline entirely — the
       transition path must be switched off or the two fight. */
    .fade-up {
      transition: none;
      opacity: 1;                 /* the animation owns opacity now */
      transform: none;
      animation: sdRevealUp linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 85%;
    }

    .section-header.fade-up {
      animation-name: sdRevealLeft;
      animation-range: entry 0% entry 88%;
    }

    .feature-card.fade-up,
    .step-card.fade-up,
    .faq-item.fade-up {
      animation-name: sdCardIn;
      animation-range: entry 0% entry 82%;
    }

    /* Stagger along the timeline rather than in seconds: each child
       starts slightly earlier, so a grid ripples regardless of length. */
    .fade-up-stagger > .fade-up { animation-delay: calc(var(--stagger-i, 0) * -1.6%); }

    /* Section backdrop art drifts as the section crosses the viewport.
       Complements the JS parallax and takes over where supported. */
    .section-bg {
      animation: sdParallax linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
      transform: none;
    }
    @keyframes sdParallax {
      from { transform: translateY(-4%); }
      to   { transform: translateY(4%); }
    }
  }
}


/* ==================================================================
   READING PROGRESS + TOP BAR MOTION
   ================================================================== */

@keyframes growWidth {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

.progress-bar {
  position: fixed;
  top: 0; left: 0;
  width: 100%;
  height: 2px;
  z-index: 1005;
  pointer-events: none;
  transform-origin: 0 50%;
  transform: scaleX(0);           /* default for browsers without a scroll timeline */
  background: linear-gradient(90deg, var(--accent-primary, #3b82f6), var(--accent-secondary, #8b5cf6));
}

@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    .progress-bar {
      animation: growWidth linear both;
      animation-timeline: scroll(root block);
    }
  }
}

/* The navbar now reacts to scroll direction: it steps aside when you
   read downward and comes straight back when you scroll up, so the top
   of the page is never permanently occupied. */
.navbar {
  transition:
    transform 0.36s var(--ease),
    background 0.3s var(--ease),
    box-shadow 0.3s var(--ease),
    border-color 0.3s var(--ease);
}

.navbar.is-hidden {
  /* Must clear the sticky announcement bar as well as its own height.
     translateY(-100%) alone moved the nav from 42..114 to -30..42 — landing exactly
     behind the banner, so the two rendered on top of each other. Adding the banner
     height (plus a small margin) takes it fully off-screen instead. */
  transform: translateY(calc(-100% - var(--banner-h, 0px) - 4px));
}

.navbar.is-scrolled {
  background: color-mix(in srgb, var(--glass) 88%, transparent);
  border-bottom: 1px solid var(--border-subtle);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.22);
}

/* Nav links get a sweep underline instead of a colour flip. */
.nav-link {
  position: relative;
}

.nav-link::after {
  content: '';
  position: absolute;
  left: 0; right: 0;
  bottom: -5px;
  height: 1.5px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--accent-primary, #3b82f6), var(--accent-secondary, #8b5cf6));
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 0.32s var(--ease);
}

.nav-link:hover::after,
.nav-link.active::after {
  transform: scaleX(1);
}

/* The announcement bar drops in rather than simply appearing. */
@keyframes bannerDrop {
  from { opacity: 0; transform: translateY(-100%); }
  to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: no-preference) {
  .announcement-bar {
    animation: bannerDrop 0.5s var(--ease) both;
  }
}

/* ==================================================================
   CARD + BUTTON MICRO-INTERACTIONS
   Guarded behind (hover: hover) so touch devices never get a stuck
   hover state after a tap.
   ================================================================== */

@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .feature-card,
  .step-card,
  .faq-item {
    transition:
      transform 0.3s var(--ease),
      border-color 0.3s var(--ease),
      box-shadow 0.3s var(--ease);
  }

  .feature-card:hover,
  .step-card:hover,
  .faq-item:hover {
    transform: translateY(-4px);
    border-color: var(--accent-border-strong, var(--border-strong));
    box-shadow: 0 14px 34px rgba(0, 0, 0, 0.26);
  }

  /* Icon lifts a touch more than the card, so the card reads as a
     surface and the icon as something resting on it. */
  .feature-card:hover .feature-icon {
    transform: translateY(-2px) scale(1.06);
  }
  .feature-icon {
    transition: transform 0.3s var(--ease);
  }

  /* Sheen across the primary CTA. */
  .btn-primary {
    position: relative;
    overflow: hidden;
  }
  .btn-primary::after {
    content: '';
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: linear-gradient(105deg, transparent 32%, rgba(255, 255, 255, 0.3) 50%, transparent 68%);
    transform: translateX(-130%);
  }
  .btn-primary:hover::after {
    transform: translateX(130%);
    transition: transform 0.75s var(--ease);
  }

  .os-badge {
    transition: transform 0.25s var(--ease), border-color 0.25s var(--ease);
  }
  .os-badge:hover {
    transform: translateY(-2px);
    border-color: var(--accent-border, var(--border-strong));
  }
}

/* ==================================================================
   PER-SECTION SCROLL PROGRESS
   A hairline under each section that fills as that section passes.
   Elements are injected by the page script.
   ================================================================== */

/* NOTE: per-section progress hairlines were removed here.
   They were injected as each section's first child, so they rendered above the heading
   looking like a stray rule, and the bar was wider than the text beneath it. The reading
   progress bar at the top of the page already covers scroll feedback. */

/* Back-to-top gains a proper entrance instead of popping in. */
.back-to-top {
  transition:
    opacity 0.3s var(--ease),
    transform 0.3s var(--ease),
    background 0.25s var(--ease);
}
.back-to-top:not(.visible):not(.show) {
  opacity: 0;
  transform: translateY(12px) scale(0.94);
  pointer-events: none;
}
.back-to-top.visible,
.back-to-top.show {
  opacity: 1;
  transform: none;
}

/* ==================================================================
   Reduced motion: switch all of the above off.
   ================================================================== */

@media (prefers-reduced-motion: reduce) {
  .progress-bar { transform: scaleX(1); animation: none; }
  .navbar,
  .navbar.is-hidden { transform: none; transition: none; }
  .nav-link::after,
  .btn-primary::after { display: none; }
  .feature-card:hover,
  .step-card:hover,
  .faq-item:hover,
  .os-badge:hover { transform: none; }
  .back-to-top { opacity: 1; transform: none; }
  .announcement-bar { animation: none; }
}


/* ==================================================================
   ELEMENT-LEVEL MOTION
   ------------------------------------------------------------------
   The audit found these still completely static: spec-row, walkthrough-
   step/num, faq-item, section-kicker, phone-frame, preview-panel,
   mobile-slide, theme-card, platform-icon.

   Everything below animates elements that are ALREADY there — no new
   visual parts, no decorative rules. Only opacity and transform, so
   nothing can read as a stray artefact.
   ================================================================== */

/* ---- Section kickers ---- */
@keyframes kickerIn {
  from { opacity: 0; transform: translateY(8px); letter-spacing: 0.22em; }
  to   { opacity: 1; transform: translateY(0);   letter-spacing: 0.14em; }
}

/* ---- Spec rows: label in from the left, value from the right ----
   The row is a 2-column grid, so each half can travel toward the other
   and meet. Reads as the row assembling itself rather than fading in. */
@keyframes specFromLeft {
  from { opacity: 0; transform: translateX(-20px); }
  to   { opacity: 1; transform: translateX(0); }
}
@keyframes specFromRight {
  from { opacity: 0; transform: translateX(20px); }
  to   { opacity: 1; transform: translateX(0); }
}

/* ---- Walkthrough steps ---- */
@keyframes stepNumPop {
  0%   { opacity: 0; transform: scale(0.6); }
  60%  { opacity: 1; transform: scale(1.08); }
  100% { opacity: 1; transform: scale(1); }
}
@keyframes stepCardIn {
  from { opacity: 0; transform: translateY(26px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ---- FAQ items ---- */
@keyframes faqIn {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ---- Device frames breathe, at slightly different rates ----
   Two durations a little apart read as depth: the eye cannot tell the
   difference consciously, but the pair stops looking like one flat
   image. */
@keyframes deviceFloat {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-9px); }
}

@keyframes panelIn {
  from { opacity: 0; transform: translateY(18px) scale(0.985); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

@media (prefers-reduced-motion: no-preference) {

  /* Kickers */
  .section-kicker { animation: kickerIn 0.7s var(--ease) both; }

  /* Walkthrough */
  .walkthrough-step {
    animation: stepCardIn 0.7s var(--ease) both;
    animation-delay: calc(var(--step-i, 0) * 0.1s);
  }
  .walkthrough-num { animation: stepNumPop 0.6s var(--ease) both; }
  .walkthrough-step:nth-child(1) { --step-i: 0; }
  .walkthrough-step:nth-child(2) { --step-i: 1; }
  .walkthrough-step:nth-child(3) { --step-i: 2; }
  .walkthrough-num { animation-delay: 0.12s; }

  /* FAQ — a short cascade so a long list still feels responsive */
  .faq-item {
    animation: faqIn 0.55s var(--ease) both;
    animation-delay: calc(var(--faq-i, 0) * 0.07s);
  }
  .faq-item:nth-child(1) { --faq-i: 0; }
  .faq-item:nth-child(2) { --faq-i: 1; }
  .faq-item:nth-child(3) { --faq-i: 2; }
  .faq-item:nth-child(4) { --faq-i: 3; }
  .faq-item:nth-child(5) { --faq-i: 4; }
  .faq-item:nth-child(6) { --faq-i: 5; }
  .faq-item:nth-child(n+7) { --faq-i: 6; }

  /* Device frames */
  .phone-frame {
    animation: deviceFloat 7s ease-in-out infinite;
    will-change: transform;
  }
  .mobile-slide .phone-frame { animation-duration: 8.6s; }
  .mobile-slide:nth-child(odd) .phone-frame { animation-duration: 9.4s; }

  /* Preview panel */
  .preview-panel { animation: panelIn 0.6s var(--ease) both; }
}

/* ---- Scroll-driven half: spec rows, where supported ---- */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {

    .spec-row > :first-child {
      animation: specFromLeft linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 85%;
    }
    .spec-row > :last-child {
      animation: specFromRight linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 85%;
    }

    /* Each kicker rides in with its own section rather than on a timer. */
    .section-kicker {
      animation: kickerIn linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 90%;
    }

    .walkthrough-step {
      animation: stepCardIn linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 85%;
    }
    .walkthrough-num {
      animation: stepNumPop linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 82%;
    }

    .faq-item {
      animation: faqIn linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 80%;
    }
  }
}

/* ---- Platform icons react on hover ---- */
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .platform-icon {
    transition: transform 0.3s var(--ease);
  }
  .download-card:hover .platform-icon {
    transform: translateY(-3px) scale(1.08);
  }
}

/* ---- Reduced motion: switch all of the above off ---- */
@media (prefers-reduced-motion: reduce) {
  .section-kicker,
  .walkthrough-step,
  .walkthrough-num,
  .faq-item,
  .preview-panel,
  .spec-row > :first-child,
  .spec-row > :last-child {
    opacity: 1;
    animation: none;
    transform: none;
    letter-spacing: 0.14em;
  }
  .phone-frame { animation: none; }
  .download-card:hover .platform-icon { transform: none; }
}


/* ==================================================================
   DESKTOP PREVIEW
   ------------------------------------------------------------------
   The large screenshot swapped instantly when a thumbnail was clicked —
   a hard cut from one 1440x900 image to another. It now eases in, which
   mirrors how the mobile carousel slides.
   ================================================================== */

@keyframes shotSwap {
  from { opacity: 0; transform: scale(0.985); }
  to   { opacity: 1; transform: scale(1); }
}

@media (prefers-reduced-motion: no-preference) {
  .preview-stage.swapping #desktopShot {
    animation: shotSwap 0.45s var(--ease) both;
  }
}

/* The stage lifts slightly under the pointer, so the preview reads as an
   object on the page rather than a flat picture of one. */
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .preview-stage {
    transition: transform 0.4s var(--ease), box-shadow 0.4s var(--ease), border-color 0.4s var(--ease);
  }
  .preview-stage:hover {
    transform: translateY(-4px);
    border-color: var(--accent-border, var(--border-strong));
    box-shadow: 0 18px 40px rgba(0, 0, 0, 0.28);
  }
}

@media (prefers-reduced-motion: reduce) {
  .preview-stage.swapping #desktopShot { animation: none; }
  .preview-stage:hover { transform: none; }
}


/* ==================================================================
   NAVBAR ENTRANCE
   ------------------------------------------------------------------
   The bar simply existed on load. Animating the navbar itself would fight the
   hide-on-scroll transform, so the motion goes on its inner container and the
   links inside it.
   ================================================================== */

@keyframes navBarIn {
  from { opacity: 0; transform: translateY(-14px); }
  to   { opacity: 1; transform: translateY(0); }
}

@keyframes navItemIn {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: no-preference) {
  .navbar > .container {
    animation: navBarIn 0.55s var(--ease) both;
  }

  /* Each link arrives on its own beat, so the bar assembles left to right. */
  .navbar .nav-link,
  .navbar .nav-actions > * {
    animation: navItemIn 0.5s var(--ease) both;
  }
  .navbar .nav-link:nth-child(1) { animation-delay: 0.10s; }
  .navbar .nav-link:nth-child(2) { animation-delay: 0.16s; }
  .navbar .nav-link:nth-child(3) { animation-delay: 0.22s; }
  .navbar .nav-link:nth-child(4) { animation-delay: 0.28s; }
  .navbar .nav-link:nth-child(5) { animation-delay: 0.34s; }
  .navbar .nav-link:nth-child(6) { animation-delay: 0.40s; }
  .navbar .nav-actions > *:nth-child(1) { animation-delay: 0.46s; }
  .navbar .nav-actions > *:nth-child(2) { animation-delay: 0.52s; }
  .navbar .nav-actions > *:nth-child(3) { animation-delay: 0.58s; }

  .navbar-logo {
    animation: navBarIn 0.6s var(--ease) both;
  }
}

@media (prefers-reduced-motion: reduce) {
  .navbar > .container,
  .navbar .nav-link,
  .navbar .nav-actions > *,
  .navbar-logo {
    opacity: 1;
    animation: none;
    transform: none;
  }
}

/* ---------- Announcement bar ---------- */
.announcement-bar {
  position: sticky;
  top: 0;
  z-index: 1001;
  font-size: 0.8125rem;
  color: var(--text-2);
  /* The tint is a translucent gradient, so on its own the bar is see-through —
     and the navbar, which slides up behind it when hiding, showed straight
     through during the transition. A solid page background underneath makes the
     bar opaque while keeping the tint. */
  background-color: var(--bg);
  background-image: var(--banner-bg);
  border-bottom: 1px solid var(--border-strong);
  animation: announcementFadeIn 0.3s var(--ease);
}
body.has-banner .navbar {
  top: var(--banner-h, 40px);
}
.announcement-bar-inner {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  min-height: 40px;
  padding: 10px 48px;
  text-align: center;
}
.announcement-text {
  font-weight: 500;
}
.announcement-link {
  color: var(--accent);
  font-weight: 600;
  white-space: nowrap;
  transition: color 0.2s ease;
}
.announcement-link:hover { color: var(--link-hover); }
.announcement-close {
  position: absolute;
  top: 50%;
  right: 12px;
  transform: translateY(-50%);
  width: 20px;
  height: 20px;
  border: none;
  border-radius: 6px;
  background: var(--overlay-soft);
  color: var(--text-2);
  font-size: 0.75rem;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.2s ease, color 0.2s ease;
}
.announcement-close:hover {
  background: var(--overlay-stronger);
  color: var(--text);
}

@keyframes announcementFadeIn {
  from {
    opacity: 0;
    transform: translateY(-4px);
  }
}

/* ---------- Navbar ---------- */
.navbar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 1000;
  height: var(--nav-h);
  background: var(--glass);
  backdrop-filter: blur(22px) saturate(1.4);
  -webkit-backdrop-filter: blur(22px) saturate(1.4);
  border-bottom: 1px solid transparent;
  transition: background 0.25s ease, border-color 0.25s ease;
}
.navbar.scrolled {
  background: var(--glass-strong);
  border-bottom-color: var(--border);
}

.navbar .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 100%;
  gap: 24px;
}

.navbar-logo {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.2rem;
  color: var(--text);
  letter-spacing: -0.3px;
  flex-shrink: 0;
}
.navbar-logo:hover { color: var(--text); }

.nav-logo-img {
  width: 28px;
  height: 28px;
  border-radius: 8px;
}

.nav-links {
  display: flex;
  align-items: center;
  gap: 4px;
  list-style: none;
}

/* ⚠⚠ `a.nav-link` WAS ANCHOR-ONLY, SO THE DROPDOWN TRIGGERS GOT NOTHING.
   A <button> matches `.nav-link` but not `a.nav-link`, so the new Product/Security buttons
   rendered with no padding, no colour and no radius — the items jammed together against the
   logo. The selector is what had to change, not the button's own styling. */
.nav-links .nav-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--text-2);
  padding: 8px 12px;
  border-radius: 8px;
  transition: color 0.2s ease, background 0.2s ease;
}
.nav-links .nav-link:hover,
.nav-links .nav-link.active {
  color: var(--text);
  background: var(--overlay-soft);
}

.nav-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}

.nav-github {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: 10px;
  background: var(--overlay-soft);
  border: 1px solid var(--border);
  color: var(--text-2);
  transition: background 0.2s ease, color 0.2s ease;
}
.nav-github:hover {
  background: var(--overlay-strong);
  color: var(--text);
}

.nav-cta {
  display: inline-flex;
  align-items: center;
  height: 38px;
  padding: 0 16px;
  border-radius: 10px;
  background: var(--accent);
  color: #fff;
  font-size: 0.85rem;
  font-weight: 600;
  transition: background 0.2s ease, transform 0.2s ease;
}
.nav-cta:hover {
  background: var(--accent-hover);
  color: #fff;
  transform: translateY(-1px);
}

/* ---------- Theme switch ---------- */
.theme-switch {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 3px;
  border-radius: 11px;
  background: var(--overlay-softer);
  border: 1px solid var(--border);
}
.theme-switch button {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: none;
  border-radius: 8px;
  background: transparent;
  color: var(--text-3);
  cursor: pointer;
  transition: background 0.2s ease, color 0.2s ease;
}
.theme-switch button:hover {
  background: var(--overlay-soft);
  color: var(--text);
}
.theme-switch button[aria-pressed="true"] {
  background: var(--accent);
  color: #fff;
}
.theme-switch button svg { width: 16px; height: 16px; }

/* Cross-fade every surface while the theme flips, then drop the class so the
   transitions do not linger and cost paint work on scroll/hover. */
html.theme-transition,
html.theme-transition *,
html.theme-transition *::before,
html.theme-transition *::after {
  transition: background-color 0.28s ease, border-color 0.28s ease, color 0.28s ease !important;
}
@media (prefers-reduced-motion: reduce) {
  html.theme-transition,
  html.theme-transition *,
  html.theme-transition *::before,
  html.theme-transition *::after {
    transition: none !important;
  }
}

.hamburger {
  display: none;
  flex-direction: column;
  gap: 5px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 6px;
}
.hamburger span {
  display: block;
  width: 22px;
  height: 2px;
  background: var(--text);
  border-radius: 2px;
  transition: all 0.3s var(--ease);
}
.hamburger.active span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.hamburger.active span:nth-child(2) { opacity: 0; }
.hamburger.active span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 14px 22px;
  border-radius: 12px;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.95rem;
  border: none;
  cursor: pointer;
  transition: background 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}
.btn-primary {
  background: var(--accent);
  color: #fff;
}
.btn-primary:hover {
  background: var(--accent-hover);
  transform: translateY(-2px);
  color: #fff;
}
.btn-secondary {
  background: var(--overlay-soft);
  color: var(--text);
  border: 1px solid var(--border-strong);
}
.btn-secondary:hover {
  background: var(--overlay-strong);
  transform: translateY(-2px);
  color: var(--text);
}

/* ---------- Hero ---------- */
.hero {
  position: relative;
  padding: calc(var(--nav-h) + 72px) 0 40px;
  overflow: hidden;
}

.hero-bg {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse 70% 50% at 50% -10%, rgba(var(--accent-rgb), 0.18) 0%, transparent 60%),
    radial-gradient(ellipse 40% 40% at 85% 30%, rgba(var(--green-rgb), 0.05) 0%, transparent 55%),
    linear-gradient(180deg, transparent 70%, var(--bg) 100%);
}

.hero-grid {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    linear-gradient(var(--grid-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
  background-size: 64px 64px;
  mask-image: radial-gradient(ellipse 80% 60% at 50% 20%, #000 20%, transparent 75%);
  -webkit-mask-image: radial-gradient(ellipse 80% 60% at 50% 20%, #000 20%, transparent 75%);
}

.orb {
  position: absolute;
  border-radius: 50%;
  filter: blur(48px);
  pointer-events: none;
  opacity: 0.22;
}
.orb-1 {
  width: 420px;
  height: 420px;
  background: rgba(var(--accent-rgb), 0.22);
  top: -80px;
  left: 10%;
  animation: orbFloat1 14s ease-in-out infinite alternate;
}
.orb-2 {
  width: 320px;
  height: 320px;
  background: rgba(var(--green-rgb), 0.08);
  top: 180px;
  right: -40px;
  animation: orbFloat2 16s ease-in-out infinite alternate;
}

@keyframes orbFloat1 {
  from { transform: translate(0, 0); }
  to { transform: translate(40px, 30px); }
}
@keyframes orbFloat2 {
  from { transform: translate(0, 0); }
  to { transform: translate(-30px, 20px); }
}

.hero-copy {
  position: relative;
  z-index: 1;
  text-align: center;
  max-width: 760px;
  margin: 0 auto 56px;
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 5px 12px 5px 6px;
  border-radius: 999px;
  background: var(--overlay-softer);
  border: 1px solid var(--border);
  font-size: 0.78rem;
  font-weight: 500;
  color: var(--text-2);
  margin-bottom: 22px;
}
.eyebrow-dot {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 600;
  letter-spacing: 0.02em;
}

.hero-copy h1 {
  font-family: var(--font-display);
  font-weight: 750;
  font-size: clamp(2.5rem, 6vw, 4.15rem);
  line-height: 1.08;
  letter-spacing: -1.4px;
  margin-bottom: 18px;
}
.hero-copy h1 .grad {
  background: var(--heading-grad);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

.hero-copy p {
  font-size: 1.12rem;
  color: var(--text-2);
  max-width: 560px;
  margin: 0 auto 32px;
  line-height: 1.7;
}

.hero-cta-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
  margin-bottom: 22px;
}

.os-badges {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
}

.os-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 11px;
  border-radius: 999px;
  background: var(--overlay-softer);
  border: 1px solid var(--border);
  font-size: 0.75rem;
  color: var(--text-2);
  font-weight: 500;
}

/* Hero product frame */
.hero-visual {
  position: relative;
  z-index: 1;
  max-width: 1040px;
  margin: 0 auto;
  padding: 0 8px;
}

.window {
  position: relative;
  border-radius: 14px;
  overflow: hidden;
  background: var(--frame-bg);
  border: 1px solid var(--border-strong);
  box-shadow:
    var(--shadow-xl);
}

.window-bar {
  display: flex;
  align-items: center;
  height: 38px;
  padding: 0 14px;
  background: var(--frame-bg);
  border-bottom: 1px solid var(--border);
  gap: 8px;
}
.window-dots {
  display: flex;
  gap: 6px;
}
.window-dots span {
  width: 10px;
  height: 10px;
  border-radius: 50%;
}
.window-dots .r { background: #FF5F57; }
.window-dots .y { background: #FEBC2E; }
.window-dots .g { background: #28C840; }
.window-title {
  flex: 1;
  text-align: center;
  font-size: 0.72rem;
  color: var(--text-3);
  font-weight: 500;
  letter-spacing: 0.02em;
}

.window-shot {
  width: 100%;
  height: auto;
  display: block;
}

.phone-float {
  position: absolute;
  right: 2%;
  bottom: -24px;
  width: min(210px, 26vw);
  z-index: 2;
  filter: drop-shadow(0 24px 40px rgba(0, 0, 0, 0.55));
}
.phone-frame {
  border-radius: 28px;
  padding: 7px;
  background: var(--frame-bg-alt);
  border: 1px solid var(--border-strong);
}
.phone-frame img {
  width: 100%;
  height: auto;
  display: block;
  border-radius: 21px;
}

.hero-phone-mobile {
  display: none;
  max-width: 220px;
  margin: 0 auto;
  filter: drop-shadow(0 24px 40px rgba(0, 0, 0, 0.55));
}

/* ---------- Trust strip ---------- */
.trust {
  position: relative;
  overflow: hidden;
  padding: 28px 0 8px;
}
.trust > .container {
  position: relative;
  z-index: 1;
}
.trust-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 12px;
}
.trust-item {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 16px 16px;
  border-radius: 14px;
  border: 1px solid var(--border);
  background: var(--card-bg-soft);
}
.trust-icon {
  width: 36px;
  height: 36px;
  border-radius: 10px;
  background: var(--accent-soft);
  color: var(--accent);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.trust-item strong {
  display: block;
  font-size: 0.9rem;
  font-weight: 600;
  margin-bottom: 2px;
}
.trust-item span {
  font-size: 0.78rem;
  color: var(--text-3);
  line-height: 1.45;
}

/* ---------- Sections ---------- */
.section {
  padding: 104px 0;
}

.section-bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.045;
  z-index: 0;
  pointer-events: none;
  -webkit-mask-image: radial-gradient(ellipse 90% 80% at 50% 40%, #000 30%, transparent 75%);
  mask-image: radial-gradient(ellipse 90% 80% at 50% 40%, #000 30%, transparent 75%);
}

.section-header {
  text-align: center;
  margin-bottom: 56px;
}
.section-kicker {
  display: inline-block;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 10px;
}
.section-header h2 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.9rem, 3.4vw, 2.7rem);
  letter-spacing: -0.6px;
  margin-bottom: 12px;
}
.section-header p {
  font-size: 1.05rem;
  color: var(--text-2);
  max-width: 540px;
  margin: 0 auto;
}

/* ---------- Preview gallery ---------- */
.preview-section {
  padding: 80px 0 104px;
}

.preview-tabs {
  display: flex;
  justify-content: center;
  gap: 6px;
  margin-bottom: 28px;
  padding: 5px;
  width: fit-content;
  margin-left: auto;
  margin-right: auto;
  background: var(--overlay-softer);
  border: 1px solid var(--border);
  border-radius: 12px;
}
.preview-tab {
  appearance: none;
  border: none;
  background: transparent;
  color: var(--text-2);
  font-size: 0.88rem;
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 8px 16px;
  border-radius: 8px;
  cursor: pointer;
  transition: color 0.2s ease, background 0.2s ease;
}
.preview-tab:hover { color: var(--text); }
.preview-tab.active {
  background: var(--surface);
  color: var(--text);
  box-shadow: 0 1px 0 var(--overlay-softer);
}

.preview-panel { display: none; }

/* display:none -> block is not a transitionable change, so the swap used to be
   instant. An animation on .active replays each time the element is displayed,
   which gives a real transition without needing allow-discrete. */
.preview-panel.active {
  display: block;
  animation: panelIn 0.42s var(--ease) both;
}

/* Thumbnails: the active one lifts and the rest recede, so switching reads as a
   change of selection rather than a swap. */
.preview-thumb {
  transition:
    opacity 0.3s var(--ease),
    transform 0.3s var(--ease),
    border-color 0.3s var(--ease);
}
.preview-thumb:not(.active) { opacity: 0.55; }
.preview-thumb:hover { opacity: 1; transform: translateY(-2px); }
.preview-thumb.active { opacity: 1; transform: translateY(-2px); }

/* Tab buttons get the same sweep underline as the nav. */
.preview-tab { position: relative; }
.preview-tab::after {
  content: '';
  position: absolute;
  left: 12px; right: 12px;
  bottom: 4px;
  height: 1.5px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--accent-primary, #3b82f6), var(--accent-secondary, #8b5cf6));
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 0.32s var(--ease);
}
.preview-tab.active::after,
.preview-tab:hover::after { transform: scaleX(1); }

.preview-stage {
  border-radius: 16px;
  overflow: hidden;
  border: 1px solid var(--border-strong);
  background: var(--bg-elevated);
  box-shadow: var(--shadow-sm);
}
.preview-stage img {
  width: 100%;
  height: auto;
  display: block;
}

.preview-thumbs {
  display: flex;
  justify-content: center;
  gap: 10px;
  margin-top: 16px;
  flex-wrap: wrap;
}
.preview-thumb {
  appearance: none;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-2);
  font-size: 0.8rem;
  font-weight: 500;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 8px 14px;
  border-radius: 999px;
  cursor: pointer;
  transition: border-color 0.2s ease, color 0.2s ease, background 0.2s ease;
}
.preview-thumb:hover {
  color: var(--text);
  border-color: var(--border-strong);
}
.preview-thumb.active {
  color: #fff;
  background: var(--accent);
  border-color: var(--accent);
}

/* ---------- Mobile slider (Android tab) ---------- */
.mobile-slider {
  position: relative;
  max-width: 400px;
  margin: 0 auto;
  padding: 0 8px;
  touch-action: pan-y;
  user-select: none;
  -webkit-user-select: none;
}
.mobile-slider-viewport {
  overflow: hidden;
  border-radius: 28px;
}
.mobile-slides {
  display: flex;
  transition: transform 0.45s var(--ease);
}
.mobile-slide {
  flex: 0 0 100%;
  min-width: 100%;
  text-align: center;
}
.mobile-slide .phone-frame {
  margin: 0 auto 14px;
  box-shadow: var(--shadow-md);
}
.mobile-slide img {
  width: 100%;
  height: auto;
}
.mobile-slide figcaption {
  font-size: 0.85rem;
  color: var(--text-2);
  font-weight: 500;
}
.mobile-slider-arrow {
  position: absolute;
  top: 42%;
  transform: translateY(-50%);
  z-index: 3;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  background: var(--glass);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  color: var(--text-2);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}
.mobile-slider-arrow:hover {
  background: var(--surface-2);
  color: var(--text);
  border-color: var(--border-strong);
}
.mobile-slider-arrow.prev { left: -52px; }
.mobile-slider-arrow.next { right: -52px; }
.mobile-dots {
  display: flex;
  justify-content: center;
  gap: 8px;
  margin-top: 18px;
}
.mobile-dot {
  position: relative;
  width: 24px;
  height: 24px;
  border-radius: 999px;
  border: none;
  padding: 0;
  background: transparent;
  cursor: pointer;
}
.mobile-dot::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: 8px;
  height: 8px;
  border-radius: 999px;
  background: var(--text-3);
  transform: translate(-50%, -50%);
  transition: background 0.25s ease, width 0.25s var(--ease);
}
.mobile-dot:hover::after {
  background: var(--text-2);
}
.mobile-dot.active::after {
  background: var(--accent);
  width: 22px;
}

.theme-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 20px;
}
.theme-card {
  flex: 1 1 30%;
  min-width: 290px;
  max-width: 380px;
  border-radius: 16px;
  overflow: hidden;
  border: 1px solid var(--border);
  background: var(--bg-elevated);
  cursor: zoom-in;
  transition: transform 0.25s var(--ease), border-color 0.25s ease, box-shadow 0.25s ease;
}
.theme-card:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.theme-card:hover {
  transform: translateY(-3px);
  border-color: var(--border-strong);
}
.theme-card img {
  width: 100%;
  height: auto;
  display: block;
}
.theme-card figcaption {
  padding: 12px 16px;
  font-size: 0.85rem;
  color: var(--text-2);
  font-weight: 500;
  border-top: 1px solid var(--border);
  text-align: left;
}

/* ---------- Features ---------- */
#features {
  position: relative;
  overflow: hidden;
}
#features > .container {
  position: relative;
  z-index: 1;
}

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

.feature-card {
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 18px;
  padding: 26px 22px;
  transition: transform 0.25s var(--ease), border-color 0.25s ease, box-shadow 0.25s ease, background 0.25s ease;
}
.feature-card:hover {
  transform: translateY(-3px);
  border-color: var(--border-strong);
  background: var(--card-bg-hover);
}

/* The card for whatever shipped in the current release. Accent tokens only, so
   it reads the same in both themes — same convention as .faq-item[open]. */
.feature-badge {
  display: inline-flex;
  align-items: center;
  /* inline-flex drops the whitespace between the label and the version span,
     so the gap has to be explicit or it reads "New inv0.5.3-beta". */
  gap: 4px;
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--accent);
  background: var(--accent-soft);
  border: 1px solid var(--accent-border);
  border-radius: 999px;
  padding: 3px 10px;
  margin-bottom: 14px;
  white-space: nowrap;
}
.feature-card-new {
  border-color: var(--accent-border);
}
.feature-card-new:hover {
  border-color: var(--accent-border-strong);
}

.feature-icon {
  width: 42px;
  height: 42px;
  border-radius: 12px;
  background: var(--accent-soft);
  color: var(--accent);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 16px;
}

.feature-card h3 {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.08rem;
  margin-bottom: 8px;
  letter-spacing: -0.2px;
}
.feature-card p {
  font-size: 0.9rem;
  color: var(--text-2);
  line-height: 1.65;
}

/* ---------- How it works ---------- */
#how-it-works {
  position: relative;
  overflow: hidden;
}
#how-it-works > .container {
  position: relative;
  z-index: 1;
}

.need-callout {
  max-width: 720px;
  margin: 0 auto 48px;
  padding: 18px 24px;
  display: flex;
  align-items: flex-start;
  gap: 22px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--card-bg-soft);
}
.need-title {
  flex-shrink: 0;
  padding-top: 4px;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-3);
}
.need-list {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.need-list li {
  font-size: 0.88rem;
  color: var(--text-2);
  line-height: 1.55;
}

.walkthrough-steps {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
  max-width: 960px;
  margin: 0 auto;
}
/* Steps, not cards.
   This was three bordered cards in a grid — the same shape as the features grid
   above it, which is why the two sections read as one long list of boxes. A
   sequence does not want boxes: the numbered nodes and the line between them ARE
   the structure, and the text sits under each node. */
.walkthrough-step {
  position: relative;
  padding: 0 4px;
  text-align: center;
}
.walkthrough-step h3 {
  margin: 0 0 10px;
  font-size: 16px;
  line-height: 1.35;
  color: var(--text);
  font-weight: 600;
}
.walkthrough-step p {
  margin: 0;
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--text-2);
  max-width: 34ch;
  margin-inline: auto;
}
.walkthrough-step:not(:last-child)::after {
  content: '';
  position: absolute;
  z-index: 1;
  top: 31px;
  left: calc(50% + 40px);
  right: calc(-50% + 40px);
  height: 1px;
  background: linear-gradient(to right, var(--border-strong), var(--border));
  transform-origin: left center;
  pointer-events: none;
}
.walkthrough-num {
  position: relative;
  z-index: 2;
  width: 62px;
  height: 62px;
  margin: 0 auto 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border-strong);
  border-radius: 50%;
  background: var(--bg);
  font-family: var(--font-mono);
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text-2);
}
.walkthrough-step h3 {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.05rem;
  letter-spacing: -0.2px;
  margin-bottom: 8px;
}
.walkthrough-step p {
  font-size: 0.9rem;
  color: var(--text-2);
  line-height: 1.65;
}
.walkthrough-note {
  margin-top: 32px;
  text-align: center;
  font-size: 0.88rem;
  color: var(--text-3);
}

/* ---------- Tech (spec sheet) ---------- */
.tech-section .section-header {
  text-align: left;
  margin-bottom: 32px;
}
.tech-section .section-header p {
  margin-left: 0;
}

.spec-panel {
  max-width: 860px;
  margin: 0 auto;
  background: var(--card-bg-mid);
  border: 1px solid var(--border);
  border-radius: 14px;
  overflow: hidden;
}
.spec-row {
  display: grid;
  grid-template-columns: 180px 1fr;
  gap: 20px;
  align-items: baseline;
  margin: 0;
  padding: 14px 22px;
}
.spec-row + .spec-row {
  border-top: 1px solid var(--border);
}
.spec-row dt {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-3);
}
.spec-row dd {
  font-family: var(--font-mono);
  font-size: 0.9rem;
  color: var(--text-2);
  line-height: 1.5;
}
.spec-caption {
  text-align: center;
  margin-top: 16px;
  font-size: 0.85rem;
  color: var(--text-3);
}

/* ---------- FAQ ---------- */
.faq-section {
  position: relative;
  overflow: hidden;
  border-top: 1px solid var(--border);
  background: radial-gradient(ellipse 60% 40% at 50% 0%, rgba(var(--accent-rgb), 0.05), transparent 70%);
}
.faq-section > .container {
  position: relative;
  z-index: 1;
}

.faq-list {
  max-width: 720px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.faq-item {
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 0 22px;
  transition: border-color 0.25s ease, background 0.25s ease;
}
.faq-item:hover {
  border-color: var(--accent-border);
}
.faq-item[open] {
  border-color: var(--accent-border-strong);
  background: var(--card-bg-hover);
}

.faq-item summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 18px 0;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1rem;
  color: var(--text);
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after {
  content: '';
  width: 8px;
  height: 8px;
  border-right: 2px solid var(--text-2);
  border-bottom: 2px solid var(--text-2);
  transform: rotate(45deg);
  transition: transform 0.25s var(--ease);
  flex-shrink: 0;
  margin-top: -4px;
}
.faq-item[open] summary::after {
  transform: rotate(-135deg);
  margin-top: 4px;
}
.faq-item p {
  padding: 0 0 18px;
  font-size: 0.92rem;
  color: var(--text-2);
  line-height: 1.65;
}

/* ---------- Download ---------- */
.download-section {
  position: relative;
  overflow: hidden;
}
.download-section > .container {
  position: relative;
  z-index: 1;
}
.download-section::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(ellipse 60% 50% at 50% 100%, rgba(var(--accent-rgb), 0.08), transparent 70%);
}

.download-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
}

.download-card {
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 18px;
  padding: 28px 18px 24px;
  text-align: center;
  color: inherit;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  transition: transform 0.25s var(--ease), border-color 0.25s ease, box-shadow 0.25s ease;
}
.download-card:hover {
  transform: translateY(-3px);
  border-color: var(--border-strong);
  color: inherit;
}

.platform-icon {
  width: 48px;
  height: 48px;
  border-radius: 13px;
  background: var(--accent-soft);
  color: var(--accent);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 6px;
}

.download-card h3 {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.12rem;
}
.download-card .version-note {
  font-size: 0.8rem;
  color: var(--text-3);
}

.download-sub {
  text-align: center;
  margin-top: 28px;
}
.download-sub p {
  font-size: 0.88rem;
  color: var(--text-3);
}

.download-mobile-cta {
  display: none;
}

/* ---------- Footer ---------- */
.footer {
  border-top: 1px solid var(--border);
  padding: 56px 0 28px;
}

.footer-grid {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 48px;
  margin-bottom: 40px;
}

.footer-brand { max-width: 300px; }
.footer-brand .navbar-logo { margin-bottom: 12px; }
.footer-brand p {
  font-size: 0.88rem;
  color: var(--text-3);
  line-height: 1.65;
}

.footer-links {
  display: flex;
  gap: 56px;
}
.footer-links-col h4 {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-3);
  margin-bottom: 14px;
}
.footer-links-col ul {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.footer-links-col a {
  font-size: 0.9rem;
  color: var(--text-2);
}
.footer-links-col a:hover { color: var(--text); }

.footer-bottom {
  border-top: 1px solid var(--border);
  padding-top: 22px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 0.82rem;
  color: var(--text-3);
}
.footer-bottom .heart { color: var(--red); }

/* ---------- Back to top ---------- */
.back-to-top {
  position: fixed;
  right: 22px;
  bottom: 22px;
  z-index: 900;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  background: var(--glass-solid);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  color: var(--text);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  opacity: 0;
  transform: translateY(12px);
  pointer-events: none;
  transition: opacity 0.25s ease, transform 0.25s var(--ease), background 0.2s ease, border-color 0.2s ease;
  box-shadow: var(--shadow-md);
}
.back-to-top.show {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}
.back-to-top:hover {
  background: var(--surface-2);
  border-color: var(--border-strong);
  color: var(--text);
}

/* ---------- Theme lightbox ---------- */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.lightbox[hidden] {
  display: none;
}
.lightbox-backdrop {
  position: absolute;
  inset: 0;
  background: var(--scrim);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}
.lightbox-figure {
  position: relative;
  max-width: min(94vw, 1100px);
  max-height: 86vh;
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
}
#lightboxImg {
  display: block;
  max-width: 100%;
  max-height: 72vh;
  width: auto;
  height: auto;
  border-radius: 12px;
  border: 1px solid var(--border-strong);
  box-shadow: var(--shadow-xl);
  background: #000;
}
.lightbox-close {
  position: absolute;
  top: -14px;
  right: -14px;
  z-index: 2;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--glass-solid);
  border: 1px solid var(--border-strong);
  color: var(--text);
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--shadow-md);
}
.lightbox-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--glass-solid);
  border: 1px solid var(--border-strong);
  color: var(--text);
  cursor: pointer;
  font-size: 1.4rem;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--shadow-md);
}
.lightbox-nav.prev { left: -60px; }
.lightbox-nav.next { right: -60px; }
.lightbox-close:hover,
.lightbox-nav:hover {
  background: var(--surface-2);
  border-color: var(--border-strong);
}
#lightboxCaption {
  text-align: center;
  padding-top: 14px;
  color: var(--text-2);
  font-size: 0.9rem;
}

@media (prefers-reduced-motion: no-preference) {
  .lightbox-figure {
    animation: lightboxIn 0.22s var(--ease);
  }
  @keyframes lightboxIn {
    from {
      opacity: 0;
      transform: scale(0.96);
    }
  }
}

/* ---------- Responsive ---------- */
@media (max-width: 960px) {
  .trust-grid { grid-template-columns: repeat(2, 1fr); }
  .features-grid { grid-template-columns: repeat(2, 1fr); }
  .download-grid { grid-template-columns: repeat(2, 1fr); }
  .mobile-slider-arrow.prev { left: -8px; }
  .mobile-slider-arrow.next { right: -8px; }
  .phone-float { display: none; }
  .walkthrough-steps {
    grid-template-columns: 1fr;
    gap: 20px;
    max-width: 640px;
  }
  .walkthrough-step:not(:last-child)::after {
    top: 80px;
    bottom: -44px;
    left: 49px;
    right: auto;
    width: 1px;
    height: auto;
  }
  .walkthrough-num {
    margin: 0 0 16px;
  }
}

@media (max-width: 720px) {
  :root { --nav-h: 62px; }

  .nav-links,
  .nav-actions { display: none; }

  .nav-links.open {
    display: flex;
    /* absolute, not fixed.
       The navbar carries backdrop-filter, which makes it the containing block for
       fixed-position descendants. `position: fixed; top: var(--nav-h)` was therefore
       measured from the navbar instead of the viewport and the panel landed ~354px
       down the page, overlapping the hero. Absolute positioning against the fixed
       navbar is unambiguous, and `top: 100%` keeps it directly beneath the bar
       whether or not the announcement banner is present. */
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: stretch;
    /* Opaque, not translucent. At 0.88 alpha the large hero headline still bled
       through enough to read behind the links. */
    background: var(--bg-elevated, #141416);
    border-bottom: 1px solid var(--border);
    box-shadow: 0 18px 40px rgba(0, 0, 0, 0.45);
    padding: 12px 20px 20px;
    gap: 4px;
    z-index: 1002;
  }
  .nav-links.open .nav-link { padding: 12px 10px; }
  .nav-links.open .nav-mobile-extra {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-top: 10px;
  }
  /* The switch takes its own row so the three states stay a comfortable tap
     target, and the two buttons share the row below. */
  /* The switch spanned the full row but its three buttons were fixed at 46px and
     centred, leaving dead space either side and a solid blue block floating in the
     middle — it read as three loose icons rather than one control. Equal-width
     segments fill the row and make the selection obvious. */
  .nav-links.open .theme-switch {
    width: 100%;
    justify-content: stretch;
    padding: 4px;
  }
  .nav-links.open .theme-switch button {
    flex: 1 1 0;
    width: auto;
    min-width: 0;
    height: 38px;
  }
  .nav-links.open .nav-mobile-extra .btn { flex: 1; justify-content: center; }

  .hamburger { display: flex; }

  .hero { padding: calc(var(--nav-h) + 40px) 0 24px; }
  .hero-copy { margin-bottom: 36px; }
  .hero-copy p { font-size: 1rem; }
  .section { padding: 72px 0; }
  .preview-section { padding: 56px 0 72px; }
  .mobile-slider { max-width: 320px; }

  .hero-phone-mobile { display: block; }
  .hero-visual .window { display: none; }

  .trust-grid { grid-template-columns: 1fr; }

  .features-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 12px;
  }
  .feature-card { padding: 16px 14px; }

  .download-grid { display: none; }
  .download-mobile-cta {
    display: inline-flex;
    margin-top: 8px;
  }
  .download-section .container { text-align: center; }

  .spec-row {
    grid-template-columns: 1fr;
    gap: 3px;
    padding: 13px 18px;
  }

  .need-callout {
    flex-direction: column;
    gap: 10px;
  }

  .theme-card { flex: 1 1 100%; min-width: 0; max-width: none; }

  #lightboxImg { max-height: 62vh; }
  .lightbox-nav.prev { left: -14px; }
  .lightbox-nav.next { right: -14px; }
  .lightbox-close { top: -10px; right: -10px; }

  .footer-grid,
  .footer-bottom {
    flex-direction: column;
    gap: 28px;
  }
  .footer-bottom { text-align: center; gap: 8px; }
  .footer-links { flex-direction: column; gap: 24px; }
}

@media (max-width: 479px) {
  .features-grid {
    grid-template-columns: 1fr;
    gap: 14px;
  }
  .feature-card { padding: 20px 18px; }
}

@media (min-width: 721px) {
  .nav-mobile-extra { display: none; }
}

/* ------------------------------------------------------------------
   Security & Trust
   The one section that must be readable rather than impressive: a
   two-column grid of plain cards, with the gaps card marked so nobody
   mistakes it for marketing. Reuses the site's card tokens, so it
   follows the theme toggle like everything else.
   ------------------------------------------------------------------ */
.security-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 18px;
  margin-top: 34px;
}
@media (max-width: 860px) {
  /* minmax(0, 1fr), not 1fr: a grid item defaults to min-width:auto, so the
     long fingerprint and code lines pushed the column wider than the phone. */
  .security-grid { grid-template-columns: minmax(0, 1fr); }
}
.security-card {
  /* and the same guard on the card itself, for the two-column case */
  min-width: 0;
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 24px 24px 20px;
  box-sizing: border-box;
}
.security-card h3 {
  display: flex;
  align-items: center;
  gap: 9px;
  margin: 0 0 14px;
  font-size: 16px;
  font-weight: 650;
  color: var(--text);
}
.security-card p {
  margin: 0 0 10px;
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--text-2);
}
.security-card ul {
  margin: 0;
  padding: 0 0 0 18px;
  display: flex;
  flex-direction: column;
  gap: 9px;
}
.security-card li {
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--text-2);
}
.security-card strong { color: var(--text); font-weight: 620; }
.security-card code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12.5px;
  color: var(--text);
}
/* The gaps card is deliberately not styled as a warning: it is a
   statement of fact, and alarm styling would read as an admission. */
.security-card--gaps {
  background: var(--card-bg-soft);
  border-style: dashed;
}
.security-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}
.security-dot--on { background: var(--green); }
.security-dot--off { background: var(--text-3); }

.security-fingerprint {
  display: block;
  margin: 4px 0 14px;
  padding: 10px 12px;
  border-radius: 10px;
  background: var(--card-bg-strong);
  border: 1px solid var(--border);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12.5px;
  letter-spacing: 0.02em;
  color: var(--text);
  overflow-wrap: anywhere;
}
.security-code {
  margin: 0 0 12px;
  padding: 12px 14px;
  border-radius: 10px;
  background: var(--card-bg-strong);
  border: 1px solid var(--border);
  overflow-x: auto;
}
.security-code code {
  font-size: 12px;
  line-height: 1.7;
  color: var(--text-2);
  white-space: pre;
}
.security-note {
  font-size: 12.5px !important;
  color: var(--text-3) !important;
}
.security-card a { color: var(--accent); }

/* ------------------------------------------------------------------
   Features, rebuilt.

   It used to be fourteen identical cards in a three-column grid: an icon, a bold
   title, two lines of body — the shape every template ships, and the reason the
   page read as generated rather than designed. The features that actually define
   Orbit now get a row each, with room to say something; everything else is one
   dense list, so it is still all there without a wall of equal-weight boxes.
   ------------------------------------------------------------------ */
.pillars {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 44px;
}
.pillar {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  gap: 48px;
  align-items: center;
  padding: 40px 0;
  border-top: 1px solid var(--border);
}
.pillar:last-child { border-bottom: 1px solid var(--border); }
/* Alternating sides: the eye gets a rhythm instead of a column of identical rows. */
.pillar--flip .pillar-text { order: 2; }
.pillar--flip .pillar-visual { order: 1; }

.pillar-index {
  display: block;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  letter-spacing: 0.12em;
  color: var(--text-3);
  margin-bottom: 14px;
}
.pillar h3 {
  margin: 0 0 14px;
  font-size: clamp(21px, 2.3vw, 28px);
  line-height: 1.22;
  letter-spacing: -0.015em;
  color: var(--text);
  font-weight: 600;
}
.pillar p {
  margin: 0 0 18px;
  font-size: 15px;
  line-height: 1.65;
  color: var(--text-2);
  max-width: 46ch;
}
.pillar-points {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.pillar-points li {
  position: relative;
  padding-left: 20px;
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--text-2);
}
.pillar-points li::before {
  content: '';
  position: absolute;
  left: 2px;
  top: 8px;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--accent);
  opacity: 0.8;
}

/* A spec block rather than a picture: honest, and it reads as documentation
   instead of decoration. */
.spec {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 10px 18px;
  padding: 22px 24px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--card-bg-soft);
}
.spec-label {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11.5px;
  letter-spacing: 0.04em;
  color: var(--text-3);
  white-space: nowrap;
}
.spec-value {
  font-size: 13px;
  line-height: 1.5;
  color: var(--text-2);
}

/* ---- the one animation, and it explains the product ---- */
.pillar-visual { display: flex; justify-content: center; }
.p2p { width: 100%; max-width: 420px; height: auto; }
/* Give the diagram the room its column has: it is the one place on the page that
   shows how Orbit works rather than describing it. */
@media (min-width: 901px) { .pillar-visual .p2p { max-width: 440px; } }

.p2p-dot {
  animation: p2p-travel 4.6s cubic-bezier(0.45, 0, 0.55, 1) infinite;
}
.p2p-route {
  animation: p2p-route-pulse 4.6s ease-in-out infinite;
}
.p2p-arrival {
  animation: p2p-arrive 4.6s ease-out infinite;
}
.p2p-absent {
  animation: p2p-absent 4.6s ease-in-out infinite;
}

/* The dot leaves the laptop, crosses, and lands on the phone — the whole idea of
   the product in one gesture. Nothing is animated that does not say something. */
@keyframes p2p-travel {
  0%, 8%   { transform: translateX(0);      opacity: 0; }
  14%      { opacity: 1; }
  46%      { transform: translateX(184px);  opacity: 1; }
  52%      { opacity: 0; }
  100%     { transform: translateX(184px);  opacity: 0; }
}
@keyframes p2p-arrive {
  0%, 46%  { opacity: 0; r: 4; }
  54%      { opacity: 0.9; r: 9; }
  70%      { opacity: 0; r: 4; }
  100%     { opacity: 0; r: 4; }
}
@keyframes p2p-route-pulse {
  0%, 10%  { opacity: 0.25; }
  30%      { opacity: 1; }
  60%      { opacity: 0.25; }
  100%     { opacity: 0.25; }
}
@keyframes p2p-absent {
  0%, 100% { opacity: 0.75; }
  50%      { opacity: 1; }
}

/* ---- everything else, dense and scannable ---- */
.feature-rest { margin-top: 44px; }
.feature-rest h3 {
  margin: 0 0 18px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-3);
}
.feature-rest-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px 32px;
}
.feature-rest-list li {
  position: relative;
  padding-left: 18px;
  font-size: 14px;
  line-height: 1.5;
  color: var(--text-2);
}
.feature-rest-list li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 8px;
  width: 6px;
  height: 6px;
  border-radius: 2px;
  background: var(--text-3);
  opacity: 0.5;
}

@media (max-width: 900px) {
  .pillar { grid-template-columns: minmax(0, 1fr); gap: 28px; padding: 32px 0; }
  /* On one column the visual should follow its text, never precede it. */
  .pillar--flip .pillar-text { order: 1; }
  .pillar--flip .pillar-visual { order: 2; }
  .feature-rest-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
  .feature-rest-list { grid-template-columns: minmax(0, 1fr); }
}

/* The animation is decoration with a job, and it stops when movement is unwelcome. */
@media (prefers-reduced-motion: reduce) {
  .p2p-dot, .p2p-route, .p2p-arrival, .p2p-absent { animation: none; }
  .p2p-dot { opacity: 1; transform: translateX(92px); }
  .p2p-arrival { opacity: 0.5; }
}

/* ------------------------------------------------------------------
   How it works — the line draws itself as the sequence arrives.
   One animation, and it says the only thing this section has to say: these happen
   in order. Where scroll-driven animation is unavailable the line is simply there,
   which is a perfectly good static state.
   ------------------------------------------------------------------ */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .walkthrough-step:not(:last-child)::after {
      animation: walk-line linear both;
      animation-timeline: view();
      animation-range: entry 15% entry 75%;
    }
    .walkthrough-num {
      animation: walk-node linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 45%;
    }
  }
}
@keyframes walk-line {
  from { transform: scaleX(0); opacity: 0; }
  to   { transform: scaleX(1); opacity: 1; }
}
@keyframes walk-node {
  from { border-color: var(--border); color: var(--text-3); }
  to   { border-color: var(--accent); color: var(--accent); }
}

/* Stacked: the sequence runs downwards, so the connector has to as well. The
   horizontal line would otherwise point at nothing. */
@media (max-width: 860px) {
  .walkthrough-steps {
    grid-template-columns: minmax(0, 1fr);
    gap: 28px;
    max-width: 520px;
  }
  .walkthrough-step {
    text-align: left;
    padding-left: 76px;
  }
  .walkthrough-step h3 { margin-top: 6px; }
  .walkthrough-step p { margin-inline: 0; max-width: none; }
  .walkthrough-num {
    position: absolute;
    left: 0;
    top: 0;
    margin: 0;
  }
  .walkthrough-step:not(:last-child)::after {
    top: 62px;
    bottom: -28px;
    left: 31px;
    right: auto;
    width: 1px;
    height: auto;
    background: linear-gradient(to bottom, var(--border-strong), var(--border));
    transform-origin: top center;
  }
  @keyframes walk-line {
    from { transform: scaleY(0); opacity: 0; }
    to   { transform: scaleY(1); opacity: 1; }
  }
}

/* ------------------------------------------------------------------
   FAQ, grouped.

   Seven identical rows said nothing about which question matters — the same
   uniformity as the old feature grid, one section further down. Three labelled
   groups give it a shape you can scan, and the first answer in each group is open
   so the section arrives with substance rather than seven closed bars.
   ------------------------------------------------------------------ */
.faq-groups {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 40px 48px;
  margin-top: 40px;
}
.faq-group h3 {
  margin: 0 0 14px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-3);
}
.faq-group .faq-list { display: flex; flex-direction: column; gap: 10px; }
.faq-more {
  margin: 44px 0 0;
  padding-top: 22px;
  border-top: 1px solid var(--border);
  font-size: 13.5px;
  line-height: 1.65;
  color: var(--text-2);
  max-width: 70ch;
}
.faq-more a { color: var(--accent); }

@media (max-width: 900px) {
  .faq-groups { grid-template-columns: minmax(0, 1fr); gap: 32px; }
}





/* ── The hero's orbit ring ──────────────────────────────────────────────────
   A ring that passes behind the app window, so the device reads as something
   sitting in orbit rather than a screenshot dropped on a background.

   ⚠ IT IS NOT CLIPPED AND DOES NOT NEED TO BE. The window is opaque and sits on
   top, so it hides the crossing point by itself — the same trick as the sphere in
   the statement band, solved by stacking order instead of a clip path. */
.hero-orbit {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  pointer-events: none;
  z-index: 0;
}

.hero-orbit svg {
  width: min(1220px, 116vw);
  height: auto;
  overflow: visible;
}

.hero-orbit .ho-ring {
  fill: none;
  stroke: var(--accent);
  stroke-opacity: 0.26;
  stroke-width: 1.3;
}

.hero-orbit .ho-node {
  fill: var(--accent);
  filter: drop-shadow(0 0 10px rgba(var(--accent-rgb), 0.85));
}

.hero-orbit .ho-drift {
  transform-origin: 600px 300px;
  animation: orbitDrift 44s linear infinite;
}

/* The window must sit above the ring for the occlusion to happen at all. */
.hero-visual .window { z-index: 1; }

@media (prefers-reduced-motion: reduce) {
  .hero-orbit .ho-drift { animation: none; }
}

/* ── The planet behind the headline ─────────────────────────────────────────
   ⚠ IT IS DIM ON PURPOSE AND THAT IS THE WHOLE DESIGN CONSTRAINT. The headline is
   the most important thing on the page; a bright sphere behind white type costs
   legibility, so the object is a backdrop at ~1/3 opacity — present enough to give
   the hero depth, quiet enough that nobody has to squint through it. */
/* The copy must sit above it, or the object draws over the text. */
.hero .container { position: relative; z-index: 2; }

/* ── Rendered objects ───────────────────────────────────────────────────────
   ⚠ THESE ARE IMAGES, NOT SVG, AND THAT IS DELIBERATE. The specular highlights and
   the glass rim are what make them read as physical; an SVG gradient approximates the
   shading but never the light. Both are transparent PNGs so they sit on any section
   without a matte, and both carry a drop-shadow in the accent colour so they feel lit
   by the same light as the page. */
.orbit-object {
  display: block;
  width: min(380px, 70vw);
  height: auto;
  margin: 0 auto 26px;
  filter: drop-shadow(0 26px 60px rgba(var(--accent-rgb), 0.26));
}




/* ── The hero's people ──────────────────────────────────────────────────────
   ⚠ FOUR FACES, TWO PER SIDE, AND THE SIZES DO THE WORK. One face per side read as a
   portrait; a pair reads as a room. The back one is smaller and dimmer, so the two make
   DEPTH instead of a lineup — equal sizes would look like a grid of stock photos.
   ⚠ EVERY EDGE IS MASKED. The photos are rectangles on near-black; a radial mask fades
   them into the page and, where the two overlap, lets their edges dissolve into each
   other instead of cutting a hard line.
   ⚠ Low opacity throughout: the headline is the subject, these are atmosphere. */
.hero-people {
  position: absolute;
  top: 132px;
  width: min(340px, 28vw);
  height: 390px;
  pointer-events: none;
  z-index: 0;
}

/* ⚠⚠ THE GAP IS TIED TO THE HEADLINE, NOT TO THE WINDOW EDGE. Anchored with `left: -40px`
   the photos sat 29px from the copy at 1440 and 280px from it at 1920, because the headline
   is centred and fixed-width while the margin is not. These expressions place the container
   a constant distance from the headline's own edge at any viewport: 380px is half the h1,
   32px is the gap, 340px is the container.
   ⚠ 32px, not 0 — Dan's "not too close". They should sit beside the text, not touch it. */
.hero-people--left { left: calc(50% - 752px); }
.hero-people--right { right: calc(50% - 752px); }

.hero-person {
  position: absolute;
  height: auto;
  pointer-events: none;
  -webkit-mask-image: radial-gradient(closest-side, #000 45%, transparent 100%);
  mask-image: radial-gradient(closest-side, #000 45%, transparent 100%);
}

/* Near: bigger, lower, in front. */
.hero-person--near {
  width: min(270px, 22vw);
  bottom: 0;
  opacity: 0.55;
  z-index: 2;
}
.hero-people--left .hero-person--near { left: 0; }
.hero-people--right .hero-person--near { right: 0; }

/* Far: smaller, higher, dimmer — it reads as somebody further across the room. */
.hero-person--far {
  width: min(195px, 16vw);
  top: 0;
  opacity: 0.3;
  z-index: 1;
}
.hero-people--left .hero-person--far { right: 0; }
.hero-people--right .hero-person--far { left: 0; }

/* ⚠ Gone under 1000px: the copy is centred, and two photos a side would sit under it. */
@media (max-width: 1000px) {
  .hero-people { display: none; }
}

/* ── The shield emblem in the statement band ────────────────────────────────
   ⚠ IT REPLACES A LINE ICON, SO IT MUST BEHAVE LIKE ONE: small, centred, no shadow box.
   The render is a 3D object, which is why it can carry a little glow where a flat icon
   could not — but it stays emblem-sized or it becomes a second subject. */
.statement-shield {
  display: block;
  width: 58px;
  height: auto;
  margin: 0 auto 18px;
  filter: drop-shadow(0 10px 24px rgba(var(--accent-rgb), 0.34));
}

/* ── The planet behind the hero copy ────────────────────────────────────────
   ⚠⚠ VERY LARGE IS THE POINT. At a normal size it would be an object sitting behind
   text, which reads as a mistake; at 150vw the sphere fills the hero and the ring leaves
   the frame, so it becomes the room the headline sits in. The section's `overflow: hidden`
   does the cropping — no clip path needed.
   ⚠ Low opacity because it is behind 15px body text as well as the headline: at .28 the
   paragraph stays legible, at .5 it does not. */
.hero-planet {
  position: absolute;
  left: 50%;
  /* ⚠ CENTRED ON THE TEXT, NOT ON THE SECTION. `top: 50%` would centre it on the whole
     hero including the app window below, which puts it under the paragraph. The copy block
     starts after the nav padding and runs to the OS badges, so its centre is a fixed
     offset from the top of the section. */
  top: 380px;
  transform: translate(-50%, -50%);
  width: min(760px, 74vw);
  height: auto;
  pointer-events: none;
  z-index: 0;
  opacity: 0.2;
  filter: drop-shadow(0 40px 120px rgba(var(--accent-rgb), 0.18));
}

/* ── Life in the hero ───────────────────────────────────────────────────────
   ⚠⚠ THE PLANET'S KEYFRAMES MUST CARRY `translateX(-50%)`. It is centred with that
   transform, so an animation that only says `translateY(...)` replaces the whole property
   and the planet jumps half its width to the right on the first frame. Any element centred
   by transform has to repeat the centring inside every keyframe.
   ⚠ The people are positioned with left/right and bottom/top, not a transform, so theirs
   is a plain translateY and cannot have that problem.
   ⚠ Different durations on purpose: if every photo drifted on the same clock the group
   would pulse together and read as a bug rather than as breathing. */
@keyframes planetDrift {
  0%, 100% { transform: translate(-50%, -50%) translateY(0) scale(1); }
  50%      { transform: translate(-50%, -50%) translateY(-26px) scale(1.02); }
}

/* ⚠⚠ 13px OVER 11s IS ABOUT ONE PIXEL A SECOND, WHICH IS INVISIBLE. The first version
   was technically animating and looked completely static — Dan tried it and nothing
   happened. Motion has to clear the threshold where a person can see it: ~26px over 7–8s
   is gentle and unmistakable. Slower is not subtler, it is absent. */
@keyframes personFloat {
  from { transform: translateY(0); }
  to   { transform: translateY(-26px); }
}

.hero-planet {
  animation: planetDrift 12s ease-in-out infinite;
}

.hero-person {
  animation: personFloat 7s ease-in-out infinite alternate;
}

/* Out of step with each other, and slower the further back they are — the far figure
   moving less is what sells the depth the sizes set up. */
.hero-person--near { animation-duration: 8s; }
.hero-person--far  { animation-duration: 11s; }
.hero-people--right .hero-person--near { animation-delay: -4.5s; }
.hero-people--right .hero-person--far  { animation-delay: -7s; }
.hero-people--left  .hero-person--far  { animation-delay: -2.5s; }

@media (prefers-reduced-motion: reduce) {
  .hero-planet,
  .hero-person { animation: none; }
}

/* ── Arriving on the page ───────────────────────────────────────────────────
   ⚠⚠ THE ENTRANCE GOES ON THE CONTAINER, THE DRIFT STAYS ON THE IMAGES. Both want to
   animate `transform`, and two animations on one element do not compose — the later one
   simply wins and the other never runs. Splitting them across the wrapper and its children
   is what lets a photo slide in from the edge AND keep floating afterwards.
   ⚠ The planet has no wrapper, so its entrance animates `opacity` ONLY (0 → its resting
   0.2, not 1) while the drift owns `transform`. ⚠ Ending that keyframe at `1` would pop it
   to full strength the moment it arrives. */
@keyframes peopleInLeft {
  from { opacity: 0; transform: translateX(-46px); }
  to   { opacity: 1; transform: translateX(0); }
}

@keyframes peopleInRight {
  from { opacity: 0; transform: translateX(46px); }
  to   { opacity: 1; transform: translateX(0); }
}

@keyframes planetIn {
  from { opacity: 0; }
  to   { opacity: 0.2; }
}

.hero-people--left {
  animation: peopleInLeft 1.1s cubic-bezier(0.22, 0.61, 0.36, 1) 0.1s both;
}

.hero-people--right {
  animation: peopleInRight 1.1s cubic-bezier(0.22, 0.61, 0.36, 1) 0.28s both;
}

.hero-planet {
  animation: planetDrift 12s ease-in-out infinite, planetIn 1.8s ease-out 0.15s both;
}

@media (prefers-reduced-motion: reduce) {
  .hero-people--left,
  .hero-people--right,
  .hero-planet { animation: none; }
}

/* ── Grouped nav ────────────────────────────────────────────────────────────
   ⚠⚠ CSS-ONLY DROPDOWNS, WITH `:focus-within` AS WELL AS `:hover`. A hover-only menu is
   unusable by keyboard and invisible to anyone on a touch screen — the trigger is a real
   `<button>`, so focusing it opens the panel and Tab walks the links inside.
   ⚠ `visibility` (not just opacity) is what removes the closed menu from the tab order and
   from screen readers; opacity alone leaves four invisible links focusable. */
.nav-group { position: relative; }

.nav-trigger {
  background: none;
  border: 0;
  font: inherit;
  cursor: pointer;
  gap: 4px;
}

.nav-caret {
  width: 13px;
  height: 13px;
  opacity: 0.7;
  transition: transform 0.25s var(--ease);
}

.nav-group:hover .nav-caret,
.nav-group:focus-within .nav-caret { transform: rotate(180deg); }

.nav-menu {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  min-width: 208px;
  padding: 8px;
  margin: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 2px;
  border-radius: 14px;
  background: var(--bg-elevated);
  border: 1px solid var(--border-subtle);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.32);
  z-index: 60;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity 0.2s var(--ease), transform 0.2s var(--ease), visibility 0.2s;
}

.nav-group:hover .nav-menu,
.nav-group:focus-within .nav-menu {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.nav-menu a.nav-link {
  width: 100%;
  min-height: 40px;
  font-size: 0.875rem;
}

/* ⚠ No sweep underline inside a panel — that treatment belongs to a row of links. */
.nav-menu .nav-link::after { display: none; }

/* ⚠ In the mobile drawer the panel must be inline and always open: there is no hover on a
   phone, and a collapsed submenu inside a drawer hides half the site. */
@media (max-width: 720px) {
  .nav-menu {
    position: static;
    opacity: 1;
    visibility: visible;
    transform: none;
    min-width: 0;
    padding: 0 0 6px 10px;
    border: 0;
    box-shadow: none;
    background: transparent;
  }
  .nav-caret { display: none; }
  .nav-trigger { pointer-events: none; }
}

/* ── Centred nav links ──────────────────────────────────────────────────────
   ⚠⚠ ABSOLUTELY POSITIONED, NOT A FLEX TRICK. The logo and the action cluster have
   different widths, so any `justify-content` arrangement centres the links in the space
   LEFT OVER rather than in the bar — which looks off by half the difference and is exactly
   what "almost centred" means. Taking it out of flow and centring on the container's own
   midpoint is the only version that is actually centred.
   ⚠ Scoped above the drawer breakpoint: `.nav-links.open` is absolutely positioned in the
   mobile drawer with its own top/left, and a `left: 50%` leaking into it would push the
   whole drawer half a screen to the right. */
@media (min-width: 721px) {
  .navbar .container { position: relative; }

  .nav-links {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
  }
}

/* ── The announcement bar on a phone ────────────────────────────────────────
   ⚠⚠ THE FULL RELEASE NOTE WAS TAKING HALF THE SCREEN. At 390px the same sentence that fits
   on one line on a desktop wrapped into ~20 lines and pushed the navbar, the headline and
   everything else below the fold. The bar is a heads-up, not the changelog — two lines and
   the "What's new" link is the whole job. */
@media (max-width: 720px) {
  .announcement-bar-inner {
    padding: 9px 42px;
    text-align: left;
  }

  .announcement-text {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
}
