/* ==========================================================================
   Slipreel landing page — design system
   --------------------------------------------------------------------------
   Dark only. No build step: this file is served exactly as committed.
   Every motion effect is gated behind prefers-reduced-motion, and nothing is
   hidden unless <html> carries the `js` class (see section 9).

     1. Tokens
     2. Derived tokens
     3. Font
     4. Reset
     5. Typography
     6. Layout
     7. Components
     8. Background layers
     9. Motion
    10. Navigation
    11. Hero
    12. Feature theater
    13. Hero cursor trail
    14. Deep dives
    15. Feature grid
    16. Platform strip
    17. Pricing
    18. FAQ
    19. Final CTA
    20. Footer
    21. Accessibility utilities
   ========================================================================== */


/* 1. Tokens ============================================================== */

:root {
  --bg: #08080C;
  --bg-elev: #0E0E15;
  --bg-card: #12121C;
  --ink: #F5F5FA;
  --ink-dim: #A3A3B8;
  --ink-faint: #6E6E85;
  --accent: #6C5CE7;
  --accent-deep: #4A3FC7;
  --accent-glow: rgba(108, 92, 231, 0.45);
  --line: rgba(255, 255, 255, 0.08);
  --line-strong: rgba(255, 255, 255, 0.14);
  --radius: 14px;
  --radius-lg: 22px;
  --maxw: 1200px;
  --pad: clamp(20px, 5vw, 48px);
}


/* 2. Derived tokens ======================================================
   Everything below is expressed in terms of section 1 where CSS allows it.
   The three --aurora-* values are alpha variants of --accent / --accent-deep;
   a custom property holding a hex cannot be given an alpha channel without
   color-mix(), so they are spelled out here rather than outside :root. Keep
   their RGB triplets in sync with --accent and --accent-deep.
   ------------------------------------------------------------------------ */

:root {
  /* Type */
  --font-sans: "Inter Variable", -apple-system, BlinkMacSystemFont, "Segoe UI",
    Roboto, "Helvetica Neue", Arial, sans-serif;

  --fs-xs: clamp(0.75rem, 0.72rem + 0.15vw, 0.8125rem);
  --fs-sm: clamp(0.875rem, 0.85rem + 0.12vw, 0.9375rem);
  --fs-base: clamp(1rem, 0.96rem + 0.2vw, 1.0625rem);
  --fs-md: clamp(1.0625rem, 1rem + 0.3vw, 1.1875rem);
  --fs-lg: clamp(1.25rem, 1.13rem + 0.6vw, 1.5rem);
  --fs-xl: clamp(1.5rem, 1.3rem + 1vw, 2rem);
  --fs-2xl: clamp(1.875rem, 1.55rem + 1.6vw, 2.75rem);
  --fs-3xl: clamp(2.25rem, 1.7rem + 2.7vw, 3.75rem);
  --fs-display: clamp(2.375rem, 1.6rem + 3.9vw, 5rem);

  --lh-tight: 1.06;
  --lh-snug: 1.18;
  --lh-base: 1.6;

  /* Rhythm */
  --section-y: clamp(64px, 9vw, 128px);
  --nav-h: 64px;

  /* Motion */
  --ease-out: cubic-bezier(0.22, 0.61, 0.36, 1);
  --dur-fast: 160ms;
  --dur-mid: 280ms;
  --dur-slow: 620ms;

  /* Ink that sits on top of --accent. Pure white clears WCAG AA (4.86:1)
     against --accent; --ink does not (4.47:1). */
  --on-accent: #FFFFFF;

  /* Aurora blooms — see the note at the top of this section. */
  --aurora-1: rgba(108, 92, 231, 0.30);
  --aurora-2: rgba(74, 63, 199, 0.28);
  --aurora-3: rgba(108, 92, 231, 0.14);

  --shadow-accent: 0 12px 34px -14px var(--accent-glow);
}

/* Measured contrast against --bg (#08080C), and against the brightest point
   of the aurora composite (#26214E, top centre of the viewport):

                  on --bg   over aurora
     --ink         18.40      13.69   AAA — body copy and headings
     --ink-dim      8.08       6.01   AA at any size, AAA on --bg
     --ink-faint    4.03       3.00   large text only (>=24px, or >=18.7px
                                      bold); never for copy a reader needs
     --accent       4.12       3.07   clears the 3:1 UI floor for focus rings
                                      and borders everywhere on the page */


/* 3. Font ================================================================
   Inter Variable, latin subset, self-hosted. No third-party request.
   Licence: SIL Open Font License 1.1 (assets/fonts/OFL.txt).
   ------------------------------------------------------------------------ */

