/* The app's design language, translated. Every value here is a copy of a constant in
   Gallerya/UI/Design/Aurora.swift — if those move, these move with them. */
:root {
  --base: #09090e;            /* Aurora.Colour.base: not pure black, a hint of blue */
  --ink: #ffffff;
  /* The text greys the app lifted for contrast: they clear 4.5:1 over the brightest glow.
     The old 0.62 / 0.45 / 0.28 left the faint grey — the price line, the captions — at 2.3:1. */
  --body: rgba(255, 255, 255, 0.84);
  --quiet: rgba(255, 255, 255, 0.70);
  --faint: rgba(255, 255, 255, 0.60);
  --hairline: rgba(255, 255, 255, 0.12);
  --good: #33d98c;            /* Aurora.Colour.good */
  --warn: #ffb84d;
  --danger: #ff9e99;          /* lifted from #ff6b6b, which measured 3.9:1 over a glow */
  --link: #94c9ff;
  --azure-core: #1747c7;      /* Tint.azure.core — deep, because it is a light source */
  --azure-accent: #8cc7ff;    /* Tint.azure.accent — the legible reading of the same hue */
  --violet-core: #6b33b8;
  --violet-accent: #c29eff;
  --jade-core: #0a8561;
  --jade-accent: #4de09e;
  --panel: rgba(255, 255, 255, 0.05);
  --radius: 28px;
  --display: ui-serif, "New York", Georgia, "Times New Roman", serif;
  --sans: -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, "Helvetica Neue", sans-serif;

  /* The app's springs, sampled into linear() so CSS moves the way SwiftUI does. Generated from
     stiffness (2π / duration)² and damping ratio 1 − bounce; a spring has no duration of its own,
     so each comes with the time it takes to settle within a thousandth.
     --spring-hero: critically damped, response 0.5 — the demo's panel changing state. The one
       deliberate departure from the app: BackupView's hero is .spring(duration: 0.5, bounce: 0.34),
       where a person's own backup starting is the event. Here nobody touched anything, and
       overshoot reads as momentum — a panel that changes on its own has none to carry.
     --spring-press: critically damped, response 0.3 — a control settling back after a press. */
  --spring-hero: linear(0, 0.0392, 0.1285, 0.2385, 0.3517, 0.4587, 0.5547, 0.638, 0.7085, 0.7672,
    0.8153, 0.8543, 0.8856, 0.9106, 0.9304, 0.946, 0.9582, 0.9677, 0.9752, 0.9809, 0.9854, 0.9888,
    0.9915, 0.9935, 0.995, 0.9962, 0.9971, 0.9978, 0.9984, 0.9988, 1);
  --spring-hero-settle: 740ms;
  --spring-press: linear(0, 0.0816, 0.2431, 0.4129, 0.562, 0.6819, 0.7736, 0.8412, 0.89, 0.9246,
    0.9487, 0.9653, 0.9767, 0.9844, 0.9896, 0.9931, 0.9955, 0.997, 0.998, 0.9987, 1);
  --spring-press-settle: 450ms;

  /* The page is only ever dark, so say so: scrollbars and native controls draw dark too, rather
     than a light scrollbar down the side of the glow on platforms that show one. */
  color-scheme: dark;
}

/* Increase Contrast: the second value the app carries for every grey and status colour. */
@media (prefers-contrast: more) {
  :root {
    --body: rgba(255, 255, 255, 0.94);
    --quiet: rgba(255, 255, 255, 0.86);
    --faint: rgba(255, 255, 255, 0.78);
    --hairline: rgba(255, 255, 255, 0.26);
    --good: #70f0b3;
    --warn: #ffd185;
    --danger: #ffbdb8;
    --link: #b8deff;
  }
}

* { box-sizing: border-box; }

/* Only for people who have not asked for less motion: a smooth jump down the page is exactly
   the kind of large, vestibular movement Reduce Motion exists to stop. */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

/* The type is set in rem, so a reader who raised their browser's text size gets it: the web's
   nearest thing to the Dynamic Type the app honours. 1rem is 16px at the default. */
body {
  margin: 0;
  background: var(--base);
  color: var(--body);
  font: 400 1.0625rem/1.65 var(--sans);
  font-optical-sizing: auto;
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent; /* every control draws its own pressed state */
  overflow-x: hidden;
}

