/*
 * Orate — site styles. v2, "Bright stage".
 *
 * The website is built the way the app is: from a small set of components with the app's names,
 * arranged by the app's recipes, in the app's tokens. `docs/design-system.md` is the rulebook and it
 * binds this file the way it binds a screen. How the rules land on a web page:
 *
 *   - Components, not one-off boxes. A card, a row with its icon tile, a pill, a hero card, a stat
 *     tile, a sheet, a notice, a badge circle — the same parts as `Components.swift` and
 *     `OrateComponents.kt`. A page that needs a new one adds it here, under its app name.
 *   - Recipes, not layouts invented per page. The landing page opens on the welcome screen's
 *     spotlight stage; claims are the store posters (a pale tint, one deeper circle); the controls
 *     are the prompter sheet; languages are script rows; the closing ask and the 404 are the empty
 *     state. Documents (privacy, terms, support) are the settings recipe: a label over a card of rows.
 *   - The spotlight yellow is a fill under ink, at most one big yellow thing per screen. Ink is the
 *     commit colour. Red is recording, and only on the drawn recorder. Figtree only.
 *   - No gradients, glows, grain, left-border cards or hairline-only lists. Depth is the card shadow.
 *     The recorder is the exception the apps make too: its backdrop is video, so its scrims fade.
 *
 * Colour, space, radius and type come from `generated/`, written by `design/build.js` from the
 * tokens the Swift and Kotlin themes come from. Light and dark are two resolutions of one set of
 * custom properties, chosen by the visitor's system or by the switch in the masthead (`theme.js`
 * sets `data-theme` on the root); nothing here branches on the scheme except the QR image, which
 * cannot read a custom property.
 *
 * Every effect is CSS. The three scripts on this site are analytics; see web/README.md.
 *
 * The two generated layers are `<link>`ed in every page head, ahead of this file, rather than
 * `@import`ed from it — an `@import` is invisible to the HTML parser and cost two serialised,
 * render-blocking round-trips. A new page needs all three links.
 */

/*
 * Figtree, self-hosted. It came from fonts.googleapis.com, which put two extra origins (the CSS on
 * one, the file on another) in a serial chain in front of first paint — the largest single
 * render-blocking cost Lighthouse found on every page — and handed every visitor's IP to Google.
 * Both files are the variable font Google serves (one file covers 500–900), split the same way:
 * `latin` is preloaded in every page head, `latin-ext` loads only when a page uses one of its
 * characters. Re-download from the URLs in the Google Fonts CSS if Figtree ever needs updating.
 */
@font-face {
  font-family: 'Figtree';
  font-style: normal;
  font-weight: 500 900;
  font-display: swap;
  src: url(/assets/fonts/figtree-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: 'Figtree';
  font-style: normal;
  font-weight: 500 900;
  font-display: swap;
  src: url(/assets/fonts/figtree-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;
}

/* ---------------------------------------------------------------- reset ---- */

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

body,
h1,
h2,
h3,
p,
ul,
dl,
dd,
figure,
blockquote {
  margin: 0;
}

ul {
  padding: 0;
  list-style: none;
}

/* --------------------------------------------------------------- design ---- */

:root {
  /* Figtree is Latin-only. Every other script falls back to the system face, as in the apps. */
  --font: "Figtree", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-code: ui-monospace, "SFMono-Regular", Menlo, monospace;

  /* Fluid page sizes, anchored on the app's roles: never below the role the app uses for the same
     job, growing where a wide screen has room a phone does not. */
  --step-hero: clamp(2.75rem, 1.2rem + 5vw, 5.25rem);
  --step-display: clamp(var(--type-title-l-size), 1.2rem + 2.6vw, 3.25rem);
  --step-title: clamp(var(--type-title-m-size), 1.1rem + 1vw, 1.875rem);
  --step-lede: clamp(var(--type-body-size), 1rem + 0.35vw, 1.25rem);

  --measure: 62ch;
  --gutter: clamp(var(--space-5), 3.5vw, var(--space-10));
  --stack: clamp(var(--space-11), 8vw, 7.5rem);

  /*
   * The card shadow, the only depth there is. Ink at very low opacity, as `orateCard` draws it: it
   * lifts a white card off the grey canvas and vanishes on the dark one, where the surfaces carry
   * the separation. `surface-scrim` is ink in light and black in dark.
   */
  --shadow-card:
    0 1px 2px color-mix(in srgb, var(--color-surface-scrim) 4%, transparent),
    0 8px 24px color-mix(in srgb, var(--color-surface-scrim) 5%, transparent);
  --shadow-lift:
    0 2px 6px color-mix(in srgb, var(--color-surface-scrim) 6%, transparent),
    0 24px 48px -18px color-mix(in srgb, var(--color-surface-scrim) 24%, transparent);

  /* The white ring on a hero card: `.white.opacity(0.3)` in `HeroCard`. */
  --ring: color-mix(in srgb, var(--color-always-dark-text) 30%, transparent);

  /* Recorder overlays: translucent always-dark ink and white, never a hex. */
  --rec-glass: color-mix(in srgb, var(--color-always-dark-surface) 82%, transparent);
  --rec-disc: color-mix(in srgb, var(--color-always-dark-text) 16%, transparent);
  --rec-faint: color-mix(in srgb, var(--color-always-dark-text) 22%, transparent);
}

/* ----------------------------------------------------------------- base ---- */

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* In-page anchors land below the floating masthead rather than under it. */
  scroll-padding-top: 6rem;
}

body {
  background: var(--color-surface-canvas);
  color: var(--color-text-primary);
  font-family: var(--font);
  font-size: var(--type-body-size);
  font-weight: var(--type-body-weight);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  font-variant-numeric: tabular-nums;
  overflow-x: hidden;
}

h1,
h2,
h3 {
  font-weight: 800;
  line-height: 1.06;
  letter-spacing: -0.03em;
  text-wrap: balance;
}

h1 {
  font-weight: var(--type-display-xl-weight);
}

strong,
b {
  font-weight: 700;
}

/* Ink, underlined. A link is never brand-coloured text. */
a {
  color: var(--color-text-primary);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

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

/* Focus is an ink outline, never a colour glow. */
:focus-visible {
  outline: 2px solid var(--color-border-focus);
  outline-offset: 3px;
  border-radius: var(--radius-sm);
}

.wrap {
  width: min(76rem, 100%);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* ================================================================ components */

/* `SectionLabel`: micro, uppercased, secondary. Above a group, never a sentence. */
.label {
  font-size: var(--type-micro-size);
  line-height: var(--type-micro-leading);
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
}

.lede {
  font-size: var(--step-lede);
  line-height: 1.55;
  color: var(--color-text-secondary);
  max-width: var(--measure);
}

/* ---- buttons: `PillButton` ---- */

/*
 * The site's buttons are the compact size (44 tall, hugging the label): they sit in headers, cards
 * and banners, where the app uses the compact size too. Ink is the next step; secondary is a raised
 * surface with a hairline, never a second fill.
 */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  min-height: 44px;
  padding-inline: var(--space-6);
  border-radius: var(--radius-full);
  border: 0;
  font-family: var(--font);
  font-size: var(--type-label-size);
  font-weight: 800;
  line-height: 1.2;
  text-decoration: none;
  transition: transform 0.14s ease, background-color 0.2s ease;
}

.btn svg {
  flex: none;
  width: 1.1rem;
  height: 1.1rem;
}

.btn--primary {
  background: var(--color-accent-default);
  color: var(--color-text-on-accent);
}

.btn--primary:hover {
  background: var(--color-accent-pressed);
  color: var(--color-text-on-accent);
  transform: translateY(-1px);
}

.btn--quiet,
.btn:not(.btn--primary) {
  background: var(--color-surface-raised);
  box-shadow: inset 0 0 0 1.5px var(--color-border-strong);
  color: var(--color-text-primary);
}

.btn--quiet:hover,
.btn:not(.btn--primary):hover {
  background: var(--color-surface-sunken);
  color: var(--color-text-primary);
}

/* ---- `Pill`: a short fact ---- */

.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 1.75rem;
  padding: 0.2rem 0.75rem;
  border-radius: var(--radius-full);
  background: var(--color-surface-sunken);
  color: var(--color-text-primary);
  font-size: var(--type-caption-size);
  font-weight: 800;
  line-height: 1.3;
  white-space: nowrap;
}

.pill svg {
  width: 0.95rem;
  height: 0.95rem;
  flex: none;
}

/* On a hero or a poster: the raised surface, so it reads as a chip lying on the colour. */
.pill--raised {
  background: var(--color-surface-raised);
  box-shadow: var(--shadow-card);
}

/* On the spotlight: the translucent white the app's hero pills use. */
.pill--wash {
  background: var(--ring);
  color: var(--color-spotlight-on-fill);
}

/* ---- `IconTile`, and the five tints ---- */

.tile {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 44px;
  height: 44px;
  border-radius: var(--radius-md);
  font-size: 1.25rem;
  font-weight: 800;
  line-height: 1;
}

.tile svg {
  width: 22px;
  height: 22px;
}

.tile--s {
  width: 32px;
  height: 32px;
  border-radius: 10px;
}

.tile--s svg {
  width: 18px;
  height: 18px;
}

.tile--lg {
  width: 56px;
  height: 56px;
  border-radius: 18px;
}

.tile--lg svg {
  width: 28px;
  height: 28px;
}

.tint-sun { background: var(--color-tint-sun-background); color: var(--color-tint-sun-foreground); }
.tint-lilac { background: var(--color-tint-lilac-background); color: var(--color-tint-lilac-foreground); }
.tint-mint { background: var(--color-tint-mint-background); color: var(--color-tint-mint-foreground); }
.tint-sky { background: var(--color-tint-sky-background); color: var(--color-tint-sky-foreground); }
.tint-peach { background: var(--color-tint-peach-background); color: var(--color-tint-peach-foreground); }

/* A tile on a tinted poster stands out by being raised rather than by repeating its own tint. */
.poster > .tile,
.poster__body > .tile {
  background: var(--color-surface-raised);
  box-shadow: var(--shadow-card);
}

/* ---- `orateCard` ---- */

.card,
.rig,
.faq,
.post-list,
.post-cta,
.table-scroll,
.guard,
.langs,
.sheet,
.qr,
.stat,
.rigs__item {
  background: var(--color-surface-raised);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
}

/* ---- `CardRow`: tile, title, subtitle, trailing ---- */

.row {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  min-height: 56px;
}

.row__text {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  flex: 1;
  min-width: 0;
}

.row__title {
  font-size: var(--type-title-s-size);
  line-height: var(--type-title-s-leading);
  font-weight: 700;
  color: var(--color-text-primary);
}

.row__sub {
  font-size: var(--type-caption-size);
  line-height: 1.5;
  font-weight: 500;
  color: var(--color-text-secondary);
}

.row__trail {
  flex: none;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--color-text-tertiary);
}