@font-face {
  font-family: "Inter Variable";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("../fonts/inter-variable.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;
}


/* 4. Reset =============================================================== */

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

* {
  margin: 0;
}

html {
  color-scheme: dark;
  -webkit-text-size-adjust: 100%;
  /* Anchors must clear the sticky nav. */
  scroll-padding-top: calc(var(--nav-h) + 16px);
}

body {
  min-height: 100vh;
  background-color: var(--bg);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  line-height: var(--lh-base);
  letter-spacing: -0.011em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* `clip`, not `hidden`: hidden would silently break position: sticky. */
  overflow-x: clip;
}

img,
picture,
svg,
video,
canvas {
  display: block;
  max-width: 100%;
  height: auto;
}

input,
button,
textarea,
select {
  font: inherit;
  color: inherit;
}

button {
  background: none;
  border: 0;
}

ul[class],
ol[class] {
  list-style: none;
  padding: 0;
}

/* The aurora sits at z-index 0, which paints above unpositioned in-flow
   content. Every other direct child of <body> is therefore lifted above it.
   Wrapped in :where() so it carries zero specificity: a later section can
   still say `position: fixed` on a body child and win, and it keeps z-index 1
   unless it deliberately sets its own. Nothing may reintroduce z-index: 0. */
:where(body > :not(.aurora, .grain)) {
  position: relative;
  z-index: 1;
}

::selection {
  background-color: var(--accent-glow);
  color: var(--ink);
}

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


/* 5. Typography ========================================================== */

h1,
h2,
h3,
h4 {
  font-weight: 620;
  line-height: var(--lh-snug);
  letter-spacing: -0.022em;
  text-wrap: balance;
}

h1 {
  font-size: var(--fs-display);
  line-height: var(--lh-tight);
  letter-spacing: -0.035em;
}

h2 {
  font-size: var(--fs-3xl);
  letter-spacing: -0.03em;
}

h3 {
  font-size: var(--fs-xl);
}

h4 {
  font-size: var(--fs-lg);
}

p {
  text-wrap: pretty;
}

a {
  color: inherit;
  text-decoration-color: var(--line-strong);
  text-underline-offset: 0.22em;
  transition: color var(--dur-fast) var(--ease-out),
    text-decoration-color var(--dur-fast) var(--ease-out);
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  margin: 0;
  color: var(--ink-dim);
  font-size: var(--fs-xs);
  font-weight: 580;
  line-height: 1;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}


/* 6. Layout ============================================================== */

.container {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--pad);
}

.section {
  padding-block: var(--section-y);
}


/* 7. Components ========================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.55em;
  min-height: 46px;
  padding: 0.75em 1.5em;
  border: 1px solid transparent;
  border-radius: var(--radius);
  color: var(--ink);
  font-size: var(--fs-sm);
  font-weight: 580;
  line-height: 1;
  letter-spacing: -0.005em;
  text-align: center;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-out),
    box-shadow var(--dur-mid) var(--ease-out),
    background-color var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out);
}

.btn:active {
  transform: translateY(1px);
}

.btn--primary {
  background-image: linear-gradient(170deg, var(--accent), var(--accent-deep));
  color: var(--on-accent);
  box-shadow: 0 1px 0 0 var(--line-strong) inset, var(--shadow-accent);
}

.btn--ghost {
  background-color: transparent;
  border-color: var(--line-strong);
  color: var(--ink);
}

@media (hover: hover) {
  .btn--primary:hover {
    box-shadow: 0 1px 0 0 var(--line-strong) inset,
      0 16px 44px -12px var(--accent-glow);
  }

  .btn--ghost:hover {
    background-color: var(--line);
    border-color: var(--accent);
  }

  a:hover {
    text-decoration-color: var(--accent);
  }
}


/* 8. Background layers ===================================================
   Both layers are inert: position: fixed, pointer-events: none, aria-hidden.
   ------------------------------------------------------------------------ */

/* Indigo aurora — the light the product floats in. The blooms are built from
   soft radial-gradient stops rather than filter: blur(), which reads the same
   and costs nothing to composite on a viewport-sized fixed layer. */
.aurora {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

.aurora::before {
  content: "";
  position: absolute;
  /* Oversized inside the clipped parent so the drift never reveals an edge.
     The box is 150% of the viewport starting 25% above it, so a bloom meant
     for viewport position P sits at (P + 25%) / 1.5 here. */
  inset: -25%;
  /* Radii are percentages, not lengths, so the composition is identical at
     every viewport width instead of swamping narrow screens. */
  background-image:
    radial-gradient(43% 50% at 50% 21%, var(--aurora-1), transparent 70%),
    radial-gradient(33% 41% at 25% 26%, var(--aurora-2), transparent 68%),
    radial-gradient(30% 39% at 75% 33%, var(--aurora-3), transparent 66%);
}

/* Film grain — keeps the gradients from banding on wide displays. */
.grain {
  position: fixed;
  inset: 0;
  z-index: 100;
  pointer-events: none;
  opacity: 0.035;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 200px 200px;
}


/* 9. Motion ==============================================================
   `.reveal` is visible by default. Task 5's script adds `js` to <html>; only
   then does it start hidden and animate in. With JavaScript off, nothing on
   the page is ever invisible.
   ------------------------------------------------------------------------ */

.reveal {
  opacity: 1;
  transform: none;
}

.js .reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity var(--dur-slow) var(--ease-out),
    transform var(--dur-slow) var(--ease-out);
}

.js .reveal.is-revealed {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }

  .aurora::before {
    animation: aurora-drift 48s var(--ease-out) infinite alternate;
  }
}