/* ── The background: the app's three tints, following the page ───────────────
   The app paints one tint per screen: azure on Backup, jade on Cleanup, violet on
   the Archive. The page has the same three subjects, so it does the same thing:
   each section says which screen it is about (data-tint), and the light takes on
   that section's tint as it reaches the middle of the window. assets/aurora.js
   does the arithmetic. The same three radial glows in the same positions (one high
   and slightly off-centre so the light falls from above the content, one cooler low
   down so it is never a flat wash, one tight at the top), just with the colours
   handed over.

   It used to cross-fade all three on a ten-second loop, with a slow drift on top.
   That is a full-window background moving and changing on its own, around a third of
   a hertz — the thing Reduce Motion exists to stop, and a distraction for everyone
   else, on a page whose hero already has one thing moving. Tied to scroll, the
   change happens because the reader moved, runs backwards when they scroll back, and
   says something: you are reading about the archive now.

   Three stacked layers whose opacities cross-fade, rather than animating the
   gradients themselves: opacity is composited on the GPU, so this costs nothing.
   The script's weights always sum to one, and that only keeps the brightness
   constant if the layers add. Stacked normally, the top layer at half opacity lets
   through half of a lower layer that is itself at half, so mid-handover the screen
   is a half of one tint, a quarter of the other, and a quarter of the dark base —
   measured in WebKit, a quarter less light at the brightest part of the glow, darker
   than either tint on its own. plus-lighter makes the stack add, and the midpoint
   falls between the two tints it joins.

   Without the script, or on the pages with no sections to follow, it holds on
   azure: the tint the app opens on. */
.aurora { position: fixed; inset: 0; z-index: -1; }

.aurora .glow {
  position: absolute; inset: -12%;
  opacity: 0;
  mix-blend-mode: plus-lighter;   /* .aurora is its own stacking context, so they add to each other, not to the page */
  will-change: opacity;
}

.aurora .azure {
  opacity: 1;
  background:
    radial-gradient(60vw 60vw at 42% -8%, rgb(23, 71, 199) 0%, rgba(23, 71, 199, 0.28) 50%, transparent 100%),
    radial-gradient(55vw 55vw at 97% 74%, rgba(76, 36, 153, 0.72) 0%, transparent 100%),
    radial-gradient(22vw 22vw at 42% 0%, rgba(23, 71, 199, 0.55) 0%, transparent 100%);
}
.aurora .jade {
  background:
    radial-gradient(60vw 60vw at 42% -8%, rgb(10, 133, 97) 0%, rgba(10, 133, 97, 0.28) 50%, transparent 100%),
    radial-gradient(55vw 55vw at 97% 74%, rgba(15, 61, 133, 0.72) 0%, transparent 100%),
    radial-gradient(22vw 22vw at 42% 0%, rgba(10, 133, 97, 0.55) 0%, transparent 100%);
}
.aurora .violet {
  background:
    radial-gradient(60vw 60vw at 42% -8%, rgb(107, 51, 184) 0%, rgba(107, 51, 184, 0.28) 50%, transparent 100%),
    radial-gradient(55vw 55vw at 97% 74%, rgba(26, 51, 140, 0.72) 0%, transparent 100%),
    radial-gradient(22vw 22vw at 42% 0%, rgba(107, 51, 184, 0.55) 0%, transparent 100%);
}

/* Reduce Transparency: the app multiplies every glow by 0.22 rather than dropping it, because
   the tint is navigation, not decoration. The layers already sum to one, so scaling the whole
   stack is the same multiplication. */
@media (prefers-reduced-transparency: reduce) {
  .aurora { opacity: 0.22; }
}

.wrap { width: min(1080px, 92vw); margin: 0 auto; }
.column { width: min(720px, 92vw); margin: 0 auto; }

/* Glass, as the app draws it: a translucent fill that still refracts, plus a hairline, and a
   brighter line along the top edge where light catches the material. */
.panel {
  background: var(--panel);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
  backdrop-filter: blur(24px) saturate(140%);
  -webkit-backdrop-filter: blur(24px) saturate(140%);
  padding: 28px;
}

/* Tracking follows size, as SF and New York's own tables do: the bigger the type, the tighter
   it is set, down to nothing at body size. One value for every heading was right for none. */
h1, h2, h3 { font-family: var(--display); color: var(--ink); font-weight: 700; }
h1 { font-size: clamp(2.125rem, 5.6vw, 4rem); line-height: 1.06; letter-spacing: -0.02em; margin: 0 0 18px; text-wrap: balance; }