/* `CardDivider`, inset to the text, between rows that share a card. */
.rows > li + li,
.notices > li + li {
  border-top: 1px solid var(--color-border-subtle);
}

/* ---- `HeroCard` / the stage: spotlight, ink on it, the white ring ---- */

.hero-card {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-xl);
  background: var(--color-spotlight-fill);
  color: var(--color-spotlight-on-fill);
}

.hero-card::before {
  content: "";
  position: absolute;
  pointer-events: none;
  width: 28rem;
  height: 28rem;
  top: -12rem;
  right: -9rem;
  border-radius: var(--radius-full);
  border: 3rem solid var(--ring);
}

/* ---- `BadgeCircle`: top of an empty state ---- */

.badge-circle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 6rem;
  height: 6rem;
  border-radius: var(--radius-full);
  background: var(--color-spotlight-fill);
  color: var(--color-spotlight-on-fill);
}

.badge-circle svg {
  width: 3.25rem;
  height: 3.25rem;
}

/* The tinted variant, for documents: a page's own glyph on its tint. */
.badge-circle[class*="tint-"] svg {
  width: 2.5rem;
  height: 2.5rem;
}

/* ---- stat tile: a number as the headline ---- */

.stat {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-6);
}

.stat b {
  font-size: clamp(var(--type-display-l-size), 1.6rem + 2.2vw, var(--type-display-xl-size));
  line-height: 1;
  font-weight: 900;
  letter-spacing: -0.04em;
}

.stat__cap {
  font-size: var(--type-caption-size);
  line-height: 1.5;
  font-weight: 600;
  color: var(--color-text-secondary);
}

.stats {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-5);
}

@media (max-width: 56rem) {
  .stats {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* ---- store badges ---- */

/*
 * Apple's and Google's own artwork, not an imitation; see web/README.md for where these files came
 * from. Neither image is ever edited. Google's PNG has clear space baked in while Apple's SVG does
 * not, so the Google image is taller with a negative margin that cancels its padding — layout on
 * our side, not a change to their asset.
 */
.store-badges {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4);
}

.store-badges a {
  display: inline-flex;
  border-radius: 10px;
  transition: transform 0.14s ease, opacity 0.2s ease;
}

.store-badges a:hover {
  transform: translateY(-2px);
  opacity: 0.88;
}

.store-badges img {
  display: block;
  width: auto;
}

.badge-apple {
  height: 48px;
}

.badge-google {
  height: 70px;
  margin: -11px;
}

/*
 * The App Store badge, waiting. The listing does not exist yet, so the badge is not a link — greyed
 * rather than removed, because the row says which platforms this runs on. The label is what stops
 * the grey reading as a broken image.
 */
.badge-soon {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  border-radius: 10px;
}

.badge-soon img {
  opacity: 0.45;
  filter: grayscale(1);
}

.badge-soon-label {
  font-size: var(--type-caption-size);
  font-weight: 600;
  color: var(--color-text-secondary);
  white-space: nowrap;
}

/* The objections removed before the tap, as pills. */
.trust {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

.trust li {
  display: inline-flex;
  align-items: center;
  min-height: 2rem;
  padding: 0.3rem 0.9rem;
  border-radius: var(--radius-full);
  background: var(--color-surface-raised);
  box-shadow: var(--shadow-card);
  font-size: var(--type-caption-size);
  font-weight: 800;
  color: var(--color-text-primary);
}

.trust a {
  text-decoration: none;
}

/* ================================================================== chrome */

/*
 * The masthead: a floating white pill, like the app's tab bar, held at the top of the window. The
 * wrap it sits in is the sticky element — a sticky child is bounded by its parent, and the parent
 * here is exactly as tall as the bar.
 */
.wrap:has(> .top) {
  position: sticky;
  top: var(--space-4);
  z-index: 40;
  margin-top: var(--space-4);
}

.top {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  min-height: 64px;
  padding: var(--space-2) var(--space-2) var(--space-2) var(--space-6);
  border-radius: var(--radius-full);
  background: var(--color-surface-raised);
  box-shadow: var(--shadow-lift);
}

.wordmark {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-weight: 800;
  font-size: 1.375rem;
  letter-spacing: -0.03em;
  color: var(--color-text-primary);
  text-decoration: none;
}

.wordmark:hover {
  color: var(--color-text-primary);
}

.wordmark svg {
  display: block;
  width: 2rem;
  height: 2rem;
}

.top nav {
  margin-left: auto;
  display: flex;
  gap: var(--space-1);
  align-items: center;
}

/*
 * `:not(.btn)` is load-bearing. Written as `.top nav a` this rule is a class, a class and a type —
 * which outranks `.btn--primary`'s single class — and once overwrote the button's own label colour,
 * so the most prominent control on every page read as disabled.
 */
.top nav a:not(.btn) {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding-inline: var(--space-5);
  border-radius: var(--radius-full);
  font-size: var(--type-label-size);
  font-weight: 700;
  color: var(--color-text-secondary);
  text-decoration: none;
  transition: background-color 0.18s ease, color 0.18s ease;
}

.top nav a:not(.btn):hover {
  background: var(--color-surface-sunken);
  color: var(--color-text-primary);
}

.top .btn {
  margin-left: var(--space-2);
}

/*
 * The theme switch: Light and Dark as a segmented control at icon size, a sunken track with the
 * chosen side raised. Until a visitor picks one, the side the system shows is the raised one. It ships `hidden` and `theme.js` reveals it, so with scripts off there is no
 * control that does nothing and the page follows the system.
 */
.theme-switch:not([hidden]) {
  display: inline-flex;
  gap: 2px;
  margin-left: var(--space-2);
  padding: 3px;
  border-radius: var(--radius-full);
  background: var(--color-surface-sunken);
}

.theme-switch button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-full);
  background: transparent;
  color: var(--color-text-secondary);
  cursor: pointer;
  transition: background-color 0.18s ease, color 0.18s ease;
}

.theme-switch button:hover {
  color: var(--color-text-primary);
}

.theme-switch button[aria-pressed="true"] {
  background: var(--color-surface-raised);
  color: var(--color-text-primary);
  box-shadow: var(--shadow-card);
}

.theme-switch svg {
  width: 18px;
  height: 18px;
}

@media (max-width: 46rem) {
  .top nav a:not(.btn) {
    display: none;
  }

  .top {
    padding-left: var(--space-5);
  }
}

/* The footer: one white card of a brand column and four link columns. */
footer {
  margin-block: var(--stack) var(--space-6);
  padding: clamp(var(--space-7), 4vw, var(--space-10));
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) repeat(4, minmax(0, 1fr));
  gap: var(--space-10) var(--space-8);
  border-radius: var(--radius-xl);
  background: var(--color-surface-raised);
  box-shadow: var(--shadow-card);
  color: var(--color-text-secondary);
  font-size: var(--type-label-size);
  font-weight: 600;
}

footer a {
  color: var(--color-text-secondary);
  text-decoration: none;
}

footer a:hover {
  color: var(--color-text-primary);
}

.footer-brand {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-5);
}

.footer-brand p {
  margin: 0;
  max-width: 28ch;
}

footer .wordmark {
  color: var(--color-text-primary);
}

/* The social profiles as pills. */
.footer-social {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.footer-social a {
  padding: 0.35rem 0.85rem;
  border-radius: var(--radius-full);
  background: var(--color-surface-sunken);
  color: var(--color-text-primary);
  font-size: var(--type-caption-size);
  font-weight: 800;
}

.footer-social a:hover {
  background: var(--color-border-strong);
}

.footer-legal {
  margin-top: auto;
  padding-top: var(--space-5);
  font-size: var(--type-caption-size);
}

.footer-col {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.footer-col .label {
  margin: 0 0 var(--space-1);
}

@media (max-width: 62rem) {
  footer {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .footer-brand {
    grid-column: 1 / -1;
  }
}

/* ========================================================== the landing page */

section {
  padding-top: var(--stack);
}

.section-head {
  display: grid;
  gap: var(--space-4);
  margin-bottom: clamp(var(--space-8), 4vw, var(--space-10));
  max-width: var(--measure);
}

.section-head h2 {
  font-size: var(--step-display);
}

/* A head beside its content rather than above it, held in view while the content scrolls. */
.split {
  display: grid;
  grid-template-columns: minmax(0, 0.8fr) minmax(0, 1fr);
  gap: clamp(var(--space-8), 5vw, var(--space-12));
  align-items: start;
}

.split__head {
  position: sticky;
  top: 7rem;
  margin-bottom: 0;
}

@media (max-width: 60rem) {
  .split {
    grid-template-columns: 1fr;
  }

  .split__head {
    position: static;
  }
}

/* ---- the stage ---- */

/*
 * The welcome screen, at page size: a spotlight panel with the white ring, ink on it, and the
 * product standing on its lower edge. The phones run off the bottom on purpose — it says there is
 * more without spending a screen saying so.
 */
.stage {
  position: relative;
  overflow: hidden;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.95fr);
  gap: var(--space-8);
  margin-top: var(--space-6);
  padding: clamp(var(--space-8), 5vw, var(--space-12)) clamp(var(--space-6), 5vw, var(--space-12)) 0;
  min-height: 40rem;
  border-radius: var(--radius-xl);
  background: var(--color-spotlight-fill);
  color: var(--color-spotlight-on-fill);
}

.stage::before {
  content: "";
  position: absolute;
  pointer-events: none;
  width: 40rem;
  height: 40rem;
  right: -8rem;
  top: -4rem;
  border-radius: var(--radius-full);
  border: 4rem solid var(--ring);
}

.stage__copy {
  position: relative;
  z-index: 1;
  display: grid;
  align-content: center;
  justify-items: start;
  gap: var(--space-6);
  padding-bottom: clamp(var(--space-8), 5vw, var(--space-12));
}

.stage h1 {
  font-size: var(--step-hero);
  line-height: 0.98;
  letter-spacing: -0.045em;
  max-width: 11ch;
}

.stage__lede {
  max-width: 34ch;
  font-size: var(--step-lede);
  line-height: 1.5;
  font-weight: 600;
}

/* On the spotlight the trust pills are the raised white chips; the badge label goes ink. */
.stage .badge-soon-label {
  color: var(--color-spotlight-on-fill);
}

.stage .badge-soon img {
  opacity: 0.55;
}

.stage .trust li {
  background: var(--ring);
  color: var(--color-spotlight-on-fill);
  box-shadow: none;
}

.stage__phones {
  position: relative;
  z-index: 1;
  align-self: end;
  height: 36rem;
}

.stage__phones .device {
  position: absolute;
  bottom: -9rem;
  height: 40rem;
}

.stage__phones .device--front {
  left: 4%;
  z-index: 2;
  rotate: -4deg;
}

.stage__phones .device--back {
  left: 44%;
  bottom: -11rem;
  z-index: 1;
  rotate: 5deg;
}

@media (prefers-reduced-motion: no-preference) {
  @keyframes rise-phone {
    from { opacity: 0; translate: 0 10%; }
  }

  .stage__phones .device--front {
    animation: rise-phone 1s cubic-bezier(0.2, 0.7, 0.2, 1) 0.2s both;
  }

  .stage__phones .device--back {
    animation: rise-phone 1s cubic-bezier(0.2, 0.7, 0.2, 1) 0.32s both;
  }
}

@media (max-width: 60rem) {
  .stage {
    grid-template-columns: 1fr;
    min-height: 0;
    gap: 0;
  }

  .stage__copy {
    justify-items: center;
    text-align: center;
    padding-bottom: var(--space-8);
  }

  .stage__copy .store-badges,
  .stage__copy .trust {
    justify-content: center;
  }

  .stage__phones {
    height: clamp(22rem, 80vw, 30rem);
    justify-self: center;
    width: min(30rem, 100%);
  }

  .stage__phones .device {
    height: clamp(26rem, 96vw, 36rem);
    bottom: -8rem;
  }

  .stage__phones .device--back {
    bottom: -10rem;
    left: 42%;
  }
}

/* ---- the free tier, as numbers ---- */

.numbers {
  padding-top: var(--space-6);
}

.numbers__title {
  margin: var(--space-6) 0 var(--space-5);
  font-size: var(--type-title-m-size);
  letter-spacing: -0.02em;
}

/* ---- the posters ---- */

/*
 * A claim per poster, the store listings' recipe: a pale token tint, one flat circle a few steps
 * deeper in the same tint, an ink headline, and the proof on a white panel. Each tint is the
 * `IconTile` tint of what the claim is about.
 */
.poster {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-4);
  padding: clamp(var(--space-6), 3vw, var(--space-9));
  border-radius: var(--radius-xl);
}

.poster::before {
  content: "";
  position: absolute;
  pointer-events: none;
  width: 22rem;
  height: 22rem;
  right: -7rem;
  top: -8rem;
  border-radius: var(--radius-full);
  background: color-mix(in srgb, currentColor 12%, transparent);
}

.poster > *,
.poster__body {
  position: relative;
}

.poster h3 {
  margin-top: var(--space-2);
  font-size: var(--step-title);
  line-height: 1.12;
  max-width: 22ch;
  color: var(--color-text-primary);
}

.poster > p,
.poster__body > p {
  color: var(--color-text-primary);
  opacity: 0.8;
  max-width: 46ch;
}

/* What sits on a poster's white panels takes the page's ink, not the tint the poster is drawn in. */
.poster .spec,
.poster .versus,
.poster__panel {
  color: var(--color-text-primary);
}

.poster .versus figcaption {
  margin-top: var(--space-5);
  text-align: center;
  font-size: var(--type-caption-size);
  font-weight: 600;
  color: var(--color-text-primary);
  opacity: 0.8;
}

.poster__panel {
  align-self: stretch;
  flex: 1;
  display: flex;
  flex-direction: column;
  margin-top: var(--space-3);
  padding: var(--space-5);
  border-radius: var(--radius-lg);
  background: var(--color-surface-raised);
  box-shadow: var(--shadow-card);
}


.poster__panel figcaption {
  margin-top: auto;
  padding-top: var(--space-5);
  font-size: var(--type-caption-size);
  line-height: 1.5;
  font-weight: 500;
  color: var(--color-text-secondary);
}

.poster .pill {
  margin-top: var(--space-2);
}

.poster--wide {
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1fr);
  gap: clamp(var(--space-7), 4vw, var(--space-11));
  align-items: center;
  margin-bottom: var(--space-6);
}