@keyframes aurora-drift {
  from {
    transform: translate3d(-1.5%, -1%, 0) scale(1);
  }
  to {
    transform: translate3d(1.5%, 1.5%, 0) scale(1.06);
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition: none !important;
    scroll-behavior: auto !important;
  }

  /* Nothing may be left hidden or offset. */
  .js .reveal,
  .js .reveal.is-revealed {
    opacity: 1 !important;
    transform: none !important;
  }

  /* Reserved hook: any element a later section parallaxes carries this. */
  [data-parallax] {
    transform: none !important;
  }
}


/* 10. Navigation =========================================================
   Sticky translucent bar. Its rendered height is exactly --nav-h, which is
   what `scroll-padding-top` in section 4 relies on, so the height is set from
   the token rather than left to fall out of the contents. The hairline is a
   box-shadow, not a border, precisely so it cannot add a 65th pixel.
   ------------------------------------------------------------------------ */

:root {
  /* Alpha variants of --bg (#08080C) and of pure black. A custom property
     holding a hex cannot be given an alpha channel without color-mix(), so
     these are spelled out. Keep the first two RGB triplets in sync with --bg.
     Same convention as the --aurora-* values in section 2. */
  --nav-bg: rgba(8, 8, 12, 0.72);
  --nav-bg-opaque: rgba(8, 8, 12, 0.94);

  /* The long fall of shadow that lets the product read as floating, and the
     bloom of accent light it appears to be floating in. Shadows rather than a
     blurred pseudo-element: a box-shadow always paints outside and behind its
     own border box, so it survives the entrance animation putting a transform
     (and therefore a stacking context) on the frame.
     The first bloom is ambient and unoffset so the frame is rimmed in light
     even when the fold crops its lower edge away; the other two fall below
     it, which is where the light reads as spilled. */
  --shadow-lift: 0 2px 8px -2px rgba(0, 0, 0, 0.5),
    0 44px 96px -34px rgba(0, 0, 0, 0.92);
  --shadow-bloom: 0 0 76px -22px var(--accent-glow),
    0 26px 60px -26px var(--accent-glow),
    0 74px 150px -44px var(--accent-glow);
}

.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  background-color: var(--nav-bg);
  -webkit-backdrop-filter: blur(14px) saturate(150%);
  backdrop-filter: blur(14px) saturate(150%);
  box-shadow: 0 1px 0 0 var(--line);
}

/* Safari on older macOS and Firefox with the feature disabled get an almost
   opaque bar instead of unreadable transparency. */
@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .nav {
    background-color: var(--nav-bg-opaque);
  }
}

.nav__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: clamp(12px, 3vw, 32px);
  height: var(--nav-h);
}

.nav__brand {
  display: inline-flex;
  align-items: center;
  gap: 0.55em;
  color: var(--ink);
  font-size: var(--fs-md);
  font-weight: 640;
  letter-spacing: -0.028em;
  text-decoration: none;
  white-space: nowrap;
}

.nav__mark {
  flex: none;
  border-radius: 7px;
  box-shadow: var(--shadow-accent);
}

/* The links are the only thing that gives, so the brand and the button keep
   their natural size at every width. */
.nav__links {
  display: flex;
  align-items: center;
  gap: 2px;
  margin: 0;
}

.nav__link {
  display: block;
  padding: 8px 12px;
  border-radius: 9px;
  color: var(--ink-dim);
  font-size: var(--fs-sm);
  font-weight: 520;
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease-out),
    background-color var(--dur-fast) var(--ease-out);
}

.nav__cta {
  min-height: 38px;
  padding: 0 1.15em;
}

@media (hover: hover) {
  .nav__link:hover {
    background-color: var(--line);
    color: var(--ink);
  }

  .nav__brand:hover .nav__wordmark {
    color: var(--ink);
  }
}

/* Below 720px the anchor links fold away entirely: the wordmark and the
   download button are what a phone visitor came for, and a hamburger for
   three in-page anchors would be more chrome than content. */
@media (max-width: 719px) {
  .nav__links {
    display: none;
  }
}


/* 11. Hero ===============================================================
   The single most important screen on the site. The product sits in a
   bezelled frame over a bloom of --accent-glow, so the light appears to spill
   out from under it, and the frame is deliberately taller than the remaining
   fold on desktop so its lower edge is cropped by the viewport.
   ------------------------------------------------------------------------ */

.hero {
  position: relative;
  padding-top: clamp(48px, 6vw, 80px);
  padding-bottom: clamp(72px, 10vw, 132px);
  /* Task 5 mounts an absolutely-positioned canvas on [data-cursor-stage];
     `clip` contains it and the stage bloom without breaking sticky. */
  overflow: clip;
}

.hero__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

/* The component's line-height of 1 assumes the eyebrow stays on one line. It
   wraps below about 340px, so give it leading here rather than changing the
   shared rule in section 5. */
.hero .eyebrow {
  line-height: 1.45;
}

.hero__title {
  max-width: 22ch;
  margin-top: 22px;
  /* Tops out at 76px and bottoms out at 34px, which it reaches at 320px. */
  font-size: clamp(2.125rem, 1.35rem + 3.9vw, 4.75rem);
  line-height: 1.05;
  letter-spacing: -0.035em;
}

.hero__sub {
  max-width: 54ch;
  margin-top: 20px;
  color: var(--ink-dim);
  font-size: var(--fs-md);
  line-height: 1.55;
}