h2 { font-size: clamp(1.6875rem, 3.4vw, 2.375rem); line-height: 1.12; letter-spacing: -0.015em; margin: 0 0 14px; text-wrap: balance; }
h3 { font-size: 1.375rem; line-height: 1.25; letter-spacing: -0.005em; margin: 0 0 8px; font-weight: 600; }
p { margin: 0 0 14px; text-wrap: pretty; }
a { color: var(--link); text-decoration: none; }

/* Hover only where there is a pointer that hovers. On a phone a tap leaves :hover stuck on
   until the next tap somewhere else, which reads as the link having broken. */
@media (hover: hover) {
  a:hover { text-decoration: underline; }
}

/* Pressed is shown the instant the finger lands, not when it lifts: an iOS-style dim for text
   links, and it comes back with the press spring rather than a snap. */
a { transition: opacity var(--spring-press-settle) var(--spring-press); }
a:active { opacity: 0.55; transition-duration: 0s; }

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

/* The small, wide, upper-case marker the app puts above every group of panels. */
.label {
  font: 600 0.75rem/1 var(--sans);
  letter-spacing: 0.1167em;   /* the app's 1.4pt at 12pt, kept in proportion as it scales */
  text-transform: uppercase;
  color: var(--quiet);
  display: block;
  margin-bottom: 12px;
}

/* A floating material rather than a bar: the page scrolls under it. The material and its
   hairline only arrive once there is content underneath to separate from, the way a navigation
   bar is clear at the scroll edge — at the top of the page there is nothing there, and a blur
   over nothing shows up as a band across the glow. All of it lives on one layer so a single
   opacity can bring it in. */
nav.top { position: sticky; top: 0; z-index: 10; }
nav.top::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: rgba(9, 9, 14, 0.62);
  border-bottom: 1px solid var(--hairline);
  backdrop-filter: blur(20px) saturate(180%); -webkit-backdrop-filter: blur(20px) saturate(180%);
}
@supports (animation-timeline: scroll()) {
  nav.top::before {
    animation: nav-edge linear both;
    animation-timeline: scroll(root);
    animation-range: 0 48px;
  }
}
@keyframes nav-edge { from { opacity: 0; } to { opacity: 1; } }

nav.top .wrap { display: flex; align-items: baseline; gap: 20px; padding: 14px 0; }
nav.top .mark { font-family: var(--display); font-weight: 700; color: var(--ink); font-size: 1.25rem; letter-spacing: -0.01em; }
nav.top a.link { color: var(--body); font-size: 0.9375rem; }

/* The nav and footer links are a line of small text, about 24px tall, where a finger needs 44.
   Each carries a larger invisible hit region instead of padding, so nothing moves on the page.
   The footer's gap is wide enough that neighbouring regions never overlap. */
nav.top a, footer .row a { position: relative; }
nav.top a::after, footer .row a::after { content: ""; position: absolute; inset: -10px -10px; }
nav.top a.link[aria-current="page"] { color: var(--ink); font-weight: 600; }
nav.top .spacer { flex: 1; }

/* The sticky nav would otherwise land on top of whatever "See how it works" jumps to. */
section[id] { scroll-margin-top: 56px; }

header.hero { padding: clamp(56px, 11vw, 132px) 0 40px; text-align: center; }
header.hero p.lede { font-size: clamp(1.125rem, 2.2vw, 1.375rem); color: var(--body); margin: 0 auto 28px; max-width: 30em; }

/* The primary button: presses in on touch-down, quickly, and settles back out on the press
   spring once the finger lifts — the transition on :active is the way in, the one on .cta is
   the way out. */
.cta {
  display: inline-flex; align-items: center; gap: 10px;
  background: rgba(60, 120, 230, 0.62);
  border: 1px solid rgba(255, 255, 255, 0.22);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.14);
  color: #fff; font: 700 1.0625rem var(--sans);
  padding: 15px 30px; border-radius: 16px;
  backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
  transition:
    transform var(--spring-press-settle) var(--spring-press),
    background-color 200ms ease-out,
    opacity 0s;
}
.cta:active { transform: scale(0.97); opacity: 1; transition-duration: 90ms, 200ms, 0s; }
@media (hover: hover) {
  .cta:hover { text-decoration: none; background-color: rgba(76, 138, 250, 0.72); }
  .cta.quiet:hover { background-color: rgba(255, 255, 255, 0.1); }
}
.cta.quiet { background: rgba(255, 255, 255, 0.06); }
.note { color: var(--faint); font-size: 0.875rem; margin-top: 14px; }