.poster--wide::before {
  width: 34rem;
  height: 34rem;
  right: -6rem;
  top: -10rem;
}

.poster__body {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-4);
}

.posters {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-6);
}

@media (max-width: 62rem) {
  .posters {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 56rem) {
  .poster--wide {
    grid-template-columns: 1fr;
  }
}

/* The three figures behind the thesis, as rows on a white panel; numbers as headlines. */
.spec {
  align-self: stretch;
  margin-top: var(--space-3);
  padding: var(--space-2) var(--space-5);
  border-radius: var(--radius-lg);
  background: var(--color-surface-raised);
  box-shadow: var(--shadow-card);
}

.spec > div {
  display: flex;
  align-items: baseline;
  gap: var(--space-5);
  padding-block: var(--space-4);
}

.spec > div + div {
  border-top: 1px solid var(--color-border-subtle);
}

.spec dt {
  flex: 0 0 6.5rem;
  font-size: var(--type-title-m-size);
  font-weight: 900;
  letter-spacing: -0.03em;
}

.spec dd {
  font-size: var(--type-caption-size);
  font-weight: 600;
  color: var(--color-text-secondary);
}

/* ---- the three screens ---- */

.tour {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-6);
}

.tour figure {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  margin: 0;
}

/* Each handset stands on its own tinted stage, cropped at the bottom like a store poster. */
.tour__stage {
  position: relative;
  overflow: hidden;
  display: grid;
  justify-items: center;
  height: 30rem;
  padding-top: var(--space-8);
  border-radius: var(--radius-xl);
}

.tour__stage::before {
  content: "";
  position: absolute;
  width: 18rem;
  height: 18rem;
  left: -5rem;
  bottom: -6rem;
  border-radius: var(--radius-full);
  background: color-mix(in srgb, currentColor 14%, transparent);
}

.tour .device {
  position: relative;
  height: 34rem;
}

.tour figcaption {
  align-items: flex-start;
  padding-inline: var(--space-2);
}

@media (max-width: 62rem) {
  .tour {
    grid-template-columns: 1fr;
    max-width: 28rem;
    margin-inline: auto;
  }
}

/* ---- every size ---- */

.rigs {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-5);
}

.rigs__item {
  margin: 0;
  display: flex;
  flex-direction: column;
  padding: var(--space-6);
}

/* The shapes stand on a shared baseline, so their true relative sizes are the comparison. */
.rigs__stage {
  display: grid;
  align-items: end;
  justify-items: center;
  min-height: 15.5rem;
  padding-bottom: var(--space-6);
}

.rigs figcaption {
  display: grid;
  gap: var(--space-2);
}

.rigs figcaption b {
  font-size: var(--type-title-s-size);
  line-height: var(--type-title-s-leading);
}

.rigs figcaption em {
  justify-self: start;
  padding: 0.1rem 0.6rem;
  border-radius: var(--radius-full);
  background: var(--color-surface-sunken);
  font-style: normal;
  font-size: var(--type-micro-size);
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
}

.rigs figcaption span {
  font-size: var(--type-caption-size);
  line-height: 1.5;
  font-weight: 500;
  color: var(--color-text-secondary);
}

.rigs__note {
  margin-top: var(--space-6);
  font-size: var(--type-caption-size);
  line-height: 1.6;
  font-weight: 500;
  color: var(--color-text-secondary);
  max-width: 64ch;
}

@media (max-width: 62rem) {
  .rigs {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 34rem) {
  .rigs {
    grid-template-columns: 1fr;
  }

  .rigs__stage {
    min-height: 0;
  }
}

/* ---- the controls, as the prompter sheet ---- */

/*
 * The app's sheet: raised surface, `Radius.xl`, a grab handle, options as rows. Every control has
 * its real range and starts where `PrompterSettings.default` starts it — the thumb on each track is
 * the reason a track beats printing two numbers.
 */
.sheet {
  border-radius: var(--radius-xl);
  padding: var(--space-4) clamp(var(--space-6), 3vw, var(--space-8)) var(--space-4);
  box-shadow: var(--shadow-lift);
}

.sheet__grab {
  display: block;
  width: 40px;
  height: 5px;
  margin: 0 auto var(--space-4);
  border-radius: var(--radius-full);
  background: var(--color-border-strong);
}

.dial {
  padding-block: var(--space-6);
}

.dial + .dial {
  border-top: 1px solid var(--color-border-subtle);
}

.dial__head {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin-bottom: var(--space-5);
}

.dial__head b {
  flex: 1;
  font-size: var(--type-title-s-size);
}

.dial__track {
  position: relative;
  height: 8px;
  margin-inline: var(--space-2);
  border-radius: var(--radius-full);
  background: var(--color-surface-sunken);
}

.dial__track span {
  position: absolute;
  inset: 0 auto 0 0;
  width: var(--at);
  border-radius: inherit;
  background: var(--color-accent-default);
}

.dial__track span::after {
  content: "";
  position: absolute;
  top: 50%;
  right: 0;
  width: 26px;
  height: 26px;
  translate: 50% -50%;
  border-radius: var(--radius-full);
  background: var(--color-surface-raised);
  box-shadow: 0 0 0 1px var(--color-border-strong), var(--shadow-card);
}

/* The switch, drawn off: a grey track and a raised thumb. */
.dial__switch {
  position: relative;
  flex: none;
  width: 52px;
  height: 32px;
  border-radius: var(--radius-full);
  background: var(--color-border-strong);
}

.dial__switch::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 26px;
  height: 26px;
  border-radius: var(--radius-full);
  background: var(--color-surface-raised);
  box-shadow: var(--shadow-card);
}

.dial__at {
  margin-top: var(--space-5);
  font-size: var(--type-caption-size);
  font-weight: 800;
  color: var(--color-text-primary);
}

.dial--switch .dial__at {
  margin-top: 0;
}

.dial__note {
  margin-top: var(--space-1);
  font-size: var(--type-caption-size);
  line-height: 1.6;
  font-weight: 500;
  color: var(--color-text-secondary);
  max-width: 58ch;
}

/* ---- capture ---- */

.stats--three {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.stats--three .stat {
  gap: var(--space-4);
  padding: var(--space-7);
}

@media (max-width: 56rem) {
  .stats--three {
    grid-template-columns: 1fr;
  }
}

/*
 * One line, and it earns its place: the three capabilities above are the ones the free tier does
 * not include, so a page that advertises them and says nothing else promises 4K to everyone who
 * installs — the setup for a one-star review. See web/README.md.
 */
.specs__note {
  margin: var(--space-5) var(--space-2) 0;
  font-size: var(--type-caption-size);
  line-height: 1.6;
  font-weight: 600;
  color: var(--color-text-secondary);
  max-width: 64ch;
}

/* What the recorder does when the phone cannot manage: the notices it shows, each in the tint of
   what it is about, in one card. */
.guard {
  margin-top: var(--space-8);
  padding: var(--space-6) clamp(var(--space-6), 3vw, var(--space-8));
}

.notices {
  margin-top: var(--space-3);
}

.notices .row {
  align-items: flex-start;
  padding-block: var(--space-5);
}

.guard__note {
  margin-top: var(--space-2);
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-md);
  background: var(--color-surface-sunken);
  font-size: var(--type-caption-size);
  line-height: 1.6;
  font-weight: 500;
  color: var(--color-text-secondary);
}

/* ---- languages, as script rows ---- */