.hero__cta {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
  margin-top: 32px;
}

.hero__meta {
  margin-top: 18px;
  color: var(--ink-dim);
  font-size: var(--fs-sm);
}

.hero__meta [data-version-badge] {
  color: var(--ink);
  font-weight: 560;
  font-variant-numeric: tabular-nums;
}

/* The frame. `padding` is the bezel; the border is the hairline that keeps it
   from dissolving into the aurora at the top of the page. */
.hero__stage {
  position: relative;
  width: 100%;
  max-width: 1100px;
  margin-top: clamp(40px, 4.5vw, 64px);
  padding: clamp(4px, 0.5vw, 7px);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  background-color: var(--bg-card);
  box-shadow: inset 0 1px 0 0 var(--line-strong), var(--shadow-lift),
    var(--shadow-bloom);
}

/* A single soft sheen down the top of the glass. */
.hero__stage::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background-image: linear-gradient(180deg, var(--line), transparent 38%);
  pointer-events: none;
}

.hero__stage img,
.hero__stage video {
  display: block;
  width: 100%;
  border-radius: calc(var(--radius-lg) - 6px);
}

/* A normal image paints independently of the video player's poster pipeline.
   Native video controls remain the fallback when JavaScript is unavailable. */
.hero__preview {
  display: none;
  position: absolute;
  inset: clamp(4px, 0.5vw, 7px);
  z-index: 1;
  padding: 0;
  border: 0;
  border-radius: calc(var(--radius-lg) - 6px);
  overflow: hidden;
  cursor: pointer;
  background: var(--bg-card);
}
.hero__preview[hidden] { display: none; }
@media (max-width: 767px), (prefers-reduced-motion: reduce) {
  .hero__preview:not([hidden]) { display: block; }
  .hero__stage:has(.hero__preview:not([hidden])) .hero__media { visibility: hidden; }
}
.hero__preview img { height: 100%; object-fit: cover; }
.hero__preview span {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  padding: 12px 18px;
  border-radius: 999px;
  color: #fff;
  background: #08080c;
  font: inherit;
  white-space: nowrap;
}
.hero__preview:focus-visible { outline: 3px solid #fff; outline-offset: -3px; }

/* Keep the mobile hero and the media visible on the first paint: opacity
   entrances delay LCP even when the poster has already downloaded. Desktop
   copy retains its entrance when motion is allowed. */
@media (min-width: 768px) and (prefers-reduced-motion: no-preference) {
  .hero__inner > :not(.hero__stage) {
    animation: hero-rise var(--dur-slow) var(--ease-out) both;
  }

  .hero__inner > :nth-child(2) {
    animation-delay: 70ms;
  }
  .hero__inner > :nth-child(3) {
    animation-delay: 130ms;
  }
  .hero__inner > :nth-child(4) {
    animation-delay: 190ms;
  }
  .hero__inner > :nth-child(5) {
    animation-delay: 240ms;
  }
  .hero__inner > :nth-child(6) {
    animation-delay: 300ms;
  }
}

@keyframes hero-rise {
  from {
    opacity: 0;
    transform: translateY(16px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}


/* 12. Feature theater ====================================================
   The centrepiece. On a wide screen, with JavaScript and with motion allowed,
   one framed app window is pinned in the right-hand column while five copy
   beats scroll past it on the left, and the pinned window cross-fades between
   the five shots as each beat takes over.

   Everywhere else -- narrow screens, JavaScript off, reduced motion -- the
   section is a single plain column in which each beat's copy sits directly
   above its own image. That fallback is the BASE state below; the pinned
   layer is added by exactly one media query,
   `(min-width: 900px) and (prefers-reduced-motion: no-preference)`, further
   scoped to `.js`. So nothing has to be undone for the fallback to be right:
   if any one of the three conditions is absent the pinned layer simply never
   applies, and no `[data-beat]` or `[data-beat-img]` is ever hidden.

   Stacking the copy with its own image needs the beat and the image to be
   siblings, but the image lives inside `[data-theater-visual]`. Both wrappers
   therefore take `display: contents` in the base state, which promotes every
   beat and every image to a direct flex item of the grid so `order` can
   interleave them. DOM order stays copy-first, so the reading order is the
   five beats in sequence followed by the five illustrations.
   ------------------------------------------------------------------------ */

.theater__grid {
  display: flex;
  flex-direction: column;
  gap: clamp(44px, 7vw, 76px);
}

.theater__beats,
.theater__visual {
  display: contents;
}

/* Beat N then image N. Only ever consulted in the stacked base state. */
.theater__beat[data-beat="0"] { order: 1; }
.theater__img[data-beat-img="0"] { order: 2; }
.theater__beat[data-beat="1"] { order: 3; }
.theater__img[data-beat-img="1"] { order: 4; }
.theater__beat[data-beat="2"] { order: 5; }
.theater__img[data-beat-img="2"] { order: 6; }
.theater__beat[data-beat="3"] { order: 7; }
.theater__img[data-beat-img="3"] { order: 8; }
.theater__beat[data-beat="4"] { order: 9; }
.theater__img[data-beat-img="4"] { order: 10; }

/* Copy. `position: relative` is here rather than in the pinned layer because
   the progress rail is painted as this element's ::before. */
.theater__beat {
  position: relative;
  max-width: 46ch;
}

.theater__step {
  color: var(--accent);
  font-size: var(--fs-xs);
  font-weight: 620;
  line-height: 1;
  letter-spacing: 0.16em;
  font-variant-numeric: tabular-nums;
}

.theater__beat h2 {
  margin-top: 14px;
  font-size: var(--fs-xl);
  letter-spacing: -0.025em;
}

.theater__copy {
  margin-top: 14px;
  color: var(--ink-dim);
  font-size: var(--fs-md);
  line-height: 1.55;
}

/* The shots. In the stacked state each one is its own framed card. */
.theater__img {
  display: block;
  width: 100%;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  background-color: var(--bg-card);
  box-shadow: inset 0 1px 0 0 var(--line-strong), var(--shadow-lift);
}

@media (min-width: 900px) and (prefers-reduced-motion: no-preference) {
  .js .theater__grid {
    display: grid;
    grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
    column-gap: clamp(32px, 4vw, 64px);
    /* Both wrappers occupy row 1, so the row is as tall as the beats column
       and the pinned figure has that whole height to travel through. */
    align-items: start;
  }

  .js .theater__beats {
    display: block;
    grid-column: 1;
    grid-row: 1;
  }

  .js .theater__visual {
    display: block;
    grid-column: 2;
    grid-row: 1;
    position: sticky;
    top: var(--nav-h);
    /* The whole viewport below the nav, so the framed window can be centred
       in it rather than clinging to the top edge. */
    height: calc(100vh - var(--nav-h));
  }

  /* One screenful of scroll per beat, with the copy centred in its own
     screenful -- which is exactly where the observer's -45%/-45% band sits,
     so the image swaps as a beat reaches the middle of the viewport. */
  .js .theater__beat {
    display: grid;
    align-content: center;
    min-height: calc(100vh - var(--nav-h));
    max-width: 44ch;
    padding-left: 30px;
  }

  /* The thin progress rail. Each beat paints its own full-height segment, so
     the five segments meet to form one unbroken line down the column and the
     active segment lights up in place. */
  .js .theater__beat::before {
    content: "";
    position: absolute;
    inset-block: 0;
    left: 0;
    width: 2px;
    border-radius: 2px;
    background-color: var(--line);
    transition: background-color var(--dur-mid) var(--ease-out),
      box-shadow var(--dur-mid) var(--ease-out);
  }

  .js .theater__beat.is-active::before {
    background-color: var(--accent);
    box-shadow: 0 0 18px 0 var(--accent-glow);
  }

  /* All five shots stacked in the pinned frame and centred in it. Absolute,
     so a cross-fade can never shift the layout; height falls out of the
     intrinsic ratio carried by the width/height attributes. max-height caps
     it to the sticky figure's own height (100vh - --nav-h, see
     .js .theater__visual above), so a short, very wide viewport can't push
     the 16:10 shot taller than the figure and spill it out. */
  .js .theater__img {
    position: absolute;
    top: 50%;
    left: 0;
    right: 0;
    max-height: 100%;
    object-fit: contain;
    opacity: 0;
    transform: translateY(-50%) scale(1.018);
    transition: opacity 450ms var(--ease-out), transform 450ms var(--ease-out);
    box-shadow: inset 0 1px 0 0 var(--line-strong), var(--shadow-lift),
      var(--shadow-bloom);
  }

  .js .theater__img.is-active {
    opacity: 1;
    transform: translateY(-50%) scale(1);
  }
}


/* 13. Hero cursor trail ==================================================
   Slipreel's own smoothed cursor, performed on the visitor. The canvas is
   mounted onto [data-cursor-stage] (the hero) by `site.js`, which refuses to
   create it at all under reduced motion or on a coarse pointer; the rule
   below is the second lock on the reduced-motion half of that.
   ------------------------------------------------------------------------ */

/* Deviation from original spec: the canvas sits above .hero__inner entirely
   rather than below the hero copy, because its parent stacking context (.hero)
   has no z-index slot that straddles the product frame. mix-blend-mode:
   plus-lighter makes this acceptable: over near-white hero text it is
   imperceptible, and the effect reads as indigo bloom rather than a smudge.
   Visually verified in browser. */
.cursor-trail {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  mix-blend-mode: plus-lighter;
}

/* Task 3 left .hero__inner unpositioned, which would leave it painting below
   a positioned sibling. Still needed even though the trail now paints above
   it: without a stacking context of its own, .hero__inner's children would
   otherwise interleave with the canvas by DOM order instead of painting as
   one unit. */
.hero__inner {
  position: relative;
  z-index: 2;
}

@media (prefers-reduced-motion: reduce) {
  .cursor-trail {
    display: none;
  }
}


/* 14. Deep dives ==========================================================
   Four alternating image/copy pairs. In the markup, copy always comes first
   and the figure second (see the comment in index.html); on narrow screens
   that DOM order is overridden so the image leads, matching the theater's
   "stacked base state, enhanced above a breakpoint" approach in section 12.

   Alternation: article 1 and 3 (Timeline, Camera) show image-left/copy-right;
   article 2 and 4 (Audio, Export) show image-right/copy-left, which is the
   plain DOM order and needs no override. `:nth-child(odd)` picks out 1 and 3.
   ------------------------------------------------------------------------ */

.deep-dives__list {
  display: flex;
  flex-direction: column;
  gap: clamp(64px, 9vw, 112px);
}

.deep-dive {
  display: grid;
  grid-template-columns: 1fr;
  align-items: center;
  gap: 28px;
}

/* Mobile/tablet base state: image always first, regardless of alternation. */
.deep-dive__media {
  order: -1;
}

.deep-dive__media img {
  width: 100%;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  background-color: var(--bg-card);
  box-shadow: inset 0 1px 0 0 var(--line-strong), var(--shadow-lift);
}

.deep-dive__copy .eyebrow {
  margin-bottom: 10px;
}

.deep-dive__copy h2 {
  font-size: var(--fs-2xl);
}

.deep-dive__copy p {
  margin-top: 14px;
  max-width: 46ch;
  color: var(--ink-dim);
  font-size: var(--fs-md);
  line-height: 1.6;
}

@media (min-width: 900px) {
  .deep-dive {
    grid-template-columns: 1fr 1fr;
    gap: clamp(40px, 6vw, 96px);
  }

  /* Reset to plain DOM order (copy left, image right)... */
  .deep-dive__media {
    order: 0;
  }

  /* ...then flip the odd ones back to image-left. */
  .deep-dive:nth-child(odd) .deep-dive__media {
    order: -1;
  }
}


/* 15. Feature grid ========================================================
   Sixteen short, individually-shippable claims. Each item is one phrase, not
   a label-plus-description pair, so this is a plain list rather than a
   definition list. 1 column by default, 2 at tablet, 4 at desktop.
   ------------------------------------------------------------------------ */

.feature-grid__heading {
  max-width: 34ch;
  margin-inline: auto;
  text-align: center;
}

.feature-grid__list {
  display: grid;
  grid-template-columns: 1fr;
  gap: 12px;
  margin-top: clamp(36px, 5vw, 56px);
}

.feature-grid__item {
  padding: 18px 20px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background-color: var(--bg-card);
  color: var(--ink-dim);
  font-size: var(--fs-sm);
  line-height: 1.5;
}

@media (min-width: 640px) {
  .feature-grid__list {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 1024px) {
  .feature-grid__list {
    grid-template-columns: repeat(4, 1fr);
  }
}


/* 16. Platform strip ======================================================
   "Built like a Mac app": four trust signals in a bordered card, echoing the
   hero stage / theater frame treatment (hairline border, --bg-card, inset
   sheen) so it reads as part of the same system rather than a new one.
   ------------------------------------------------------------------------ */

.platform-strip {
  padding: clamp(28px, 4vw, 40px) clamp(20px, 4vw, 44px);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  background-color: var(--bg-card);
  box-shadow: inset 0 1px 0 0 var(--line-strong);
}

.platform-strip__heading {
  text-align: center;
}

.platform-strip__list {
  display: grid;
  grid-template-columns: 1fr;
  gap: 20px;
  margin-top: clamp(28px, 4vw, 40px);
}

.platform-strip__item {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  color: var(--ink-dim);
  font-size: var(--fs-sm);
  line-height: 1.5;
}

.platform-strip__icon {
  flex: none;
  width: 20px;
  height: 20px;
  margin-top: 0.15em;
  color: var(--accent);
}

@media (min-width: 720px) {
  .platform-strip__list {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 1024px) {
  .platform-strip__list {
    grid-template-columns: repeat(4, 1fr);
  }
}


/* 17. Pricing ============================================================
   Early-access banner (see the copy note in index.html -- this product has
   no license enforcement yet, so nothing here may claim an instant key)
   above two cards. The lifetime card is first in the DOM, which is what
   keeps it first when the grid collapses to one column on mobile; the
   two-column desktop layout below just lays that same order out side by
   side, echoing the platform strip's bordered/--bg-card treatment.
   ------------------------------------------------------------------------ */

.pricing__banner {
  max-width: 68ch;
  margin-inline: auto;
  padding: clamp(20px, 3vw, 28px) clamp(20px, 4vw, 32px);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  background-color: var(--bg-card);
  box-shadow: inset 0 1px 0 0 var(--line-strong);
  color: var(--ink-dim);
  font-size: var(--fs-sm);
  line-height: 1.6;
  text-align: center;
}

.pricing__banner strong {
  color: var(--ink);
}

.pricing__cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: 20px;
  margin-top: clamp(28px, 4vw, 40px);
}

.pricing-card {
  display: flex;
  flex-direction: column;
  padding: clamp(28px, 4vw, 36px);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background-color: var(--bg-card);
}

.pricing-card--featured {
  border-color: var(--accent);
  box-shadow: var(--shadow-accent);
}

.pricing-card__badge {
  display: inline-flex;
  align-self: flex-start;
  margin-bottom: 14px;
  padding: 0.3em 0.85em;
  border-radius: 999px;
  background-image: linear-gradient(170deg, var(--accent), var(--accent-deep));
  color: var(--on-accent);
  font-size: var(--fs-xs);
  font-weight: 580;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.pricing-card__name {
  font-size: var(--fs-xl);
}

.pricing-card__price {
  margin-top: 10px;
  color: var(--ink-dim);
  font-size: var(--fs-sm);
}

.pricing-card__amount {
  color: var(--ink);
  font-size: var(--fs-2xl);
  font-weight: 620;
  letter-spacing: -0.02em;
}

.pricing-card__features {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 24px;
  margin-bottom: 28px;
  color: var(--ink-dim);
  font-size: var(--fs-sm);
}

.pricing-card__features li {
  display: flex;
  align-items: baseline;
  gap: 10px;
}

.pricing-card__features li::before {
  content: "";
  flex: none;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background-color: var(--accent);
}

.pricing-card__cta {
  margin-top: auto;
  width: 100%;
}

.pricing__note {
  margin-top: clamp(24px, 4vw, 32px);
  color: var(--ink-dim);
  font-size: var(--fs-sm);
  text-align: center;
}

@media (min-width: 640px) {
  .pricing__cards {
    grid-template-columns: repeat(2, 1fr);
    align-items: stretch;
  }
}


/* 18. FAQ =================================================================
   Native <details>/<summary>: opens and closes with the keyboard alone and
   needs no script. The plus/cross indicator is two absolutely-positioned
   bars on the summary itself so no extra markup is needed per question;
   the vertical bar just rotates flat onto the horizontal one when open.
   Its rotation is a transition, so it is already covered by section 9's
   blanket `*, *::before, *::after` reduced-motion override.
   ------------------------------------------------------------------------ */

.faq__inner {
  max-width: 760px;
  margin-inline: auto;
}

.faq__heading {
  text-align: center;
}

.faq__list {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: clamp(32px, 5vw, 48px);
}

.faq__item {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background-color: var(--bg-card);
  overflow: hidden;
}

.faq__question {
  position: relative;
  padding: 20px 52px 20px 22px;
  color: var(--ink);
  font-size: var(--fs-md);
  font-weight: 580;
  cursor: pointer;
  list-style: none;
}

.faq__question::marker,
.faq__question::-webkit-details-marker {
  display: none;
}

.faq__question::before,
.faq__question::after {
  content: "";
  position: absolute;
  top: 50%;
  right: 24px;
  width: 14px;
  height: 2px;
  background-color: var(--accent);
  transform: translateY(-50%);
  transition: transform var(--dur-fast) var(--ease-out);
}

.faq__question::after {
  transform: translateY(-50%) rotate(90deg);
}

.faq__item[open] .faq__question::after {
  transform: translateY(-50%) rotate(0deg);
}

.faq__answer {
  margin: 0;
  padding: 0 22px 22px;
  max-width: 62ch;
  color: var(--ink-dim);
  font-size: var(--fs-sm);
  line-height: 1.6;
}


/* 19. Final CTA ===========================================================
   Closing band: repeats the hero's headline promise next to a third
   download button. Same bordered/--bg-card/--shadow-accent treatment as the
   pricing cards, so the page's last beat still reads as the same system.
   ------------------------------------------------------------------------ */

.cta-band__card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 28px;
  padding: clamp(48px, 8vw, 96px) clamp(24px, 6vw, 64px);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  background-color: var(--bg-card);
  box-shadow: inset 0 1px 0 0 var(--line-strong), var(--shadow-accent);
  text-align: center;
}

.cta-band__heading {
  max-width: 20ch;
  font-size: var(--fs-2xl);
}


/* 20. Footer ==============================================================
   Wordmark, contact, and copyright. No social links, no changelog link.
   ------------------------------------------------------------------------ */

.site-footer {
  border-top: 1px solid var(--line);
  padding-block: clamp(28px, 4vw, 40px);
}

.site-footer__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px 24px;
}

.site-footer__wordmark {
  color: var(--ink);
  font-weight: 620;
  letter-spacing: -0.01em;
}

.site-footer__contact {
  color: var(--ink-dim);
}

.site-footer__copyright {
  flex-basis: 100%;
  order: 1;
  color: var(--ink-dim);
  font-size: var(--fs-xs);
}

@media (min-width: 640px) {
  .site-footer__copyright {
    flex-basis: auto;
    order: 0;
    margin-left: auto;
  }
}


/* 21. Accessibility utilities ============================================
   The skip link is the first focusable element in the document (see
   index.html) so a keyboard or screen-reader visitor can jump straight past
   the nav to #main. It is off-screen rather than display:none, so it still
   receives focus; :focus-visible in section 4 supplies the ring.
   `.visually-hidden` clips content to a single pixel without display:none,
   for the same reason -- section headings and other assistive-tech-only
   text still need to exist in the accessibility tree even when the visual
   design has no room for them.
   ------------------------------------------------------------------------ */

.skip-link {
  position: absolute;
  top: -80px;
  left: 16px;
  z-index: 1000;
  padding: 12px 20px;
  border-radius: var(--radius);
  background-color: var(--bg-card);
  color: var(--ink);
  font-size: var(--fs-sm);
  font-weight: 580;
  text-decoration: none;
  box-shadow: var(--shadow-lift);
  transition: top var(--dur-fast) var(--ease-out);
}

.skip-link:focus {
  top: 16px;
}

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


/* 22. Comparison pages ===================================================
   Shared by the "<competitor> alternative" pages. Reuses the site tokens
   and the nav / faq / cta / footer components verbatim; only the centered
   page header, the feature table, and the two-up notes are page-specific.
   ------------------------------------------------------------------------ */

.page-hero {
  padding-top: calc(var(--nav-h) + clamp(28px, 7vw, 72px));
  padding-bottom: 0;
  text-align: center;
}

.page-hero__inner {
  max-width: 60ch;
  margin-inline: auto;
}

.page-hero .eyebrow {
  justify-content: center;
}

.page-hero h1 {
  margin-top: 0.5em;
  font-size: var(--fs-3xl);
}

.page-hero__sub {
  margin-top: 1em;
  color: var(--ink-dim);
  font-size: var(--fs-md);
}

.page-hero__cta {
  margin-top: 2em;
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: center;
}

.prose {
  max-width: 68ch;
}

.prose p + p {
  margin-top: 1em;
}

.prose p {
  color: var(--ink-dim);
  font-size: var(--fs-md);
}

/* Feature table. Wrapped in .ctable-wrap so it scrolls on its own on narrow
   screens without the page body ever scrolling sideways. */
.ctable-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}

.ctable {
  width: 100%;
  min-width: 520px;
  border-collapse: separate;
  border-spacing: 0;
  font-size: var(--fs-sm);
}

.ctable th,
.ctable td {
  padding: 0.85em 1.1em;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--line);
}

.ctable tr:last-child th,
.ctable tbody tr:last-child td {
  border-bottom: 0;
}

.ctable thead th {
  color: var(--ink);
  font-weight: 620;
  white-space: nowrap;
  border-bottom-color: var(--line-strong);
}

.ctable tbody th {
  color: var(--ink-dim);
  font-weight: 500;
}

.ctable td {
  color: var(--ink);
}

/* Highlight the Slipreel column (2nd). Explicit rgba (not color-mix) to match
   the rest of the stylesheet's approach to accent tints. */
.ctable th:nth-child(2),
.ctable td:nth-child(2) {
  background-color: rgba(108, 92, 231, 0.1);
}

.ctable__yes {
  color: var(--accent);
  font-weight: 580;
}

.ctable__no {
  color: var(--ink-faint);
}

/* Two-up: why switch / when the other wins. */
.compare-split {
  display: grid;
  gap: 20px;
  grid-template-columns: 1fr;
}

@media (min-width: 760px) {
  .compare-split {
    grid-template-columns: 1fr 1fr;
  }
}

.compare-note {
  padding: clamp(20px, 3vw, 30px);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background-color: var(--bg-card);
}

.compare-note h3 {
  font-size: var(--fs-lg);
  margin-bottom: 0.75em;
}

.compare-note ul {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 0.7em;
}

.compare-note li {
  position: relative;
  padding-left: 1.4em;
  color: var(--ink-dim);
}

.compare-note li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background-color: var(--accent);
}