.shots { display: flex; gap: 22px; justify-content: center; flex-wrap: wrap; padding: 22px 0 8px; }
.shots img {
  width: min(258px, 44vw); border-radius: 26px;
  border: 1px solid var(--hairline);
  box-shadow: 0 30px 70px rgba(0, 0, 0, 0.55);
}

section { padding: clamp(40px, 7vw, 82px) 0; }
.grid { display: grid; gap: 18px; grid-template-columns: repeat(auto-fit, minmax(276px, 1fr)); }
.grid .panel p:last-child { margin-bottom: 0; }

.dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; margin-right: 8px; }
.dot.good { background: var(--good); box-shadow: 0 0 10px rgba(51, 217, 140, 0.7); }
.dot.warn { background: var(--warn); box-shadow: 0 0 10px rgba(255, 184, 77, 0.7); }

/* A headline number, as the app sets one: light weight, tabular figures. */
.figure { font: 300 3.5rem/1 var(--sans); font-variant-numeric: tabular-nums; color: var(--ink); display: block; }
.figure.serif { font-family: var(--display); font-weight: 700; }
.measure { font-variant-numeric: tabular-nums; font-size: 0.875rem; color: var(--quiet); }

ol.steps { counter-reset: step; list-style: none; padding: 0; margin: 0; display: grid; gap: 16px; }
ol.steps li { counter-increment: step; display: grid; grid-template-columns: 2.75rem 1fr; gap: 16px; align-items: start; }
ol.steps li::before {
  content: counter(step);
  font: 300 1.25rem/2.625rem var(--sans); text-align: center;
  width: 2.625rem; height: 2.625rem; border-radius: 50%;
  border: 1px solid var(--hairline); color: var(--ink);
  background: rgba(255, 255, 255, 0.04);
}