.langs {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0 var(--space-6);
  padding: var(--space-3) var(--space-6);
}

.lang {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding-block: var(--space-4);
  border-bottom: 1px solid var(--color-border-subtle);
}

/* The last row of each column sits on the card edge, not on a divider. */
.lang:nth-last-child(-n + 2) {
  border-bottom: 0;
}

/* English runs the full width. It earns the place — it is the line the other fourteen are
   translations of — and it is also what keeps fifteen cards from leaving one alone on the last row
   of a two-column grid: one wide, then seven even rows. If the count goes even again, this is the
   rule to remove, or it becomes the thing that makes the grid ragged. */
.lang--lead {
  grid-column: 1 / -1;
}

.lang[dir="rtl"] .row__sub {
  text-align: right;
}

.lang .row__title {
  font-size: var(--type-title-s-size);
  line-height: 1.35;
}

.langs__note {
  font-size: var(--type-caption-size);
  line-height: 1.7;
  font-weight: 500;
  color: var(--color-text-secondary);
}

@media (max-width: 34rem) {
  .langs {
    grid-template-columns: 1fr;
  }

  .lang:nth-last-child(2) {
    border-bottom: 1px solid var(--color-border-subtle);
  }
}

/* ---- the closing ask: the empty-state recipe ---- */

.close-cta {
  display: grid;
  justify-items: center;
  gap: var(--space-5);
  margin-top: var(--stack);
  padding: clamp(var(--space-10), 6vw, var(--space-12)) var(--gutter);
  text-align: center;
  border-radius: var(--radius-xl);
  background: var(--color-surface-raised);
  box-shadow: var(--shadow-card);
}

.close-cta h2 {
  font-size: var(--step-display);
  max-width: 20ch;
}

.close-cta .lede {
  max-width: 52ch;
}

.close-cta .store-badges,
.close-cta .trust {
  justify-content: center;
}

.close-cta .trust li {
  background: var(--color-surface-sunken);
  box-shadow: none;
}

/* ---- the QR card ---- */

/*
 * Desktop-to-phone is the friction that kills an app site: someone reads this on a laptop and the
 * app is on their phone. The code points at the site root so the phone picks its own store.
 */
.qr {
  position: fixed;
  left: var(--space-6);
  bottom: var(--space-6);
  z-index: 20;
  display: grid;
  gap: var(--space-2);
  justify-items: center;
  padding: var(--space-4);
  box-shadow: var(--shadow-lift);
  color: var(--color-text-primary);
  text-decoration: none;
  transition: transform 0.16s ease;
}

.qr:hover {
  transform: translateY(-2px);
  color: var(--color-text-primary);
}

.qr img {
  display: block;
  width: 72px;
  height: 72px;
}

/* An <img> cannot inherit `currentColor`, so the code is always black. On the dark theme it sits on
   a white quiet zone rather than being inverted: plenty of scanners read only dark on light. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .qr img {
    padding: 6px;
    border-radius: var(--radius-sm);
    background: var(--color-always-dark-text);
    box-sizing: content-box;
  }
}

/* The same, when the dark theme is chosen in the masthead rather than by the system. */
:root[data-theme="dark"] .qr img {
  padding: 6px;
  border-radius: var(--radius-sm);
  background: var(--color-always-dark-text);
  box-sizing: content-box;
}

.qr span {
  font-size: var(--type-micro-size);
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
}

@media (max-width: 64rem) {
  .qr {
    display: none;
  }
}


/* ================================================================ the handsets */

/* --------------------------------------------------------------- device ---- */

/*
 * A phone, at a size worth looking at, showing the real interface.
 *
 * The screens inside are drawn from the shipped Compose and SwiftUI code, element for element, at
 * the app's own sizes — the same drawings the store listings use (`store/assets/ios/screens.py`).
 * `--pt` is one point of a 393-point-wide phone, measured off the screen itself, so a handset drawn
 * at any size on this page shows the app at true proportion. The bezel is always dark, in both
 * themes, because that is what a phone is.
 */
.device {
  position: relative;
  height: min(40rem, 76vh);
  aspect-ratio: 9 / 19.5;
  margin-inline: auto;
  border-radius: 13.5% / 6.2%;
  padding: 2.4%;
  background: var(--color-always-dark-surface);
  box-shadow:
    0 0 0 1px var(--color-always-dark-border) inset,
    var(--shadow-lift);
}

.device__screen {
  position: relative;
  height: 100%;
  border-radius: 11.5% / 5.4%;
  overflow: hidden;
  background: var(--color-always-dark-surface);
  container-type: size;
  --pt: calc(100cqw / 393);
}

/* The camera preview when there is no footage behind it: an even always-dark field. The thirds
   guides are what make a rectangle read as a viewfinder. */
.device__preview {
  position: absolute;
  inset: 0;
  background: var(--color-always-dark-surface-raised);
}

.device__preview::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(90deg, transparent 33.2%, var(--rec-faint) 33.2% 33.45%, transparent 33.45%),
    linear-gradient(90deg, transparent 66.5%, var(--rec-faint) 66.5% 66.75%, transparent 66.75%);
  opacity: 0.4;
}

/*
 * Real footage behind the recorder chrome. Everything else on this page is either the app itself or
 * a diagram, and the one thing neither could supply was a face in the viewfinder — which is the
 * whole situation the product is for.
 */
.device__footage {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* The screen already clips, but a video paints outside a parent's radius in Safari. */
  border-radius: inherit;
}

/* Over the footage rather than instead of it: the recorder's own top and bottom scrims, which keep
   the white chrome legible over a bright room. The one fade on the site, and it is the app's. */
.device__preview--over {
  background:
    linear-gradient(color-mix(in srgb, var(--color-always-dark-surface) 55%, transparent), transparent 34%),
    linear-gradient(transparent 70%, color-mix(in srgb, var(--color-always-dark-surface) 78%, transparent));
}

.device__preview--over::after {
  display: none;
}

/*
 * The punch-hole. The lens the whole product is measured from, drawn where phones put it — at the
 * top edge, centred — and out of flow, so the prompter below sits where the app puts it.
 */
.device__lens {
  position: absolute;
  top: calc(16 * var(--pt));
  left: 50%;
  translate: -50% 0;
  z-index: 8;
  width: calc(14 * var(--pt));
  height: calc(14 * var(--pt));
  border-radius: var(--radius-full);
  background: var(--color-always-dark-surface);
  box-shadow: 0 0 0 calc(2 * var(--pt)) var(--color-always-dark-border);
}

/* The take timer: red, because a take is running — the one red thing outside the shutter. */
.device__timer {
  position: absolute;
  top: calc(52 * var(--pt));
  left: 50%;
  translate: -50% 0;
  z-index: 6;
  display: flex;
  align-items: center;
  gap: calc(8 * var(--pt));
  height: calc(40 * var(--pt));
  padding-inline: calc(16 * var(--pt));
  border-radius: var(--radius-full);
  background: var(--color-status-record);
  color: var(--color-always-dark-text);
  font-size: calc(19 * var(--pt));
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

.device__timer i {
  display: block;
  width: calc(8 * var(--pt));
  height: calc(8 * var(--pt));
  border-radius: var(--radius-full);
  background: var(--color-always-dark-text);
}

/*
 * The prompter, directly beneath the lens: `PrompterOverlay`, an always-dark glass panel with the
 * reading-line dot at its top and the script scrolling through it.
 */
.device__band {
  position: absolute;
  z-index: 6;
  top: calc(104 * var(--pt));
  left: calc(12 * var(--pt));
  right: calc(12 * var(--pt));
  height: 38%;
  border-radius: calc(32 * var(--pt));
  background: var(--rec-glass);
  overflow: hidden;
}

.device__band::before {
  content: "";
  position: absolute;
  z-index: 1;
  top: calc(12 * var(--pt));
  left: 50%;
  translate: -50% 0;
  width: calc(8 * var(--pt));
  height: calc(8 * var(--pt));
  border-radius: var(--radius-full);
  background: var(--color-always-dark-accent);
}

.device__band p {
  margin: 0;
  padding: calc(34 * var(--pt)) calc(22 * var(--pt)) 0;
  font-size: calc(27 * var(--pt));
  font-weight: 800;
  line-height: 1.34;
  letter-spacing: -0.01em;
  text-align: center;
  color: var(--color-prompter-default-text);
  mask-image: linear-gradient(transparent, #000 20%, #000 72%, transparent);
  animation: scroll-script 26s linear infinite;
}

/*
 * Starts with the script already legible rather than parked below the band. Beginning a full band
 * height down meant the centrepiece of the hero was an empty box for the first moment of the page —
 * and empty in any screenshot or social preview, which is where a lot of people see it first.
 */
@keyframes scroll-script {
  from { transform: translateY(0); }
  to { transform: translateY(-70%); }
}

/* The control row, Android's: slower, faster, the shutter, an empty slot, pause — and the level
   meter above it. */
.device__controls {
  position: absolute;
  z-index: 6;
  left: 0;
  right: 0;
  bottom: calc(40 * var(--pt));
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(18 * var(--pt));
}

.device__meter {
  display: block;
  width: calc(120 * var(--pt));
  height: calc(4 * var(--pt));
  border-radius: var(--radius-full);
  background: var(--rec-faint);
  overflow: hidden;
}

.device__meter i {
  display: block;
  width: 58%;
  height: 100%;
  background: var(--color-always-dark-success);
}

.device__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding-inline: calc(22 * var(--pt));
}

.device__disc {
  display: flex;
  align-items: center;
  justify-content: center;
  width: calc(52 * var(--pt));
  height: calc(52 * var(--pt));
  border-radius: var(--radius-full);
  background: var(--rec-disc);
  color: var(--color-always-dark-text);
}

.device__disc svg {
  width: calc(22 * var(--pt));
  height: calc(22 * var(--pt));
}

.device__disc--empty {
  background: none;
}

/* The shutter, mid-take: a white ring around the red stop square. */
.device__shutter {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: calc(84 * var(--pt));
  height: calc(84 * var(--pt));
  border-radius: var(--radius-full);
  border: calc(5 * var(--pt)) solid var(--color-always-dark-text);
}

.device__shutter::after {
  content: "";
  width: calc(32 * var(--pt));
  height: calc(32 * var(--pt));
  border-radius: calc(8 * var(--pt));
  background: var(--color-status-record);
}

/* ------------------------------------------------------ the app, drawn ---- */

/*
 * App chrome inside a handset: Scripts and Settings, from `LibraryScreen.kt` / `ScriptListView.swift`
 * and `SettingsScreen.kt` / `SettingsView.swift`. Semantic tokens only, so on a visitor whose system
 * is dark the handsets show the app's dark resolution — which is what that visitor would see.
 * Android's variants, since Google Play is where the app installs from today.
 */
.app {
  position: absolute;
  inset: 0;
  overflow: hidden;
  padding: 0 calc(16 * var(--pt));
  background: var(--color-surface-canvas);
  color: var(--color-text-primary);
  font-variant-numeric: tabular-nums;
  line-height: 1.2;
  text-align: left;
}

.app svg {
  flex: none;
  width: calc(22 * var(--pt));
  height: calc(22 * var(--pt));
}

.app__card {
  background: var(--color-surface-raised);
  border-radius: calc(24 * var(--pt));
  box-shadow: var(--shadow-card);
}

.app__col {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
  gap: calc(2 * var(--pt));
}

.app__col b {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-size: calc(17 * var(--pt));
  line-height: calc(22 * var(--pt));
}

.app__cap {
  font-size: calc(13 * var(--pt));
  line-height: calc(18 * var(--pt));
  font-weight: 500;
  color: var(--color-text-secondary);
}

.app__head {
  display: flex;
  align-items: center;
  gap: calc(10 * var(--pt));
  margin: calc(64 * var(--pt)) calc(4 * var(--pt)) calc(18 * var(--pt));
}

.app__title {
  flex: 1;
  font-size: calc(34 * var(--pt));
  line-height: calc(38 * var(--pt));
  font-weight: 800;
  letter-spacing: -0.025em;
}

.app__head .app__title {
  margin: 0;
}

.app > .app__title {
  display: block;
  margin: calc(64 * var(--pt)) calc(4 * var(--pt)) calc(18 * var(--pt));
}

.app__premium {
  display: inline-flex;
  align-items: center;
  gap: calc(6 * var(--pt));
  height: calc(32 * var(--pt));
  padding: 0 calc(12 * var(--pt));
  border-radius: var(--radius-full);
  background: var(--color-accent-default);
  color: var(--color-text-on-accent);
  font-size: calc(14 * var(--pt));
  font-weight: 700;
}

.app__premium svg {
  width: calc(15 * var(--pt));
  height: calc(15 * var(--pt));
}

.app__ib {
  display: flex;
  align-items: center;
  justify-content: center;
  width: calc(44 * var(--pt));
  height: calc(44 * var(--pt));
  border-radius: var(--radius-full);
  background: var(--color-surface-raised);
  box-shadow: var(--shadow-card);
}

/* `HeroCard`: the one yellow thing on the Scripts screen, the same in both themes. */
.app__hero {
  position: relative;
  overflow: hidden;
  margin-bottom: calc(12 * var(--pt));
  padding: calc(20 * var(--pt));
  border-radius: calc(32 * var(--pt));
  background: var(--color-spotlight-fill);
  color: var(--color-spotlight-on-fill);
}

.app__hero::after {
  content: "";
  position: absolute;
  top: calc(-80 * var(--pt));
  right: calc(-70 * var(--pt));
  width: calc(180 * var(--pt));
  height: calc(180 * var(--pt));
  border-radius: var(--radius-full);
  border: calc(26 * var(--pt)) solid color-mix(in srgb, var(--color-always-dark-text) 30%, transparent);
}

.app__kicker {
  display: block;
  font-size: calc(11 * var(--pt));
  line-height: calc(14 * var(--pt));
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-spotlight-on-fill);
}