/* Footer "Compare" row — the internal links between the comparison pages. */
.footer-compare {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px 20px;
  padding-bottom: 18px;
  margin-bottom: 18px;
  border-bottom: 1px solid var(--line);
  font-size: var(--fs-sm);
}

.footer-compare__label {
  color: var(--ink-dim);
  font-weight: 580;
}

.footer-compare a {
  color: var(--ink-dim);
  text-decoration: none;
}


/* 23. Nav "Compare" dropdown =============================================
   A native <details> disclosure in the nav — the same zero-JS pattern as the
   FAQ. The nav is desktop-only (it folds away below 720px, see section 10),
   so this never has to solve touch dropdowns. Click the summary to open;
   following a link (or reloading) resets it closed.
   ------------------------------------------------------------------------ */

.nav__dropdown {
  position: relative;
}

/* inline-flex also removes the default list-item disclosure marker. */
.nav__link--menu {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  list-style: none;
  cursor: pointer;
}

.nav__link--menu::-webkit-details-marker {
  display: none;
}

.nav__link--menu::after {
  content: "";
  width: 5px;
  height: 5px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
  transition: transform var(--dur-fast) var(--ease-out);
}

.nav__dropdown[open] .nav__link--menu::after {
  transform: translateY(1px) rotate(-135deg);
}

.nav__menu {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  min-width: 210px;
  margin: 0;
  padding: 6px;
  display: grid;
  gap: 2px;
  background-color: var(--nav-bg-opaque);
  -webkit-backdrop-filter: blur(14px) saturate(150%);
  backdrop-filter: blur(14px) saturate(150%);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lift);
}

.nav__menu a {
  display: block;
  padding: 8px 12px;
  border-radius: 9px;
  color: var(--ink-dim);
  font-size: var(--fs-sm);
  font-weight: 520;
  text-decoration: none;
  white-space: nowrap;
}

@media (hover: hover) {
  .nav__menu a:hover {
    background-color: var(--line);
    color: var(--ink);
  }
}

/* Avoid full-viewport noise blending and continuous background compositing
   on phones. Keep the same static gradient composition. */
@media (max-width: 767px) {
  .grain { display: none; }
  .aurora::before { animation: none; }
}