.price { display: grid; gap: 18px; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
.price .amount { font: 300 2.5rem/1 var(--sans); color: var(--ink); font-variant-numeric: tabular-nums; }
.price .per { color: var(--quiet); font-size: 0.9375rem; }
.price ul { list-style: none; padding: 0; margin: 14px 0 0; }
.price li { padding-left: 24px; position: relative; margin-bottom: 8px; }
.price li::before { content: "✓"; position: absolute; left: 0; color: var(--good); }

footer { border-top: 1px solid var(--hairline); padding: 38px 0 56px; color: var(--faint); font-size: 0.875rem; }
footer a { color: var(--body); }
footer .row { display: flex; gap: 20px; flex-wrap: wrap; margin-bottom: 10px; }

/* The prose pages — privacy and support — get the same shell, narrower measure. */
article.prose h1 { font-size: clamp(2rem, 4.6vw, 2.875rem); margin-bottom: 8px; }
article.prose h2 { font-size: 1.5rem; margin-top: 34px; }
article.prose ul { padding-left: 22px; }
article.prose li { margin-bottom: 8px; }
article.prose strong { color: var(--ink); }
article.prose { padding: 40px 0 72px; }

/* ── Hero: copy beside the device, stacked on narrow screens ───────────────── */
header.hero { text-align: left; padding: clamp(40px, 7vw, 96px) 0 48px; }
header.hero .split {
  display: grid; gap: clamp(28px, 5vw, 64px); align-items: center;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
}
header.hero p.lede { margin-left: 0; max-width: 34em; }
@media (max-width: 860px) {
  header.hero { text-align: center; }
  header.hero .split { grid-template-columns: 1fr; }
  header.hero p.lede { margin-left: auto; margin-right: auto; }
}

:root { --device-w: min(330px, 78vw); }

/* ── The device the demo plays inside ─────────────────────────────────────── */
.device {
  --w: var(--device-w);
  width: var(--w);
  aspect-ratio: 440 / 956;      /* iPhone 17 Pro Max, in points */
  margin: 0 auto;
  padding: calc(var(--w) * 0.028);
  border-radius: calc(var(--w) * 0.155);
  background: linear-gradient(160deg, #2a2a33, #0d0d12 55%, #24242c);
  box-shadow: 0 40px 90px rgba(0, 0, 0, 0.6), inset 0 0 0 1px rgba(255, 255, 255, 0.09);
}
.device .screen {
  position: relative; height: 100%; overflow: hidden;
  border-radius: calc(var(--w) * 0.13);
  background: var(--base);
  /* the same glow the app paints behind the Backup tab */
  background-image:
    radial-gradient(120% 70% at 42% -8%, var(--azure-core) 0%, rgba(23, 71, 199, 0.22) 45%, transparent 100%),
    radial-gradient(110% 60% at 97% 78%, rgba(77, 36, 153, 0.6) 0%, transparent 100%);
  padding: calc(var(--w) * 0.055) calc(var(--w) * 0.05);
  font-size: calc(var(--w) * 0.0345);
  color: var(--body);
}
.device .island {
  position: absolute; top: calc(var(--w) * 0.028); left: 50%; transform: translateX(-50%);
  width: 31%; height: calc(var(--w) * 0.075);
  background: #000; border-radius: 999px;
}
.device .statusbar {
  display: flex; justify-content: space-between; align-items: center;
  font-variant-numeric: tabular-nums; color: var(--ink);
  font-size: calc(var(--w) * 0.038); font-weight: 600;
  padding: 0 calc(var(--w) * 0.02) calc(var(--w) * 0.05);
}
.device .statusbar .icons { display: flex; gap: 5px; align-items: center; opacity: 0.9; }
.device .statusbar .battery {
  width: calc(var(--w) * 0.062); height: calc(var(--w) * 0.03);
  border: 1px solid rgba(255, 255, 255, 0.75); border-radius: 3px;
  position: relative;
}
.device .statusbar .battery::after {
  content: ""; position: absolute; inset: 1.5px; background: var(--ink); border-radius: 1.5px;
}
.device .screen-title {
  font-family: var(--display); font-weight: 700; color: var(--ink);
  font-size: calc(var(--w) * 0.088); margin: 0 0 calc(var(--w) * 0.045);
}
.device .glass {
  background: var(--panel); border: 1px solid var(--hairline);
  border-radius: calc(var(--w) * 0.075); padding: calc(var(--w) * 0.05);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
  backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
}

/* The running panel and the finished one share a place, so one can hand over to the other the
   way BackupView's hero does: out and in along the same path, scaling from 0.94 — critically
   damped here, where the app's bounces (see --spring-hero). The same transition runs backwards
   when the loop restarts, so the panel leaves the way it arrived. Visibility flips at the end of
   a hide and the start of a show, so the panel that is gone is not left painting a blur nobody
   can see. */
.device .panes { display: grid; }
.device .panes > .glass {
  grid-area: 1 / 1; align-self: start;
  transition:
    opacity var(--spring-hero-settle) var(--spring-hero),
    transform var(--spring-hero-settle) var(--spring-hero),
    visibility 0s linear 0s;
}
.device[data-state="running"] [data-done],
.device[data-state="done"] [data-running] {
  opacity: 0; transform: scale(0.94); visibility: hidden;
  transition-delay: 0s, 0s, var(--spring-hero-settle);
}
.device .row { display: flex; align-items: center; gap: calc(var(--w) * 0.025); }
.device .marker {
  font-size: calc(var(--w) * 0.031); font-weight: 600; letter-spacing: 0.1167em;
  text-transform: uppercase; color: var(--quiet);
}
.device .rate { margin-left: auto; font-variant-numeric: tabular-nums; color: var(--quiet); font-size: calc(var(--w) * 0.034); }
.device .count {
  display: block; font-weight: 300; color: var(--ink);
  font-size: calc(var(--w) * 0.2); line-height: 1.05;
  font-variant-numeric: tabular-nums;
  margin-top: calc(var(--w) * 0.035);
}
/* Sans and light while it ticks; serif once it is a result, as Aurora.Font.serifFigure. */
.device .count.serif { font-family: var(--display); font-weight: 700; }
.device .of { display: block; color: var(--body); font-size: calc(var(--w) * 0.042); }
.device .track {
  height: calc(var(--w) * 0.022); border-radius: 999px;
  background: rgba(255, 255, 255, 0.1);
  margin: calc(var(--w) * 0.05) 0 calc(var(--w) * 0.04);
  overflow: hidden;
}
/* Full width and slid in from the left, rather than grown: a transform is composited, where a
   width is laid out again on every frame of the run. The track clips the part still to come. */
.device .track span {
  display: block; height: 100%; width: 100%;
  transform: translateX(-100%);
  border-radius: 999px;
  background: linear-gradient(90deg, var(--azure-core), var(--azure-accent));
  box-shadow: 0 0 12px rgba(140, 199, 255, 0.55);
  will-change: transform;
}
.device .file {
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: calc(var(--w) * 0.033); color: var(--quiet);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.device .status { font-variant-numeric: tabular-nums; font-size: calc(var(--w) * 0.034); color: var(--faint); margin-top: calc(var(--w) * 0.012); }
.device .stop {
  margin-top: calc(var(--w) * 0.05); text-align: center;
  border: 1px solid rgba(255, 158, 153, 0.45); color: var(--danger);
  border-radius: calc(var(--w) * 0.04); padding: calc(var(--w) * 0.035) 0;
  font-weight: 700; font-size: calc(var(--w) * 0.042);
  background: rgba(255, 158, 153, 0.08);
}
.device .verdict { display: flex; align-items: center; gap: 8px; }
.device .verdict .marker { color: var(--good); }
.device + .caption {
  width: var(--device-w);
  margin: 14px auto 0;
  text-align: center;
  color: var(--faint);
  font-size: 0.8125rem;
}

/* The three working bars the app animates beside "Backing up". Full height and scaled from the
   bottom, like the progress bar: animating the height itself laid the row out again on every
   frame, for as long as the hero was on screen. */
.bars { display: flex; align-items: flex-end; gap: 2px; height: calc(var(--w) * 0.038); }
.bars i {
  width: calc(var(--w) * 0.011); height: 100%; background: var(--azure-accent); border-radius: 1px;
  transform-origin: bottom;
  animation: bar 1s ease-in-out infinite;
}
.bars i:nth-child(2) { animation-delay: 0.18s; }
.bars i:nth-child(3) { animation-delay: 0.36s; }
@keyframes bar { 0%, 100% { transform: scaleY(0.28); } 50% { transform: scaleY(1); } }
.device[data-paused] .bars i { animation-play-state: paused; }
/* Under Reduce Motion the demo opens already finished, and that one change of state should be
   a cut, not a spring the reader asked not to see. */
@media (prefers-reduced-motion: reduce) {
  .bars i { animation: none; transform: scaleY(0.64); }
  .device .panes > .glass { transition: none; }
}

/* Screenshots further down: exported at twice their rendered width so a retina
   display has pixels to work with, which the first attempt did not. */
.shots img { width: min(280px, 44vw); height: auto; }

header.hero .split > div { container-type: inline-size; }
header.hero h1 { font-size: clamp(2rem, 11cqw, 3.75rem); }
@media (max-width: 860px) { header.hero h1 { font-size: clamp(2.125rem, 8vw, 3.25rem); } }

/* ── A section that pairs an explanation with the screen it describes ─────── */
.feature {
  display: grid; gap: clamp(26px, 4.5vw, 58px); align-items: center;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
}
.feature.reverse > figure { order: -1; }
.feature figure { margin: 0; text-align: center; }
.feature figure img {
  width: min(300px, 74vw); height: auto;
  border-radius: 30px; border: 1px solid var(--hairline);
  box-shadow: 0 34px 80px rgba(0, 0, 0, 0.55);
}
.feature figcaption { color: var(--faint); font-size: 0.8125rem; margin-top: 14px; max-width: 30em; margin-inline: auto; }
.feature .grid.two { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); margin-top: 22px; }
.feature .panel { padding: 22px; }
.feature > div > p { max-width: 38em; }
@media (max-width: 860px) {
  .feature { grid-template-columns: 1fr; }
  .feature.reverse > figure { order: 0; }
}

/* ── The film ─────────────────────────────────────────────────────────────────
   Framed like the screenshots — hairline, deep shadow — at the full width of the column. The
   width and height on the element hold its 16:9 space before anything has loaded, so the page
   does not jump when the poster arrives. */
.film { margin: 22px 0 0; }
.film video {
  display: block; width: 100%; height: auto;
  border-radius: var(--radius);
  border: 1px solid var(--hairline);
  box-shadow: 0 34px 80px rgba(0, 0, 0, 0.55);
  background: var(--base);
}
.film figcaption { color: var(--faint); font-size: 0.8125rem; margin-top: 14px; text-align: center; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* ── Reduce Transparency, for the surfaces ─────────────────────────────────────
   Last in the file so it outranks every surface above it. Frosted to solid, blur dropped: the
   same hierarchy — panel over page, button over panel — with nothing showing through. */
@media (prefers-reduced-transparency: reduce) {
  .panel, .device .glass { background: #15151b; backdrop-filter: none; -webkit-backdrop-filter: none; }
  nav.top::before { background: var(--base); backdrop-filter: none; -webkit-backdrop-filter: none; }
  .cta { background: #2f5fc0; backdrop-filter: none; -webkit-backdrop-filter: none; }
  .cta.quiet { background: #1c1c23; }
}