.app__hero-title {
  display: block;
  max-width: calc(300 * var(--pt));
  margin-top: calc(6 * var(--pt));
  font-size: calc(28 * var(--pt));
  line-height: calc(32 * var(--pt));
  font-weight: 800;
  letter-spacing: -0.02em;
}

.app__pills {
  display: flex;
  gap: calc(8 * var(--pt));
  margin: calc(10 * var(--pt)) 0 calc(18 * var(--pt));
}

.app__pill {
  display: inline-flex;
  align-items: center;
  height: calc(26 * var(--pt));
  padding: 0 calc(10 * var(--pt));
  border-radius: var(--radius-full);
  background: var(--color-spotlight-subtle);
  color: var(--color-spotlight-on-subtle);
  font-size: calc(13 * var(--pt));
  font-weight: 700;
  white-space: nowrap;
}

/* `OnHeroButton`: ink fill on the hero, in both themes. */
.app__record {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: calc(10 * var(--pt));
  height: calc(56 * var(--pt));
  border-radius: var(--radius-full);
  background: var(--color-spotlight-on-fill);
  color: var(--color-always-dark-text);
  font-size: calc(17 * var(--pt));
  font-weight: 700;
}

.app__record i {
  display: block;
  width: calc(10 * var(--pt));
  height: calc(10 * var(--pt));
  border-radius: var(--radius-full);
  background: var(--color-status-record);
}

.app__stats {
  display: flex;
  gap: calc(10 * var(--pt));
  margin-bottom: calc(20 * var(--pt));
}

.app__stat {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: calc(4 * var(--pt));
  padding: calc(16 * var(--pt));
}

.app__stat b {
  font-size: calc(34 * var(--pt));
  line-height: calc(38 * var(--pt));
  font-weight: 800;
  letter-spacing: -0.03em;
}

.app__group {
  overflow: hidden;
  margin-bottom: calc(20 * var(--pt));
}

.app__row {
  display: flex;
  align-items: center;
  gap: calc(14 * var(--pt));
  padding: calc(13 * var(--pt)) calc(10 * var(--pt)) calc(13 * var(--pt)) calc(16 * var(--pt));
}

/* `CardDivider`, inset to the text. */
.app__row + .app__row {
  box-shadow: calc(74 * var(--pt)) -1px 0 0 var(--color-border-subtle);
}

.app__more,
.app__chev {
  display: flex;
  flex: none;
  color: var(--color-text-secondary);
}

.app__chev {
  color: var(--color-text-tertiary);
}

.app__chev svg,
.app__acc svg {
  width: calc(20 * var(--pt));
  height: calc(20 * var(--pt));
}

/* `IconTile`, in the script's colour. */
.app__tile {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: calc(44 * var(--pt));
  height: calc(44 * var(--pt));
  border-radius: calc(14 * var(--pt));
}

.app__tile--s {
  width: calc(32 * var(--pt));
  height: calc(32 * var(--pt));
  border-radius: calc(10 * var(--pt));
}

.app__tile--s svg {
  width: calc(18 * var(--pt));
  height: calc(18 * var(--pt));
}

.tint-sun { background: var(--color-tint-sun-background); color: var(--color-tint-sun-foreground); }
.tint-lilac { background: var(--color-tint-lilac-background); color: var(--color-tint-lilac-foreground); }
.tint-mint { background: var(--color-tint-mint-background); color: var(--color-tint-mint-foreground); }
.tint-sky { background: var(--color-tint-sky-background); color: var(--color-tint-sky-foreground); }
.tint-peach { background: var(--color-tint-peach-background); color: var(--color-tint-peach-foreground); }

/* The floating tab bar and the New script button beside it. */
.app__tabbar {
  position: absolute;
  z-index: 9;
  left: calc(16 * var(--pt));
  right: calc(16 * var(--pt));
  bottom: calc(30 * var(--pt));
  display: flex;
  align-items: center;
  gap: calc(10 * var(--pt));
}

.app__tabs {
  flex: 1;
  display: flex;
  gap: calc(2 * var(--pt));
  padding: calc(6 * var(--pt));
  border-radius: var(--radius-full);
}

.app__tab {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: calc(2 * var(--pt));
  height: calc(56 * var(--pt));
  border-radius: var(--radius-full);
  color: var(--color-text-secondary);
  font-size: calc(12 * var(--pt));
  font-weight: 700;
}

.app__tab.is-on {
  background: var(--color-surface-sunken);
  color: var(--color-text-primary);
}

.app__fab {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: calc(64 * var(--pt));
  height: calc(64 * var(--pt));
  border-radius: var(--radius-full);
  background: var(--color-accent-default);
  color: var(--color-text-on-accent);
  box-shadow: var(--shadow-lift);
}

.app__fab svg {
  width: calc(28 * var(--pt));
  height: calc(28 * var(--pt));
}

/* ---- settings ---- */

/* The Pro card: always dark, with the lens mark and the yellow Upgrade pill. */
.app__pro {
  display: flex;
  align-items: center;
  gap: calc(14 * var(--pt));
  margin-bottom: calc(22 * var(--pt));
  padding: calc(16 * var(--pt)) calc(16 * var(--pt)) calc(16 * var(--pt)) calc(18 * var(--pt));
  border-radius: calc(24 * var(--pt));
  background: var(--color-always-dark-surface);
  color: var(--color-always-dark-text);
}

.app__pro svg {
  width: calc(40 * var(--pt));
  height: calc(40 * var(--pt));
}

.app__pro .app__cap {
  color: var(--color-always-dark-text-secondary);
}

.app__upgrade {
  display: flex;
  align-items: center;
  height: calc(40 * var(--pt));
  padding: 0 calc(18 * var(--pt));
  border-radius: var(--radius-full);
  background: var(--color-spotlight-fill);
  color: var(--color-spotlight-on-fill);
  font-size: calc(15 * var(--pt));
  font-weight: 700;
}

.app__slabel {
  display: block;
  margin: 0 calc(8 * var(--pt)) calc(8 * var(--pt));
  font-size: calc(11 * var(--pt));
  line-height: calc(14 * var(--pt));
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
}

.app__set {
  display: flex;
  align-items: center;
  gap: calc(14 * var(--pt));
  min-height: calc(56 * var(--pt));
  padding: 0 calc(14 * var(--pt)) 0 calc(16 * var(--pt));
  font-size: calc(17 * var(--pt));
  font-weight: 600;
}

.app__set + .app__set,
.app__seg + .app__set {
  box-shadow: calc(62 * var(--pt)) -1px 0 0 var(--color-border-subtle);
}

.app__set > span:first-of-type:not(.app__tile) {
  flex: 1;
}

.app__set .app__grow {
  flex: 1;
  min-width: 0;
}

.app__val {
  font-weight: 500;
  color: var(--color-text-secondary);
}

.app__acc {
  display: flex;
  color: var(--color-text-tertiary);
}

/* Appearance on Android: a segmented control, System chosen. */
.app__seg {
  display: flex;
  margin: calc(10 * var(--pt));
  padding: calc(4 * var(--pt));
  border-radius: var(--radius-full);
  background: var(--color-surface-sunken);
}

.app__seg span {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  height: calc(40 * var(--pt));
  border-radius: var(--radius-full);
  font-size: calc(15 * var(--pt));
  font-weight: 700;
  color: var(--color-text-secondary);
}

.app__seg .is-on {
  background: var(--color-surface-raised);
  color: var(--color-text-primary);
  box-shadow: var(--shadow-card);
}

/* A switch, drawn off: a sunken track and a raised thumb. */
.app__switch {
  position: relative;
  flex: none;
  width: calc(52 * var(--pt));
  height: calc(32 * var(--pt));
  border-radius: var(--radius-full);
  background: var(--color-border-strong);
}

.app__switch::after {
  content: "";
  position: absolute;
  top: calc(3 * var(--pt));
  left: calc(3 * var(--pt));
  width: calc(26 * var(--pt));
  height: calc(26 * var(--pt));
  border-radius: var(--radius-full);
  background: var(--color-surface-raised);
  box-shadow: var(--shadow-card);
}

.app__foot {
  margin: calc(-12 * var(--pt)) calc(8 * var(--pt)) calc(22 * var(--pt));
  font-size: calc(13 * var(--pt));
  line-height: calc(18 * var(--pt));
  font-weight: 500;
  color: var(--color-text-secondary);
}

/* ---- the diagrams ---- */

/* The versus pair sits straight on the poster: two handsets already read as objects. */
.poster .versus {
  background: none;
  padding: 0;
  margin: 0;
}

.versus__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(var(--space-4), 2.5vw, var(--space-8));
  align-items: start;
}

.versus .device {
  height: min(24rem, 54vh);
  margin-inline: auto;
}

.vs__cap {
  margin-bottom: var(--space-4);
  text-align: center;
  font-size: var(--type-micro-size);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
}

.vs--us .vs__cap {
  color: var(--color-text-primary);
}

/* Their way: the same script, over the entire display. Real sentences rather than grey bars — the
   argument is about where words are, and grey bars are not words. */
.vs__spread {
  position: absolute;
  z-index: 2;
  inset: 17% 10% 6%;
  margin: 0;
  font-size: calc(24 * var(--pt));
  line-height: 2.1;
  font-weight: 700;
  color: var(--color-always-dark-text-secondary);
}

/* Orate's way: the same script, held in the prompter under the lens, still. */
.versus .device__band {
  top: calc(50 * var(--pt));
  height: 30%;
}

.versus .device__band p {
  padding-top: calc(30 * var(--pt));
  font-size: calc(24 * var(--pt));
  animation: none;
}

/* The reach, as a dimension line down the right-hand edge: how far the eye is asked to go. */
.vs__reach {
  position: absolute;
  right: 5%;
  top: 6%;
  z-index: 3;
  width: 2px;
  border-radius: var(--radius-full);
}

.vs--them .vs__reach {
  height: 88%;
  background: var(--color-always-dark-text-secondary);
}

.vs--us .vs__reach {
  height: 33%;
  background: var(--color-always-dark-success);
}

/*
 * The gaze. The whole argument is a distance, and a distance is something motion can show and
 * prose cannot. A dot travels the script on each handset at the same tempo, so what differs on
 * screen is how far it has to go.
 */
.vs__gaze {
  position: absolute;
  /* Off the text column: centred, the dot sat on top of the very words it was meant to be reading. */
  left: 6%;
  top: 6%;
  z-index: 4;
  width: 10px;
  height: 10px;
  border-radius: var(--radius-full);
  translate: -50% 0;
  pointer-events: none;
  box-shadow: 0 0 0 3px var(--rec-faint);
}

.vs--them .vs__gaze { background: var(--color-always-dark-text-secondary); }
.vs--us .vs__gaze { background: var(--color-always-dark-success); }

@media (prefers-reduced-motion: no-preference) {
  .vs--them .vs__gaze { animation: gaze-far 4.4s cubic-bezier(0.45, 0, 0.55, 1) infinite; }
  .vs--us .vs__gaze { animation: gaze-near 4.4s cubic-bezier(0.45, 0, 0.55, 1) infinite; }

  /* Same tempo on both, so the eye reads the distance rather than the speed. */
  @keyframes gaze-far {
    0%, 8% { top: 6%; }
    46%, 54% { top: 92%; }
    92%, 100% { top: 6%; }
  }

  @keyframes gaze-near {
    0%, 8% { top: 6%; }
    46%, 54% { top: 37%; }
    92%, 100% { top: 6%; }
  }
}

.vs__verdict {
  margin-top: var(--space-4);
  text-align: center;
  font-size: var(--type-caption-size);
  font-weight: 700;
  color: var(--color-text-secondary);
}

.vs--us .vs__verdict {
  color: var(--color-status-success);
}

.versus > figcaption {
  margin-top: var(--space-6);
  text-align: center;
}

/* ---- 02: the tape ---- */

.tape__axis,
.tape__who,
.tape__out {
  margin: 0;
  font-size: var(--type-caption-size);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.tape__axis {
  display: flex;
  justify-content: space-between;
  color: var(--color-text-secondary);
  margin-bottom: var(--space-4);
}

.tape__kill { color: var(--color-tint-peach-foreground); }

.tape__row + .tape__row { margin-top: var(--space-5); }

.tape__who {
  color: var(--color-text-primary);
  margin-bottom: var(--space-2);
}

.tape__bar {
  display: flex;
  height: 24px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--color-surface-sunken);
}

.tape__kept,
.tape__lost {
  flex: 0 0 var(--w);
}

/*
 * Kept footage, with the fragment boundaries drawn on it. Four ticks across eight seconds is the
 * two-second flush interval made visible — the diagram states the mechanism rather than asserting
 * the outcome.
 */
.tape__kept {
  background-color: var(--color-status-success);
  background-image: repeating-linear-gradient(
    90deg,
    transparent 0 calc(25% - 2px),
    var(--color-surface-raised) calc(25% - 2px) 25%
  );
}

/* Lost footage, hatched in the peach of a destructive confirmation — not red, which means
   recording. */
.tape__lost {
  background-color: var(--color-tint-peach-background);
  background-image: repeating-linear-gradient(
    135deg,
    color-mix(in srgb, var(--color-tint-peach-foreground) 45%, transparent) 0 3px,
    transparent 3px 8px
  );
}

.tape__out {
  margin-top: var(--space-3);
  text-align: right;
}

.tape__out--bad { color: var(--color-tint-peach-foreground); }
.tape__out--good { color: var(--color-status-success); }

/* ---- 03: the pace ---- */

.pace__head {
  margin: 0 0 var(--space-4);
  font-size: var(--type-micro-size);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
}

.pace__row {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.pace__row + .pace__row { margin-top: var(--space-5); }

.pace__meta {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  margin: 0 0 var(--space-2);
}

.pace__lang {
  font-weight: 700;
  color: var(--color-text-primary);
}

.pace__rate {
  font-size: var(--type-caption-size);
  font-weight: 500;
  color: var(--color-text-secondary);
}

/* The number is the headline. */
.pace__time {
  margin-left: auto;
  font-size: var(--type-title-s-size);
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  color: var(--color-text-primary);
}

.pace__track {
  height: 10px;
  border-radius: var(--radius-full);
  background: var(--color-surface-sunken);
  overflow: hidden;
}

.pace__fill {
  display: block;
  width: var(--w);
  height: 100%;
  border-radius: inherit;
  background: var(--color-accent-default);
}

@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    /* The bars draw themselves as the card arrives, which is the one moment the comparison between
       them is actually being looked at. */
    .pace__fill {
      animation: pace-draw linear both;
      animation-timeline: view();
      animation-range: entry 12% cover 34%;
    }

    @keyframes pace-draw {
      from { width: 0; }
    }
  }
}

/* ---- 04: the ledger ---- */

.ledger__head {
  margin: 0 0 var(--space-3);
  font-size: var(--type-micro-size);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
}

.ledger ul {
  list-style: none;
  margin: 0;
  padding: 0;
  /* Fills the panel, so a card made taller by its neighbours reads as a table with room. */
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}

.ledger li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding-block: var(--space-3);
  font-weight: 600;
  color: var(--color-text-primary);
}

.ledger li + li { border-top: 1px solid var(--color-border-subtle); }

.ledger b {
  flex: none;
  white-space: nowrap;
  padding: 0.15rem 0.65rem;
  border-radius: var(--radius-full);
  background: var(--color-tint-mint-background);
  color: var(--color-tint-mint-foreground);
  font-size: var(--type-caption-size);
}

/* The exception, marked as one. A privacy claim that hides its single caveat is the kind that gets
   found out; naming it is what makes the other three rows worth believing. */
.ledger .ledger__opt {
  background: var(--color-tint-sun-background);
  color: var(--color-tint-sun-foreground);
}


/* ---- the four shapes ---- */

/* ---- the shapes ---- */

.frame {
  position: relative;
  overflow: hidden;
  background: var(--color-always-dark-surface-raised);
  box-shadow:
    0 0 0 4px var(--color-always-dark-surface),
    var(--shadow-lift);
}

/* True relative scale: a tablet is bigger than a phone, and the row says so. */
.frame--phone {
  height: 11.5rem;
  aspect-ratio: 9 / 19;
  border-radius: 15px;
}

.frame--phone-land {
  width: 11.5rem;
  aspect-ratio: 19 / 9;
  border-radius: 15px;
}

.frame--tab-land {
  width: min(15rem, 100%);
  aspect-ratio: 4 / 3;
  border-radius: 11px;
}

.frame--tab-port {
  height: 14rem;
  max-width: 100%;
  aspect-ratio: 3 / 4;
  border-radius: 11px;
}

/* The lens, wherever that device and orientation actually keeps it. Everything else is placed
   relative to this, which is the whole argument of the section. */
.frame__lens {
  position: absolute;
  z-index: 3;
  width: 6px;
  height: 6px;
  border-radius: var(--radius-full);
  background: var(--color-always-dark-surface);
  box-shadow: 0 0 0 1.5px var(--color-always-dark-border);
}

.frame--phone .frame__lens,
.frame--tab-port .frame__lens,
.frame--tab-land .frame__lens {
  top: 6px;
  left: 50%;
  translate: -50% 0;
}

/* Landscape phone: the natural top edge is now the leading one, and the lens rides round with it. */
.frame--phone-land .frame__lens {
  left: 6px;
  top: 50%;
  translate: 0 -50%;
}

/* The reading window. Two bars rather than type: at this size real words would be unreadable. The
   always-dark glass, with the reading lines in the recorder's yellow readout colour. */
.frame__band {
  position: absolute;
  z-index: 2;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  padding: 7px;
  border-radius: 6px;
  background: var(--color-always-dark-surface);
}

.frame__band i {
  display: block;
  height: 2px;
  border-radius: var(--radius-full);
  background: var(--color-always-dark-accent);
}

.frame__band i:first-child { width: 100%; }
.frame__band i:last-child { width: 62%; }

.frame--phone .frame__band {
  top: 15px;
  left: 7px;
  right: 7px;
}

/* Down the leading edge, clear of the rail opposite. */
.frame--phone-land .frame__band {
  top: 8px;
  bottom: 8px;
  left: 16px;
  width: 42%;
}

/* Held to a measure and centred under the lens rather than filling the width — the `expanded` rule,
   and the reason a tablet does not simply get bigger text. */
.frame--tab-land .frame__band {
  top: 15px;
  left: 50%;
  translate: -50% 0;
  width: 54%;
}

.frame--tab-port .frame__band {
  top: 15px;
  left: 50%;
  translate: -50% 0;
  width: 76%;
}

/* The controls. Where they sit is the other half of what changes per variant. */
.frame__rail {
  position: absolute;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  border-radius: var(--radius-full);
  background: var(--rec-glass);
}

.frame__rail i {
  width: 5px;
  height: 5px;
  border-radius: var(--radius-full);
  background: var(--color-always-dark-text-secondary);
}

/* The shutter, always the largest target on the rail. */
.frame__rail i:nth-child(2) {
  width: 10px;
  height: 10px;
  background: var(--color-status-record);
  box-shadow: 0 0 0 1.5px var(--color-always-dark-text);
}

.frame__rail--bottom {
  left: 50%;
  translate: -50% 0;
  bottom: 8px;
  padding: 5px 9px;
}

/* Bigger targets on the expanded widths, which is the variant table's own wording. */
.frame__rail--wide {
  gap: 11px;
  padding: 6px 13px;
}

.frame__rail--wide i { width: 6px; height: 6px; }
.frame__rail--wide i:nth-child(2) { width: 13px; height: 13px; }

/* Landscape phone: trailing edge, vertical. */
.frame__rail--trailing {
  flex-direction: column;
  right: 7px;
  top: 50%;
  translate: 0 -50%;
  padding: 9px 5px;
}

/* Landscape tablet: a side rail, off the video. */
.frame__rail--side {
  flex-direction: column;
  right: 9px;
  top: 50%;
  translate: 0 -50%;
  gap: 11px;
  padding: 12px 7px;
}

.frame__rail--side i { width: 6px; height: 6px; }
.frame__rail--side i:nth-child(2) { width: 13px; height: 13px; }

@media (max-width: 62rem) {
  .rigs {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 34rem) {
  .rigs {
    grid-template-columns: 1fr;
  }

  .rigs__stage {
    min-height: 0;
  }
}


/* ================================================================ the other pages */

/*
 * Every page that is not the landing page opens the same way: the empty-state recipe turned into a
 * page header — a `BadgeCircle` in the page's tint carrying its glyph, the breadcrumb, a title, one
 * lede, and its one action. Centred, with nothing competing.
 */
.hero {
  display: grid;
  justify-items: center;
  gap: var(--space-5);
  padding-top: clamp(var(--space-10), 6vw, var(--space-12));
  text-align: center;
}

.hero > div {
  display: grid;
  justify-items: center;
  gap: var(--space-5);
}

.hero h1 {
  font-size: var(--step-hero);
  line-height: 1;
  letter-spacing: -0.045em;
  max-width: 16ch;
}

.hero h1 em {
  font-style: normal;
}

.hero .lede {
  max-width: 50ch;
}

.hero .actions,
.hero .trust {
  justify-content: center;
}

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: center;
}

/* Breadcrumb, as a row of small pills: the same trail the BreadcrumbList in the head declares. */
.crumbs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
  justify-content: center;
  font-size: var(--type-caption-size);
  font-weight: 700;
  color: var(--color-text-secondary);
}

.crumbs a {
  color: var(--color-text-secondary);
  text-decoration: none;
}

.crumbs a:hover {
  color: var(--color-text-primary);
}

.crumbs [aria-hidden] {
  color: var(--color-text-tertiary);
}

.policy-date {
  font-size: var(--type-caption-size);
  font-weight: 700;
  color: var(--color-text-secondary);
}

/* ---- documents: the settings recipe ---- */

/*
 * Privacy, terms and support are long and structured, so they take the settings screen's shape at
 * page width: each group's label and heading held in a column of their own, beside a card of rows
 * that holds the group. On a narrow screen the head goes back above its card.
 */
.doc section {
  display: grid;
  grid-template-columns: minmax(0, 0.7fr) minmax(0, 1.3fr);
  gap: var(--space-2) clamp(var(--space-7), 4vw, var(--space-11));
  align-items: start;
}

.doc .section-head {
  position: sticky;
  top: 7rem;
  grid-row: span 3;
  margin-bottom: 0;
}

.doc .section-head h2 {
  font-size: var(--step-title);
}

.doc section > :not(.section-head) {
  grid-column: 2;
}

@media (max-width: 60rem) {
  .doc section {
    grid-template-columns: 1fr;
  }

  .doc .section-head {
    position: static;
  }

  .doc section > :not(.section-head) {
    grid-column: 1;
  }
}

/* Questions and clauses as rows in one card, divided inset — never a list on hairlines alone. */
.faq {
  display: grid;
  padding-inline: clamp(var(--space-6), 3vw, var(--space-8));
}

.faq > div {
  display: grid;
  gap: var(--space-3);
  padding-block: var(--space-7);
}

.faq > div + div {
  border-top: 1px solid var(--color-border-subtle);
}

.faq h3 {
  font-size: var(--type-title-s-size);
  line-height: 1.3;
  letter-spacing: -0.01em;
}

.faq p,
.faq li {
  color: var(--color-text-secondary);
  max-width: var(--measure);
}

.faq p + p {
  margin-top: var(--space-2);
}

/* The privacy table: a real `<table>`, because three columns of facts have to be read across. It
   scrolls inside its own card so the page body never does. */
.table-scroll {
  overflow-x: auto;
}

.policy-table {
  width: 100%;
  min-width: 34rem;
  border-collapse: collapse;
  text-align: left;
}

.policy-table caption {
  caption-side: top;
  padding: var(--space-6) var(--space-6) 0;
  font-size: var(--type-micro-size);
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
  text-align: left;
}

.policy-table th {
  padding: var(--space-5) var(--space-6);
  font-size: var(--type-caption-size);
  font-weight: 800;
  border-bottom: 1px solid var(--color-border-subtle);
}

.policy-table td {
  padding: var(--space-5) var(--space-6);
  font-size: var(--type-caption-size);
  line-height: 1.6;
  font-weight: 500;
  color: var(--color-text-secondary);
  vertical-align: top;
}

.policy-table tbody tr + tr td {
  border-top: 1px solid var(--color-border-subtle);
}

.policy-table td b {
  color: var(--color-text-primary);
}

/* A row's first cell names the row, so it is a <th scope="row"> — which is what lets a screen reader
   announce "Export, Orate: free" rather than a bare "free", and what Lighthouse's td-has-header
   check asks for. Styled as the cell it replaced, not as a column heading. */
.policy-table tbody th {
  padding: var(--space-5) var(--space-6);
  font-size: var(--type-caption-size);
  line-height: 1.6;
  font-weight: 500;
  color: var(--color-text-primary);
  vertical-align: top;
  border-bottom: 0;
}

.policy-table tbody tr + tr th {
  border-top: 1px solid var(--color-border-subtle);
}

/* The closing row is the claim the other rows exist to bound. */
.policy-table__never td,
.policy-table__never th {
  color: var(--color-text-primary);
  font-weight: 800;
}

.policy-note {
  margin-top: var(--space-5);
  font-size: var(--type-caption-size);
  line-height: 1.7;
  font-weight: 500;
  color: var(--color-text-secondary);
  max-width: 68ch;
}

/* The support page's fact card: where the bug report lives and what it carries, as rows. */
.rig {
  width: min(30rem, 100%);
  margin-top: var(--space-3);
  padding: var(--space-6);
  text-align: left;
}

.rig__chrome {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

.rig__dot {
  display: none;
}

.rig__time {
  font-size: var(--type-micro-size);
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
}

.rig__badge {
  margin-left: auto;
  padding: 0.15rem 0.65rem;
  border-radius: var(--radius-full);
  background: var(--color-tint-sky-background);
  color: var(--color-tint-sky-foreground);
  font-size: var(--type-caption-size);
  font-weight: 800;
}

.rig__foot div {
  display: grid;
  gap: var(--space-1);
  padding-block: var(--space-4);
}

.rig__foot div + div {
  border-top: 1px solid var(--color-border-subtle);
}

.rig__foot b {
  font-size: var(--type-title-s-size);
  line-height: var(--type-title-s-leading);
}

/* ---- the blog listing ---- */

/* The newest post, as the page's hero card: the Scripts screen's "Up next". */
.post-hero {
  display: grid;
  justify-items: start;
  gap: var(--space-4);
  margin-top: var(--space-11);
  padding: clamp(var(--space-7), 4vw, var(--space-10));
  text-align: left;
}

.post-hero > * {
  position: relative;
}

.post-hero .label {
  color: var(--color-spotlight-on-fill);
}

.post-hero h2 {
  font-size: var(--step-display);
  max-width: 22ch;
}

.post-hero p {
  max-width: 56ch;
  font-weight: 600;
}

/* On the hero the button is ink with white type, in both themes — `OnHeroButton`. */
.post-hero .btn {
  background: var(--color-spotlight-on-fill);
  color: var(--color-always-dark-text);
  box-shadow: none;
}

.post-hero .btn:hover {
  background: var(--color-always-dark-surface-raised);
  color: var(--color-always-dark-text);
}

/* Every post as a row in one card: its category's tile, the facts, the title, one line, a chevron.
   The anchor is on the title alone and stretched over the row, so the link text is the headline. */
.post-list {
  display: grid;
  margin-top: var(--space-6);
  padding-inline: clamp(var(--space-5), 2.5vw, var(--space-7));
}

.post-card {
  position: relative;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  column-gap: var(--space-5);
  row-gap: var(--space-1);
  align-items: start;
  padding-block: var(--space-6);
}

.post-card + .post-card {
  border-top: 1px solid var(--color-border-subtle);
}

.post-card > .tile {
  grid-row: 1 / span 3;
}

.post-card > :not(.tile):not(.chevron) {
  grid-column: 2;
}

.post-card .chevron {
  grid-column: 3;
  grid-row: 1 / span 3;
  align-self: center;
}

.post-card h3 {
  font-size: var(--type-title-s-size);
  line-height: 1.3;
  letter-spacing: -0.01em;
}

.post-card h3 a {
  color: inherit;
  text-decoration: none;
}

.post-card h3 a::after {
  content: "";
  position: absolute;
  inset: 0;
}

.post-card:hover h3 a,
.post-card h3 a:focus-visible {
  text-decoration: underline;
}

.post-card p {
  color: var(--color-text-secondary);
  max-width: var(--measure);
  font-size: var(--type-caption-size);
  line-height: 1.6;
  font-weight: 500;
}

.post-card__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
}

.post-card .post-card__meta {
  font-size: var(--type-caption-size);
  font-weight: 700;
}

.post-card__meta b {
  color: var(--color-text-primary);
}

/* `Chevron`: a row that opens something. Drawn, so it takes the text colour. */
.chevron {
  display: block;
  width: 10px;
  height: 10px;
  margin-right: var(--space-2);
  border-top: 2.5px solid var(--color-text-tertiary);
  border-right: 2.5px solid var(--color-text-tertiary);
  border-radius: 2px;
  rotate: 45deg;
}

@media (max-width: 40rem) {
  .post-card {
    grid-template-columns: auto minmax(0, 1fr);
  }

  .post-card .chevron {
    display: none;
  }
}

/* ---- an article ---- */

/*
 * An article is a sheet: a white reading surface on the grey stage, held to a measure, with its
 * header standing above it. The longest text on the site gets the plainest, calmest surface.
 */
.article {
  width: min(48rem, 100%);
  margin-inline: auto;
  padding-top: clamp(var(--space-10), 6vw, var(--space-12));
}

.article__head {
  display: grid;
  gap: var(--space-5);
  padding: 0 clamp(0rem, 3vw, var(--space-8)) var(--space-8);
}

.article__head .crumbs {
  justify-content: flex-start;
}

.article__head h1 {
  font-size: var(--step-display);
  line-height: 1.04;
  letter-spacing: -0.035em;
  max-width: 24ch;
}

.article__standfirst {
  font-size: var(--step-lede);
  line-height: 1.55;
  color: var(--color-text-secondary);
}

/* The byline as a row of facts: the category as a pill, then author, date and length. */
.article__byline {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  align-items: center;
  font-size: var(--type-caption-size);
  font-weight: 700;
  color: var(--color-text-secondary);
}

.article__byline > span:first-child {
  padding: 0.2rem 0.75rem;
  border-radius: var(--radius-full);
  background: var(--color-tint-sky-background);
  color: var(--color-tint-sky-foreground);
  font-weight: 800;
}

.article__byline a {
  color: var(--color-text-primary);
}

.prose {
  padding: clamp(var(--space-6), 5vw, var(--space-11));
  border-radius: var(--radius-xl);
  background: var(--color-surface-raised);
  box-shadow: var(--shadow-card);
}

.prose > :first-child {
  margin-top: 0;
}

.prose h2 {
  font-size: var(--step-title);
  line-height: 1.15;
  letter-spacing: -0.025em;
  margin-top: clamp(var(--space-9), 5vw, var(--space-11));
}

.prose h3 {
  font-size: var(--type-title-s-size);
  letter-spacing: -0.01em;
  margin-top: var(--space-8);
}

.prose p,
.prose li {
  color: var(--color-text-secondary);
  line-height: 1.75;
}

.prose p {
  margin-top: var(--space-5);
}

/*
 * Prose links are underlined. Elsewhere a link is identifiable by position — in a nav, or a button —
 * but inside a paragraph colour alone is not a cue for anyone who cannot separate these two greys.
 */
.prose a {
  color: var(--color-text-primary);
  font-weight: 700;
  text-decoration: underline;
  text-decoration-color: var(--color-text-tertiary);
  text-decoration-thickness: 1.5px;
  text-underline-offset: 0.2em;
}

.prose a:hover {
  text-decoration-color: currentColor;
}

.prose ul,
.prose ol {
  margin-top: var(--space-5);
  display: grid;
  gap: var(--space-3);
  padding-left: var(--space-6);
}

.prose ul {
  padding-left: 0;
}

.prose ul li {
  position: relative;
  padding-left: var(--space-6);
}

.prose ul li::before {
  content: "";
  position: absolute;
  left: var(--space-1);
  top: 0.7em;
  width: 7px;
  height: 7px;
  border-radius: var(--radius-full);
  background: var(--color-text-primary);
}

.prose ol {
  list-style: decimal;
}

.prose ol li::marker {
  font-weight: 900;
  color: var(--color-text-primary);
}

.prose strong {
  color: var(--color-text-primary);
}

.prose code {
  font-family: var(--font-code);
  font-size: 0.85em;
  padding: 0.1em 0.4em;
  border-radius: var(--radius-sm);
  background: var(--color-surface-sunken);
  color: var(--color-text-primary);
}

/* Tables inside a sheet sit on the sunken surface rather than raising a card on a card. */
.prose .table-scroll {
  margin-top: var(--space-6);
  background: var(--color-surface-sunken);
  box-shadow: none;
  border-radius: var(--radius-md);
}

.prose .policy-table tbody tr + tr td,
.prose .policy-table tbody tr + tr th,
.prose .policy-table th {
  border-color: var(--color-border-strong);
}

.prose figure {
  margin-top: var(--space-6);
}

.prose figcaption {
  margin-top: var(--space-4);
  font-size: var(--type-caption-size);
  font-weight: 500;
  line-height: 1.6;
  color: var(--color-text-secondary);
}

/* A pulled-out measurement or caveat, as a tinted notice — the system's banner, not a left-border
   accent card, which the design system rules out by name. */
.note {
  margin-top: var(--space-7);
  padding: var(--space-6);
  border-radius: var(--radius-lg);
  background: var(--color-tint-sun-background);
}

.note > p {
  margin-top: 0;
  color: var(--color-text-primary);
  line-height: 1.7;
}

.note > p + p {
  margin-top: var(--space-3);
}

.note__label {
  font-size: var(--type-micro-size);
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-tint-sun-foreground);
}

.note strong,
.note a {
  color: var(--color-text-primary);
}

/* The closing ask inside an article: quieter than the landing page's, a sunken panel in the sheet. */
.post-cta {
  margin-top: clamp(var(--space-9), 5vw, var(--space-11));
  padding: var(--space-7);
  display: grid;
  gap: var(--space-3);
  justify-items: start;
  background: var(--color-surface-sunken);
  box-shadow: none;
}

.post-cta h2 {
  font-size: var(--type-title-m-size);
  margin-top: 0;
}

.post-cta p {
  margin-top: 0;
}

.post-cta .btn {
  margin-top: var(--space-2);
}

/* A button inside an article is still a button. `.prose a` outranks `.btn--primary` (0,1,1 against
   0,1,0), so without this the article's link colour won: on the dark theme that is white on
   spotlight yellow, 1.5:1, and it carried the link underline too. */
.prose a.btn {
  text-decoration: none;
}

.prose a.btn--primary,
.prose a.btn--primary:hover {
  color: var(--color-text-on-accent);
}

/* What to read next: rows with chevrons. */
.post-next {
  margin-top: var(--space-9);
}

.post-next .label {
  display: block;
  margin-bottom: var(--space-3);
}

.post-next ul {
  display: grid;
  gap: 0;
  padding-left: 0;
  margin-top: 0;
}

.post-next li {
  padding-left: 0;
}

.post-next li + li {
  border-top: 1px solid var(--color-border-subtle);
}

.post-next li::before {
  display: none;
}

.post-next a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  min-height: 56px;
  padding-block: var(--space-3);
  text-decoration: none;
}

.post-next a::after {
  content: "";
  flex: none;
  width: 9px;
  height: 9px;
  margin-right: var(--space-2);
  border-top: 2.5px solid var(--color-text-tertiary);
  border-right: 2.5px solid var(--color-text-tertiary);
  border-radius: 2px;
  rotate: 45deg;
}

/* ================================================================ motion */

@media (prefers-reduced-motion: no-preference) {
  .reveal {
    animation: rise 0.7s cubic-bezier(0.2, 0.7, 0.2, 1) both;
  }

  .reveal:nth-child(1) { animation-delay: 0.04s; }
  .reveal:nth-child(2) { animation-delay: 0.1s; }
  .reveal:nth-child(3) { animation-delay: 0.16s; }
  .reveal:nth-child(4) { animation-delay: 0.22s; }
  .reveal:nth-child(5) { animation-delay: 0.28s; }

  /* 0.01, not 0. Chrome does not count a fully transparent element as a Largest Contentful Paint
     candidate, so with 0 the hero headline was skipped and the page's LCP became the cookie banner,
     which a deferred script draws seconds later. 0.01 looks the same and lets the headline count
     from its first frame. */
  @keyframes rise {
    from {
      opacity: 0.01;
      transform: translateY(14px);
    }
  }
}

/* Scroll-driven reveals, no JavaScript. Browsers without support simply show the content. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .in-view {
      animation: rise-in linear both;
      animation-timeline: view();
      animation-range: entry 4% cover 22%;
    }

    @keyframes rise-in {
      from {
        opacity: 0;
        transform: translateY(24px);
      }
    }
  }
}

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

  /* The scroll is the one piece of motion that carries meaning, so it stops rather than
     disappearing: the prompter still shows the script, held still and readable. */
  .device__band p {
    animation: none;
    transform: none;
  }

  /* The gaze is parked at the far end of each reach rather than removed, so the distance it
     exists to show is still legible. */
  .vs--them .vs__gaze { top: 92%; }
  .vs--us .vs__gaze { top: 37%; }

  /* The footage becomes its own poster frame, in CSS — a browser that does not pause
     `display: none` media keeps decoding it silently; Chrome and Safari both stop it. */
  .device__footage {
    display: none;
  }

  .device__preview--over {
    background:
      linear-gradient(color-mix(in srgb, var(--color-always-dark-surface) 55%, transparent), transparent 34%),
      linear-gradient(transparent 70%, color-mix(in srgb, var(--color-always-dark-surface) 78%, transparent)),
      url("/assets/footage/viewfinder.jpg") center / cover;
  }
}

/* ================================================================ the cookie banner */

/*
 * A floating card at the bottom rather than a modal over the page: the site's argument is that it
 * does not interrupt. Ignoring it is declining — nothing is set until somebody presses Accept. Both
 * buttons are the same size and weight and Decline comes first in the DOM and the tab order; a
 * banner whose Accept is a button and whose Decline is a grey link has decided for you.
 */
.consent {
  position: fixed;
  left: var(--space-5);
  right: var(--space-5);
  bottom: var(--space-5);
  z-index: 50;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4) var(--space-6);
  max-width: 64rem;
  margin-inline: auto;
  padding: var(--space-5) var(--space-6);
  border-radius: var(--radius-lg);
  background: var(--color-surface-raised);
  box-shadow: var(--shadow-lift);
}

/* The scan-to-install card sits bottom-left, exactly where the banner lands; it is lifted clear by
   the banner's measured height, because the banner wraps on narrow screens. */
body.consent-open .qr {
  bottom: calc(var(--consent-height, 0px) + var(--space-8));
}

.consent__text {
  margin: 0;
  max-width: 62ch;
  font-size: var(--type-caption-size);
  line-height: 1.5;
  font-weight: 500;
  color: var(--color-text-secondary);
}

.consent__actions {
  display: flex;
  gap: var(--space-3);
  flex: none;
}

.consent__btn {
  cursor: pointer;
}

@media (max-width: 40rem) {
  .consent {
    flex-direction: column;
    align-items: stretch;
  }

  .consent__actions {
    justify-content: stretch;
  }

  .consent__btn {
    flex: 1;
  }
}

/*
 * The landing page below its first two screens is skipped until it scrolls near. Those sections hold
 * most of the page's drawn handsets — hundreds of elements each — and laying them out before the
 * first paint was most of the home page's main-thread time, which Lighthouse charged to the LCP as
 * "render delay". `auto` in the intrinsic size remembers each section's real height once it has
 * been laid out, so the scrollbar settles rather than jumping on the way back up.
 */
#how,
.tour-section,
#sizes,
#controls,
#quality,
#languages,
#download {
  content-visibility: auto;
  contain-intrinsic-size: auto 1000px;
}
