/*
 * Orate — site styles.
 *
 * Colour and spacing come from `generated/`, which `design/build.js` writes from the same tokens
 * the two apps consume. Nothing here invents a colour: if the accent changes, this site changes
 * with it. See web/README.md.
 *
 * Dark only, on purpose. The recorder is always dark and the app is dark-first by decision, so a
 * light shop window would misrepresent the thing it is selling.
 *
 * On motion: the hero is one orchestrated moment — the rig comes to life and keeps living, because
 * the product *is* motion (a script scrolling under a lens) and a still image cannot say that.
 * Everything below the fold is restrained by comparison. Every effect is CSS; there is no JS on
 * this site at all.
 */

@import url("generated/tokens.css");
@import url("generated/dimensions.css");

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

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

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

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

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

:root {
  /* Type. A sturdy engineered display face, a plain body, and a mono that reads as the
     silkscreened label on a piece of equipment. */
  --font-display: "Bricolage Grotesque", "Chivo", ui-sans-serif, system-ui, sans-serif;
  --font-body: "Instrument Sans", ui-sans-serif, system-ui, sans-serif;
  --font-mono: "Martian Mono", ui-monospace, "SFMono-Regular", monospace;

  --step--1: 0.8125rem;
  --step-0: 1.0625rem;
  --step-1: 1.25rem;
  --step-2: clamp(1.5rem, 1.2rem + 1.2vw, 2rem);
  --step-3: clamp(2rem, 1.5rem + 2.4vw, 3rem);
  --step-4: clamp(2.9rem, 1.5rem + 6vw, 6rem);

  --measure: 60ch;
  --gutter: clamp(1.25rem, 4vw, 4rem);
  --rule: 1px solid var(--color-border-subtle);

  /* The tally light. Red is reserved for the recording state and destructive actions and must
     never become the brand accent — the same rule the apps hold. */
  --tally: var(--color-status-record);
}

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  background: var(--color-surface-canvas);
  color: var(--color-text-primary);
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* Atmosphere. A slow amber wash that drifts, so the page reads as a lit object rather than a
   flat fill. Its own layer, because animating a body background repaints the whole document. */
.aurora {
  position: fixed;
  inset: -30% -10% auto -10%;
  height: 90vh;
  z-index: -2;
  pointer-events: none;
  background:
    radial-gradient(45% 55% at 30% 30%, rgb(232 180 74 / 13%), transparent 70%),
    radial-gradient(40% 50% at 72% 20%, rgb(162 118 31 / 12%), transparent 70%);
  filter: blur(30px);
  animation: drift 24s ease-in-out infinite alternate;
}

@keyframes drift {
  to {
    transform: translate3d(3%, 4%, 0) scale(1.12);
  }
}

/* Grain over everything, for texture. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 100;
  pointer-events: none;
  opacity: 0.035;
  background-image: url("data:image/svg+xml;utf8,\
<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'>\
<filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/></filter>\
<rect width='120' height='120' filter='url(%23n)'/></svg>");
}

h1,
h2,
h3 {
  font-family: var(--font-display);
  font-weight: 800;
  line-height: 1.04;
  letter-spacing: -0.03em;
  text-wrap: balance;
}

a {
  color: var(--color-accent-default);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

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

:focus-visible {
  outline: 2px solid var(--color-accent-default);
  outline-offset: 3px;
  border-radius: 4px;
}

/* --------------------------------------------------------------- shared ---- */

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

.label {
  font-family: var(--font-mono);
  font-size: 0.625rem;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--color-text-tertiary);
}

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

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

.top {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  padding-block: var(--space-6);
  border-bottom: var(--rule);
}

.wordmark {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: var(--step-1);
  letter-spacing: -0.02em;
  color: var(--color-text-primary);
  text-decoration: none;
}

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

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

/*
 * `:not(.btn)` is load-bearing, not tidiness. Written as `.top nav a` this rule is a class, a class
 * and a type — which outranks `.btn--primary`'s single class — so it was overwriting the button's
 * own ink and painting #8A8A93 on the amber fill: a contrast ratio of 1.24:1, against the 10.36:1
 * the button declares. It looked like a disabled control on every page of the site.
 */
.top nav a:not(.btn) {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
  text-decoration: none;
}

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

.top .btn {
  min-height: 40px;
  font-family: var(--font-body);
  font-size: var(--step--1);
  letter-spacing: normal;
  text-transform: none;
}

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

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

/*
 * Centred, with the handsets below rather than beside. The claim gets the full measure of the page
 * and the product sits directly under it, which is how a phone app is usually shown — and it is
 * where the eye goes after reading, so the device is the reward rather than a sidebar.
 */
.hero {
  display: grid;
  justify-items: center;
  text-align: center;
  padding-top: clamp(2.5rem, 6vw, 5rem);
}

.hero h1 {
  font-size: var(--step-4);
  margin-block: var(--space-5) var(--space-6);
  max-width: 20ch;
}

.hero h1 em {
  font-style: normal;
  color: var(--color-accent-default);
}

.hero .lede {
  margin-bottom: var(--space-7);
  max-width: 48ch;
}

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

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

/*
 * Two handsets, angled and overlapping, running off the bottom of the fold. The crop is the point:
 * it says there is more below without spending a whole screen on saying so.
 */
.hero__stage {
  position: relative;
  /*
   * Wider than the text column, and allowed to spend the gutters. The pair of handsets is wider
   * than the column below about 760px, and the clip below was slicing the back one's right edge
   * flat against the stage edge.
   */
  width: min(52rem, calc(100% + var(--gutter) * 2));
  /* Height and margin carry the `top` inset below, so the handsets show the same amount of
     themselves and everything under the stage sits where it did before the inset existed. */
  height: clamp(19rem, 42vw, 26.5rem);
  margin-top: clamp(0.75rem, 2vw, 1.5rem);
  /*
   * Clipped, and faded rather than sliced. The handsets are taller than the stage on purpose —
   * running off the bottom says there is more below. Without the clip they carried on into the
   * amber plane and sat on top of two of its four stats.
   */
  overflow: hidden;
  mask-image: linear-gradient(to bottom, #000 78%, transparent 99%);
}

/*
 * Position lives in custom properties because the load animation below has to read it. Writing the
 * angles straight into the keyframes meant an animation filling `both` held the wide-screen
 * position forever, and the narrow-screen overrides at the bottom of this block silently never
 * applied to anyone whose device wasn't asking for reduced motion.
 */
.hero__stage .device {
  position: absolute;
  left: 50%;
  /*
   * Inset from the top, because these are rotated. Turning a handset about its centre lifts one
   * upper corner above `top: 0` — around 18px at this size and angle — and the clip on the stage
   * then sliced both handsets flat across their tops.
   */
  top: clamp(1rem, 3.6vw, 2rem);
  height: min(40rem, 94vw);
  margin-inline: 0;
  translate: var(--tx) 0;
  rotate: var(--rot);
}

.hero__stage .device--front {
  --tx: -74%;
  --rot: -7deg;
  --rot-start: -14deg;
  --rise: 12%;
  z-index: 2;
}

.hero__stage .device--back {
  --tx: -18%;
  --rot: 8deg;
  --rot-start: 16deg;
  --rise: 16%;
  z-index: 1;
  filter: brightness(1.05) saturate(0.95);
}

@media (prefers-reduced-motion: no-preference) {
  /* Both land exactly where they sit with no motion at all — the `to` frame is the same declaration
     as above — so a dropped animation is a still hero rather than a broken one. */
  @keyframes settle {
    from { opacity: 0; translate: var(--tx) var(--rise); rotate: var(--rot-start); }
    to { opacity: 1; translate: var(--tx) 0; rotate: var(--rot); }
  }

  .hero__stage .device--front {
    animation: settle 1.1s cubic-bezier(0.2, 0.7, 0.2, 1) 0.24s both;
  }

  .hero__stage .device--back {
    animation: settle 1.1s cubic-bezier(0.2, 0.7, 0.2, 1) 0.34s both;
  }
}

@media (max-width: 40rem) {
  .hero__stage {
    height: clamp(17rem, 60vw, 21rem);
  }

  /* Only the angles change. A small screen exaggerates a tilt, and the same composition reads as
     steeper on a phone than it does on a laptop. The positions carry over unchanged. */
  .hero__stage .device--front { --rot: -5deg; --rot-start: -11deg; }
  .hero__stage .device--back { --rot: 6deg; --rot-start: 13deg; }
}

/* ------------------------------------------------------------ the CTAs ---- */

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

.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-4);
  min-height: 52px;
  padding-inline: var(--space-6);
  border-radius: var(--radius-full);
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--step-0);
  text-decoration: none;
  border: 1px solid transparent;
  transition: transform 0.14s ease, background-color 0.2s ease, border-color 0.2s ease,
    box-shadow 0.2s ease;
}

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

.btn--primary {
  background: var(--color-accent-default);
  color: var(--color-text-on-accent);
  box-shadow: 0 10px 30px -12px rgb(232 180 74 / 55%);
}

.btn--primary:hover {
  background: var(--color-accent-pressed);
  color: var(--color-text-on-accent);
  transform: translateY(-2px);
  box-shadow: 0 16px 36px -12px rgb(232 180 74 / 65%);
}

.btn--quiet {
  border-color: var(--color-border-strong);
  color: var(--color-text-primary);
  background: var(--color-surface-raised);
}

.btn--quiet:hover {
  border-color: var(--color-text-secondary);
  color: var(--color-text-primary);
  transform: translateY(-2px);
}

.btn small {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.5625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0.72;
  line-height: 1.4;
}

.btn b {
  display: block;
  font-weight: 600;
  line-height: 1.2;
}

/*
 * Store badges — Apple's and Google's own artwork, not an imitation. Both stores require their
 * official badge and publish rules about clear space and wording; see web/README.md for where these
 * files came from. Neither image is ever edited.
 *
 * Google's PNG has clear space baked into the file while Apple's SVG does not, so rendering both at
 * the same CSS height makes Google's button look visibly smaller. The taller height plus a negative
 * margin cancels that padding, which is layout on our side rather than 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 line under the buttons that removes the last objection before the tap. */
.trust {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-5);
  margin-top: var(--space-6);
}

.trust li {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--step--1);
  color: var(--color-text-secondary);
}

.trust li::before {
  content: "";
  width: 5px;
  height: 5px;
  border-radius: var(--radius-full);
  background: var(--color-status-success);
  flex: none;
}

/* ------------------------------------------------------ the prompter rig ---- */

/*
 * The hero is the product, working: the lens above, the script scrolling in a narrow band beneath
 * it, and the gap between them annotated. That gap is the entire thesis — how far the reader's eyes
 * travel from the words to the camera — so the page states it as a measurement rather than a claim.
 */
.rig {
  position: relative;
  background: var(--color-surface-raised);
  border: var(--rule);
  border-radius: var(--radius-xl);
  padding: var(--space-7) var(--space-6) var(--space-6);
  box-shadow: 0 40px 80px -40px rgb(0 0 0 / 90%);
  overflow: hidden;
}

/* A slow specular sweep, as if a light were passing over a physical device. */
.rig::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
    115deg,
    transparent 38%,
    rgb(245 243 239 / 6%) 48%,
    transparent 58%
  );
  transform: translateX(-120%);
  animation: sweep 9s ease-in-out 1.6s infinite;
}

/* A focus pull. The one thing on a camera that is never quite still. */
@keyframes focus-pull {
  0%, 100% { scale: 1; border-color: var(--color-accent-default); }
  50% { scale: 1.08; border-color: var(--color-accent-pressed); }
}

@keyframes sweep {
  0% { transform: translateX(-120%); }
  55%, 100% { transform: translateX(120%); }
}

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

.rig__dot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: var(--radius-full);
  background: var(--tally);
  animation: tally 2.4s ease-in-out infinite;
}

/* A tally light, which is the one thing on a camera that is always moving. */
@keyframes tally {
  0%, 100% { box-shadow: 0 0 0 0 rgb(241 52 61 / 45%); opacity: 1; }
  50% { box-shadow: 0 0 0 6px rgb(241 52 61 / 0%); opacity: 0.55; }
}

.rig__time {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  color: var(--color-text-secondary);
  font-variant-numeric: tabular-nums;
}

.rig__badge {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: 0.625rem;
  letter-spacing: 0.1em;
  color: var(--color-text-secondary);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-full);
  padding: 0.2rem 0.5rem;
}

/*
 * 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 black 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(1.5rem); }
  to { transform: translateY(-100%); }
}

.rig__foot {
  display: flex;
  gap: var(--space-5);
  margin-top: var(--space-5);
  padding-top: var(--space-5);
  border-top: var(--rule);
}

.rig__foot div {
  display: grid;
  gap: 0.15rem;
}

.rig__foot b {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--color-text-primary);
  font-variant-numeric: tabular-nums;
}

/* -------------------------------------------------------------- section ---- */

section {
  padding-block: clamp(3rem, 7vw, 6rem);
  border-top: var(--rule);
}

.section-head {
  display: grid;
  gap: var(--space-5);
  margin-bottom: clamp(2rem, 5vw, 3.5rem);
  max-width: var(--measure);
}

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

/* --------------------------------------------------------------- claims ---- */

.claims {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: 1px;
  background: var(--color-border-subtle);
  border: var(--rule);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

.claim {
  background: var(--color-surface-raised);
  padding: var(--space-6);
  display: grid;
  gap: var(--space-4);
  align-content: start;
  transition: background-color 0.25s ease;
}

.claim:hover {
  background: var(--color-surface-overlay);
}

.claim h3 {
  font-size: var(--step-1);
  letter-spacing: -0.02em;
}

.claim p {
  color: var(--color-text-secondary);
  font-size: var(--step--1);
  line-height: 1.65;
}

.claim .fact {
  font-family: var(--font-mono);
  font-size: 0.625rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-accent-default);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border-subtle);
}

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

/*
 * A phone, at a size worth looking at, showing the real interface.
 *
 * The first version of this page had no product in it at all — four diagrams and a small panel.
 * For an app whose whole pitch is what the screen looks like while you talk, that was the wrong
 * thing to leave out. Everything here is drawn from the app's own tokens, so it cannot show a
 * colour the app does not have.
 */
.device {
  position: relative;
  /* Sized by height so it always fits the fold. At full column width the handset ran past the
     viewport and its own shutter was cut off. */
  height: min(40rem, 76vh);
  aspect-ratio: 9 / 19;
  margin-inline: auto;
  border-radius: 40px;
  padding: 7px;
  background: linear-gradient(160deg, #2A2A2F, #0B0B0C 60%);
  box-shadow:
    0 2px 0 rgb(245 243 239 / 8%) inset,
    0 60px 90px -50px rgb(0 0 0 / 100%),
    0 0 0 1px rgb(0 0 0 / 60%);
}

.device__screen {
  position: relative;
  height: 100%;
  border-radius: 34px;
  overflow: hidden;
  background: var(--color-surface-canvas);
  display: flex;
  flex-direction: column;
}

/* The camera preview. A wash rather than a photograph — the app is what is being shown here, and
   a stock face would be a picture of somebody who has never used it. */
.device__preview {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(34% 22% at 50% 58%, #5E5E69, transparent 72%),
    radial-gradient(76% 44% at 50% 98%, #40404A, transparent 74%),
    radial-gradient(95% 60% at 50% 4%, #2A2A31, transparent 72%),
    linear-gradient(180deg, #22222899, transparent 30%),
    linear-gradient(180deg, #1F1F23, #0E0E11);
}

/*
 * 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 guides and vignette are what make a rectangle
   read as a viewfinder, and they are the reason this is not just a video in a phone. */
.device__preview--over {
  background: none;
}

/*
 * Translucent where there is footage behind it. Band opacity is a control in the app, so this is
 * the product rather than a liberty — and at full opacity the band sat squarely across the
 * speaker's eyes, hiding the one thing the footage was added to show.
 */
.device__footage ~ .device__band {
  background: rgb(0 0 0 / 46%);
}

.device__footage ~ .device__band p {
  text-shadow: 0 1px 4px rgb(0 0 0 / 95%), 0 0 12px rgb(0 0 0 / 70%);
}

/* Thirds guides and a vignette. Without them the preview was a grey smudge; with them it reads as
   a viewfinder, which is what the reader is being asked to imagine themselves in front of. */
.device__preview::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(90deg, transparent 33.2%, rgb(245 243 239 / 6%) 33.2% 33.5%, transparent 33.5%),
    linear-gradient(90deg, transparent 66.4%, rgb(245 243 239 / 6%) 66.4% 66.7%, transparent 66.7%),
    radial-gradient(120% 90% at 50% 50%, transparent 55%, rgb(0 0 0 / 55%));
}

.device__chrome {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-5) var(--space-4) 0;
  font-family: var(--font-mono);
  font-size: 0.5rem;
  letter-spacing: 0.08em;
  color: var(--color-always-dark-text-secondary);
}

.device__pill {
  border: 1px solid rgb(245 243 239 / 22%);
  border-radius: var(--radius-full);
  padding: 0.15rem 0.4rem;
  color: rgb(245 243 239 / 78%);
}

.device__pill--rec {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  border-color: transparent;
  padding-left: 0;
}

.device__pill--rec::before {
  content: "";
  width: 5px;
  height: 5px;
  border-radius: var(--radius-full);
  background: var(--tally);
  animation: tally 2.4s ease-in-out infinite;
}

.device__spacer { flex: 1; }

/*
 * The lens the whole product is measured from — drawn as the punch-hole it is, at the top edge
 * where phones put it. It used to sit in the flex flow underneath the recording chrome, which left
 * a 26px ring floating in the upper third of the screen, nowhere a camera has ever been.
 *
 * Out of flow, so the reading band below rises to meet it. That is the product: the band sits
 * directly under the lens, and with the lens in the wrong place the band was in the wrong place too.
 */
.device__lens {
  position: absolute;
  top: 3.2%;
  left: 50%;
  translate: -50% 0;
  z-index: 4;
  animation: focus-pull 7s ease-in-out infinite;
  width: 13px;
  height: 13px;
  border-radius: var(--radius-full);
  /* Dark glass with an accent rim: reads as a camera at a glance, and still marks the one point
     every measurement on this page is taken from. */
  border: 1.5px solid var(--color-accent-default);
  background: radial-gradient(circle at 38% 32%, #3A3A42, #000 62%);
  box-shadow: 0 0 0 2px rgb(0 0 0 / 55%), 0 0 10px 1px rgb(232 180 74 / 30%);
}

/* The reading band, directly beneath it. The top margin clears the recording chrome, which is what
   is above it now that the lens is pinned to the screen edge. */
.device__band {
  position: relative;
  margin: var(--space-6) var(--space-3) 0;
  height: 23%;
  border-radius: var(--radius-md);
  background: rgb(0 0 0 / 72%);
  overflow: hidden;
  mask-image: linear-gradient(to bottom, transparent, #000 18%, #000 82%, transparent);
}

.device__band p {
  margin: 0;
  padding: var(--space-4);
  font-size: 0.8125rem;
  font-weight: 600;
  line-height: 1.4;
  color: #fff;
  animation: scroll-script 22s linear infinite;
}

/* The transport, and the shutter. */
.device__transport {
  position: relative;
  margin: auto var(--space-4) var(--space-4);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-full);
  background: rgb(11 11 12 / 78%);
}

.device__transport i {
  display: block;
  width: 8px;
  height: 8px;
  border-radius: 2px;
  background: rgb(245 243 239 / 75%);
  flex: none;
}

.device__transport span {
  flex: 1;
  height: 3px;
  border-radius: var(--radius-full);
  background: linear-gradient(
    90deg,
    var(--color-accent-default) 0 55%,
    rgb(245 243 239 / 22%) 55%
  );
}

.device__shutter {
  position: relative;
  align-self: center;
  margin-bottom: var(--space-6);
  width: 46px;
  height: 46px;
  border-radius: var(--radius-full);
  border: 3px solid rgb(245 243 239 / 92%);
  display: grid;
  place-items: center;
}

.device__shutter::after {
  content: "";
  width: 34px;
  height: 34px;
  border-radius: var(--radius-full);
  background: var(--color-status-record);
}

/* A real screenshot filling the frame. */
.device__shot {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
}

/* ---- the library screen, for the tour ---- */

.device--light .device__screen { background: var(--color-surface-canvas); }

.screen {
  position: relative;
  height: 100%;
  padding: var(--space-6) var(--space-4) var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.screen h4 {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--color-text-primary);
}

.screen__bar {
  height: 26px;
  border-radius: var(--radius-md);
  border: 1px solid var(--color-border-subtle);
}

.screen__row {
  display: grid;
  gap: 5px;
  padding-block: var(--space-3);
  border-bottom: 1px solid var(--color-border-subtle);
}

.screen__row b {
  font-size: 0.6875rem;
  font-weight: 600;
  color: var(--color-text-primary);
}

.screen__row span {
  font-family: var(--font-mono);
  font-size: 0.5rem;
  color: var(--color-text-tertiary);
}

.screen__slider {
  display: grid;
  gap: 6px;
  margin-top: var(--space-3);
}

.screen__slider em {
  font-family: var(--font-mono);
  font-size: 0.5rem;
  font-style: normal;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-tertiary);
}

.screen__slider i {
  display: block;
  height: 4px;
  border-radius: var(--radius-full);
  background: linear-gradient(
    90deg,
    var(--color-accent-default) 0 var(--at, 60%),
    var(--color-surface-overlay) var(--at, 60%)
  );
}

/* ---------------------------------------------------------------- planes ---- */

/*
 * Full-bleed colour, breaking out of the column. The page was near-black from top to bottom with
 * every section the same shape; these are the change of pace, and they carry the two things worth
 * shouting.
 */
.plane {
  border: 0;
  padding-block: clamp(2.5rem, 5vw, 4rem);
}

.plane--amber {
  background: var(--color-accent-default);
  color: var(--color-text-on-accent);
}

.plane--amber .plane__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: var(--space-6);
}

.plane--amber h2 {
  font-size: clamp(1.75rem, 1.2rem + 2.2vw, 2.75rem);
  max-width: 22ch;
  margin-bottom: var(--space-6);
}

.plane--amber dl {
  margin: 0;
  display: grid;
  gap: 0.2rem;
}

.plane--amber dt {
  font-family: var(--font-mono);
  font-size: 0.5625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0.65;
}

.plane--amber dd {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.5rem;
  letter-spacing: -0.02em;
}

.plane--raised {
  background: var(--color-surface-raised);
  border-block: var(--rule);
}

/* ----------------------------------------------------------------- tour ---- */

.tour {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: clamp(1.5rem, 3vw, 2.5rem);
}

/* A column each, so the three captions start on one line however tall the screen above them is. */
.tour figure {
  display: flex;
  flex-direction: column;
  margin: 0;
}

/* The handsets stand on a shared baseline. */
.tour__stage {
  display: grid;
  align-items: end;
  justify-items: center;
  flex: 1;
}

.tour .device {
  height: min(32rem, 62vh);
}

.tour figcaption {
  display: grid;
  gap: var(--space-2);
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: var(--rule);
}

.tour figcaption b {
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--color-text-primary);
}

.tour figcaption span {
  font-size: var(--step--1);
  color: var(--color-text-secondary);
}

/* The recorder is the screen the product is about, so it gets the emphasis — by being the one in
   colour with a face in it, rather than by being nudged out of line with its neighbours. */

/* ------------------------------------------------------ feature graphics ---- */

/*
 * Every claim gets a drawing, because a teleprompter is a visual product and prose cannot show a
 * reading band. These are built from CSS and inline SVG rather than screenshots: they illustrate
 * the real interface, they cost nothing to keep in step with it, and they need no photography.
 */

/* ------------------------------------------------------------- the claims ---- */

/*
 * Four claims, and the count is the structure: 01 is the product thesis so it gets the full width
 * and the side-by-side handsets, and 02–04 are the three proofs that follow, equal to each other.
 * An earlier version gave the four claims three different layouts — two wide rows and a two-up
 * card — which read as three sections rather than one list of four.
 */

.claim {
  position: relative;
  padding: clamp(1.5rem, 3vw, 2.25rem);
  border: var(--rule);
  border-radius: var(--radius-xl);
  background:
    linear-gradient(180deg, rgb(245 243 239 / 3%), transparent 40%),
    var(--color-surface-raised);
}

.claim__no {
  font-family: var(--font-mono);
  font-size: 0.625rem;
  letter-spacing: 0.16em;
  color: var(--color-accent-default);
  margin-bottom: var(--space-4);
}

.claim h3 {
  font-size: var(--step-1);
  line-height: 1.25;
  margin-bottom: var(--space-4);
  max-width: 26ch;
}

/* Scoped to the child combinator: the body copy is styled, the paragraphs inside the diagrams
   below are not. A bare `.claim p` set the colour and measure on both, and then every diagram had
   to fight its way back out. */
.claim > p {
  color: var(--color-text-secondary);
  font-size: var(--step--1);
  max-width: 44ch;
}

.claim .fact {
  display: inline-block;
  margin-top: var(--space-6);
  font-family: var(--font-mono);
  font-size: 0.5625rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-accent-default);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-full);
  padding: 0.35rem 0.7rem;
}

/* Every diagram sits on the same inset panel, so four different drawings still read as one set. */
.claim figure {
  margin: var(--space-6) 0 0;
  padding: var(--space-5);
  border-radius: var(--radius-lg);
  background: var(--color-surface-sunken);
  border: 1px solid var(--color-border-subtle);
}

.claim figcaption {
  margin-top: var(--space-5);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border-subtle);
  font-size: 0.6875rem;
  line-height: 1.6;
  color: var(--color-text-tertiary);
}

/* ---- 01: the thesis ---- */

.claim--lead {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 3.5rem);
  align-items: center;
  margin-bottom: var(--space-6);
}

.claim--lead h3 {
  font-size: var(--step-2);
  max-width: 22ch;
}

.claim--lead > .claim__body > p {
  max-width: 42ch;
}

.claim--lead > .claim__body > p + p {
  margin-top: var(--space-4);
}

/* The three figures that make the claim checkable rather than assertable. */
.spec {
  margin: var(--space-7) 0 0;
  border-top: 1px solid var(--color-border-subtle);
}

/* Rows, not columns. Three figures side by side in a narrow column left every label wrapping
   mid-phrase, which made a spec sheet look like a layout accident. */
.spec > div {
  display: flex;
  align-items: baseline;
  gap: var(--space-5);
  padding-block: var(--space-4);
  border-bottom: 1px solid var(--color-border-subtle);
}

.spec dt {
  flex: 0 0 6.5rem;
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 700;
  color: var(--color-text-primary);
  font-variant-numeric: tabular-nums;
}

.spec > div:last-child {
  border-bottom: 0;
}

.spec dd {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.5625rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  line-height: 1.5;
  color: var(--color-text-tertiary);
}

/* The two handsets. The shared `.device` component rather than a wireframe of one: this is the
   comparison the whole product rests on, and it should not look cheaper than the tour further
   down the page. */
/*
 * Sits directly on the card: the panel treatment the other three diagrams use would put a box
 * inside a box around two objects that already read as objects. Scoped with `.claim` rather than
 * written bare, because `.claim figure` above is a class plus a type selector and outranks a lone
 * class — which is exactly how the handsets first ended up inside the panel.
 */
.claim .versus {
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
}

.versus__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(0.75rem, 2.5vw, 1.75rem);
  align-items: start;
}

.versus .device {
  height: min(23rem, 52vh);
  border-radius: 26px;
  padding: 5px;
  margin-inline: auto;
}

.versus .device__screen { border-radius: 22px; }

.vs__cap {
  font-family: var(--font-mono);
  font-size: 0.5625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  margin-bottom: var(--space-4);
  color: var(--color-text-tertiary);
}

.vs--us .vs__cap { color: var(--color-accent-default); }

/* 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;
  inset: 17% 9% 6%;
  margin: 0;
  font-size: 0.5rem;
  line-height: 2.35;
  font-weight: 600;
  color: rgb(245 243 239 / 66%);
  text-shadow: 0 1px 6px rgb(0 0 0 / 90%);
}

/* Orate's way: the same script, held in the band under the lens. */
.versus .device__band {
  height: 27%;
  background: rgb(0 0 0 / 92%);
  /* Faded at the bottom only. The hero's band fades both ends, which at this size erased the text;
     an unfaded band clipped the last line mid-word, which reads as a bug rather than as a window
     with more script below it. */
  mask-image: linear-gradient(to bottom, #000 74%, rgb(0 0 0 / 15%));
  border: 1px solid rgb(245 243 239 / 12%);
}

.versus .device__band p {
  padding: var(--space-3);
  font-size: 0.5625rem;
  line-height: 1.45;
  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: 8px;
  top: 5%;
  z-index: 2;
  width: 1px;
}

.vs__reach::before,
.vs__reach::after {
  content: "";
  position: absolute;
  left: -2px;
  width: 5px;
  height: 1px;
  background: currentColor;
}

.vs__reach::before { top: 0; }
.vs__reach::after { bottom: 0; }

.vs--them .vs__reach {
  height: 89%;
  color: var(--tally);
  background: rgb(241 52 61 / 70%);
}

.vs--us .vs__reach {
  height: 34%;
  color: var(--color-status-success);
  background: var(--color-status-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. Vertical travel is the axis that carries the meaning, and centred the dot
     sat on top of the very words it was meant to be reading. */
  left: 8%;
  z-index: 3;
  width: 8px;
  height: 8px;
  border-radius: var(--radius-full);
  translate: -50% 0;
  pointer-events: none;
  top: 5%;
}

.vs__gaze::after {
  content: "";
  position: absolute;
  inset: -6px;
  border-radius: var(--radius-full);
  border: 1px solid currentColor;
  opacity: 0.35;
}

.vs--them .vs__gaze {
  background: var(--tally);
  color: var(--tally);
  box-shadow: 0 0 14px 2px rgb(241 52 61 / 55%);
}

.vs--us .vs__gaze {
  background: var(--color-status-success);
  color: var(--color-status-success);
  box-shadow: 0 0 14px 2px rgb(34 197 94 / 45%);
}

@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: 5%; }
    46%, 54% { top: 88%; }
    92%, 100% { top: 5%; }
  }

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

.vs__verdict {
  margin-top: var(--space-4);
  font-family: var(--font-mono);
  font-size: 0.5rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  line-height: 1.5;
}

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

.versus > figcaption {
  margin-top: var(--space-6);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border-subtle);
}

/* ---- 02–04: the three proofs ---- */

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

/* Each proof is a column so its chip can be pushed to the bottom: the three cards are the same
   height, so the chips line up and the row reads as one row. */
.claim-row .claim {
  display: flex;
  flex-direction: column;
}

.claim-row .claim .fact {
  align-self: start;
  margin-top: auto;
}

/* The panel takes the slack rather than leaving it under the diagram, and its caption sits on the
   bottom edge — so the extra height reads as a footer rule instead of an unfinished card. */
.claim-row .claim figure {
  flex: 1;
  display: flex;
  flex-direction: column;
  margin-bottom: var(--space-6);
}

.claim-row .claim figcaption {
  margin-top: auto;
}

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

.tape__axis,
.tape__who,
.tape__out {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.5rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

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

.tape__kill { color: var(--tally); }

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

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

.tape__bar {
  display: flex;
  height: 22px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--color-surface-canvas);
  box-shadow: 0 0 0 1px var(--color-border-subtle) inset;
}

.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-accent-default);
  background-image: repeating-linear-gradient(
    90deg,
    transparent 0 calc(25% - 1px),
    rgb(0 0 0 / 45%) calc(25% - 1px) 25%
  );
}

.tape__lost {
  background-image: repeating-linear-gradient(
    135deg,
    rgb(241 52 61 / 34%) 0 4px,
    transparent 4px 8px
  );
  border-left: 1px solid var(--tally);
}

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

.tape__out--bad { color: var(--tally); }
.tape__out--good { color: var(--color-status-success); }

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

.pace__head {
  margin: 0 0 var(--space-4);
  font-family: var(--font-mono);
  font-size: 0.5rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-text-tertiary);
}

.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: 600;
  font-size: 0.8125rem;
  color: var(--color-text-primary);
}

.pace__rate {
  font-family: var(--font-mono);
  font-size: 0.5rem;
  letter-spacing: 0.08em;
  color: var(--color-text-tertiary);
}

.pace__time {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  color: var(--color-accent-default);
}

.pace__track {
  height: 8px;
  border-radius: var(--radius-full);
  background: var(--color-surface-canvas);
  box-shadow: 0 0 0 1px var(--color-border-subtle) inset;
  overflow: hidden;
}

.pace__fill {
  display: block;
  width: var(--w);
  height: 100%;
  border-radius: inherit;
  /* Both stops are bright. Starting on `accent-subtle` (#523A0E) against the #0B0B0C track made
     the first fifth of every bar invisible, so three bars that do start together looked as though
     they began at three different places — the one thing a comparison chart must not do. */
  background: linear-gradient(90deg, var(--color-accent-default), var(--color-accent-pressed));
}

@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-4);
  font-family: var(--font-mono);
  font-size: 0.5rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-text-tertiary);
}

.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 rather
     than as a diagram with a gap under it. */
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}

.ledger li {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
  padding-block: var(--space-4);
  font-size: 0.8125rem;
  color: var(--color-text-secondary);
}

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

.ledger b {
  flex: none;
  white-space: nowrap;
  font-family: var(--font-mono);
  font-size: 0.5rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-align: right;
  color: var(--color-status-success);
}

/* 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 { color: var(--color-accent-default); }

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

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

/*
 * Below this the two handsets are sized by the column rather than by their own height. Height-driven
 * they have a min-content width of their own, and two of them plus the gap were wider than a 390px
 * phone's column — a real 16px horizontal overflow on the whole page. Stacking them was the other
 * option and the wrong one: side by side *is* the comparison.
 */
@media (max-width: 40rem) {
  .versus .device {
    height: auto;
    width: 100%;
  }
}

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

/*
 * Four devices, four recorder layouts, drawn as diagrams rather than as photographs.
 *
 * The first version put the real footage in all four viewfinders, and it was the wrong instinct:
 * the same face at size four times over read as wallpaper, buried the one thing the section is
 * about, and cost most of a screen to say it. What matters here is *where the reading window and
 * the controls go* at each width — so that is all each shape carries, small enough that the four
 * sit in one row and can be compared at a glance. The footage stays where it earns its keep.
 */

.rigs {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(1.25rem, 3vw, 2.25rem);
  margin-top: clamp(2rem, 4vw, 3rem);
}

/* 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;
  /* Taller than the tallest shape, or that shape pushes its own caption out of line with the
     other three — which is the whole point of a shared baseline. */
  min-height: 15.5rem;
  padding-bottom: var(--space-5);
}

.rigs__item {
  margin: 0;
  display: flex;
  flex-direction: column;
}

.rigs figcaption {
  display: grid;
  gap: var(--space-2);
  padding-top: var(--space-5);
  border-top: var(--rule);
}

.rigs figcaption b {
  font-family: var(--font-display);
  font-size: var(--step-0);
}

.rigs figcaption em {
  font-family: var(--font-mono);
  font-style: normal;
  font-size: 0.4375rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-accent-default);
}

.rigs figcaption span {
  font-size: 0.75rem;
  line-height: 1.6;
  color: var(--color-text-secondary);
}

/* Sentence case. Forty words of uppercase mono is a caption pretending to be a paragraph. */
.rigs__note {
  margin-top: clamp(1.5rem, 4vw, 2.5rem);
  padding-top: var(--space-6);
  border-top: var(--rule);
  font-size: var(--step--1);
  line-height: 1.7;
  color: var(--color-text-tertiary);
  max-width: 64ch;
}

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

.frame {
  position: relative;
  overflow: hidden;
  /* The same viewfinder wash the handsets use, so a diagram still reads as a screen. */
  background:
    radial-gradient(70% 40% at 50% 62%, #3A3A44, transparent 74%),
    linear-gradient(180deg, #1E1E23, #101014);
  box-shadow:
    0 0 0 4px #191A1F,
    0 0 0 5px rgb(245 243 239 / 10%),
    0 18px 34px -18px rgb(0 0 0 / 95%);
}

/* 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: 15rem;
  aspect-ratio: 4 / 3;
  border-radius: 11px;
}

.frame--tab-port {
  height: 14rem;
  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-accent-default);
  box-shadow: 0 0 0 2px rgb(11 11 12 / 80%), 0 0 9px 2px rgb(232 180 74 / 55%);
}

/* Top edge, centred: both portrait shapes, and the landscape tablet whose camera is on the long
   edge. */
.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, and
   the question the diagram answers is which part of the screen holds them. */
.frame__band {
  position: absolute;
  z-index: 2;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  padding: 7px;
  border-radius: 5px;
  background: rgb(232 180 74 / 14%);
  border: 1px solid rgb(232 180 74 / 45%);
}

.frame__band i {
  display: block;
  height: 2px;
  border-radius: var(--radius-full);
  background: rgb(232 180 74 / 80%);
}

.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: rgb(0 0 0 / 55%);
  border: 1px solid rgb(245 243 239 / 12%);
}

.frame__rail i {
  width: 4px;
  height: 4px;
  border-radius: 1px;
  background: rgb(245 243 239 / 60%);
}

/* The shutter, always the largest target on the rail. */
.frame__rail i:nth-child(2) {
  width: 9px;
  height: 9px;
  border-radius: var(--radius-full);
  background: var(--color-status-record);
  box-shadow: 0 0 0 1.5px rgb(245 243 239 / 80%);
}

.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));
    gap: clamp(1.5rem, 4vw, 2.5rem);
  }
}

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

  .rigs__stage {
    min-height: 0;
  }
}

/* ------------------------------------------------------- the settings screen ---- */

/*
 * The third screen, drawn. It was a screenshot, and at a third of a column the real one is a stack
 * of dim grey rows with the values unreadable — accurate and unappealing at the same time. Drawn, it
 * can carry the few settings worth showing at a size someone will actually read.
 */
.prefs {
  position: relative;
  height: 100%;
  text-align: left;
  padding: var(--space-5) var(--space-4) 0;
  background: var(--color-surface-canvas);
  overflow: hidden;
}

.prefs__bar {
  display: flex;
  align-items: baseline;
  margin-bottom: var(--space-5);
}

.prefs__bar b {
  font-family: var(--font-display);
  font-size: 0.9375rem;
  letter-spacing: -0.02em;
}

.prefs__bar span {
  margin-left: auto;
  font-size: 0.625rem;
  font-weight: 600;
  color: var(--color-accent-default);
}

.prefs__group {
  margin: var(--space-5) 0 var(--space-3);
  font-family: var(--font-mono);
  font-size: 0.4375rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-text-tertiary);
}

.prefs__group:first-of-type { margin-top: 0; }

/* The appearance control, which is the one thing on the screen with a chosen state. */
.prefs__seg {
  display: flex;
  gap: 2px;
  padding: 2px;
  border-radius: 8px;
  background: var(--color-surface-sunken);
  border: 1px solid var(--color-border-subtle);
}

.prefs__seg i {
  flex: 1;
  text-align: center;
  padding: 4px 0;
  border-radius: 6px;
  font-style: normal;
  font-size: 0.5rem;
  font-weight: 600;
  color: var(--color-text-tertiary);
}

.prefs__seg .is-on {
  background: var(--color-accent-default);
  color: var(--color-text-on-accent);
}

.prefs__row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 7px 9px;
  border-radius: 8px;
  background: var(--color-surface-raised);
  border: 1px solid var(--color-border-subtle);
  font-size: 0.5625rem;
  color: var(--color-text-primary);
}

.prefs__row + .prefs__row { margin-top: 4px; }

.prefs__row b {
  margin-left: auto;
  font-weight: 500;
  color: var(--color-accent-default);
  white-space: nowrap;
}

/* Off, and drawn off — the diagnostics setting is opt-in and the mock should not imply otherwise. */
.prefs__switch {
  position: relative;
  margin-left: auto;
  flex: none;
  width: 20px;
  height: 11px;
  border-radius: var(--radius-full);
  background: var(--color-surface-sunken);
  border: 1px solid var(--color-border-strong);
}

.prefs__switch::after {
  content: "";
  position: absolute;
  top: 1px;
  left: 1px;
  width: 7px;
  height: 7px;
  border-radius: var(--radius-full);
  background: var(--color-text-tertiary);
}

.prefs__note {
  margin: var(--space-3) 0 0;
  padding-inline: 2px;
  font-size: 0.4375rem;
  line-height: 1.6;
  color: var(--color-text-tertiary);
}

/* ---------------------------------------------------------- the shelf ---- */

/*
 * The script library as a staggered two-column shelf. The hero's second handset previously carried
 * a screenshot of the single-column list, which at this size and angle was a stack of grey bars
 * with the titles cut off by the handset in front of it. Cards of unequal height give the eye
 * something to read at a glance instead.
 */
.shelf {
  position: relative;
  height: 100%;
  /* The hero centres its own text, and a screen inside it must not inherit that. */
  text-align: left;
  padding: var(--space-5) var(--space-3) 0;
  background: var(--color-surface-canvas);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.shelf__bar {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding-inline: var(--space-2);
}

.shelf__bar b {
  font-family: var(--font-display);
  font-size: 0.9375rem;
  letter-spacing: -0.02em;
}

.shelf__count {
  font-family: var(--font-mono);
  font-size: 0.4375rem;
  padding: 0.1rem 0.3rem;
  border-radius: var(--radius-full);
  border: 1px solid var(--color-border-subtle);
  color: var(--color-text-tertiary);
}

.shelf__add {
  margin-left: auto;
  font-size: 0.875rem;
  line-height: 1;
  color: var(--color-accent-default);
}

/* Two columns, and the stagger falls out of the cards being different heights rather than being
   staged with offsets. */
/*
 * Two columns, and the stagger falls out of the cards being different heights rather than being
 * staged with offsets. Six cards is the most that fits: the flex parent gives this box a definite
 * height, so multicol balances into a third column and the overflow clips it — a seventh card does
 * not appear, it disappears.
 */
.shelf__grid {
  columns: 2;
  column-gap: 6px;
  overflow: hidden;
}

.slip {
  break-inside: avoid;
  margin-bottom: 6px;
  padding: 8px 9px 9px;
  border-radius: 10px;
  background: var(--color-surface-raised);
  border: 1px solid var(--color-border-subtle);
}

/* Sized to be read, not to be accurate to a 6pt screenshot. This handset is 300px wide on the page
   and the previous sizes were faithful to the app and illegible on the site — the card only earns
   its place in the hero if the title can actually be read from a metre away. */
.slip h5 {
  margin: 0 0 4px;
  font-size: 0.6875rem;
  font-weight: 600;
  line-height: 1.25;
  color: var(--color-text-primary);
}

.slip p {
  margin: 0 0 7px;
  font-size: 0.5625rem;
  line-height: 1.5;
  color: var(--color-text-secondary);
}

.slip span {
  font-family: var(--font-mono);
  font-size: 0.4375rem;
  letter-spacing: 0.04em;
  color: var(--color-text-tertiary);
}

/* -------------------------------------------------------------- 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 competitor gets this right and it costs almost nothing.
 */
.qr {
  position: fixed;
  left: var(--space-6);
  bottom: var(--space-6);
  z-index: 20;
  display: grid;
  gap: var(--space-3);
  justify-items: center;
  padding: var(--space-4);
  border-radius: var(--radius-lg);
  background: var(--color-surface-raised);
  border: var(--rule);
  box-shadow: 0 20px 40px -20px rgb(0 0 0 / 90%);
  color: var(--color-text-primary);
  text-decoration: none;
  transition: transform 0.16s ease, border-color 0.2s ease;
}

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

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

.qr span {
  font-family: var(--font-mono);
  font-size: 0.5rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
}

/* No room for it beside the content on a phone — and pointless there, since the reader is already
   holding the thing they would scan it with. */
@media (max-width: 64rem) {
  .qr {
    display: none;
  }
}

/* ---------------------------------------------------------------- steps ---- */

.steps {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: var(--space-6);
  counter-reset: step;
}

.step {
  counter-increment: step;
  display: grid;
  gap: var(--space-4);
  align-content: start;
  padding-top: var(--space-6);
  border-top: 2px solid var(--color-border-strong);
  transition: border-color 0.3s ease;
}

.step:hover {
  border-top-color: var(--color-accent-default);
}

.step::before {
  content: counter(step, decimal-leading-zero);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.12em;
  color: var(--color-accent-default);
}

.step h3 {
  font-size: var(--step-1);
}

.step p {
  color: var(--color-text-secondary);
  font-size: var(--step--1);
  line-height: 1.65;
}

/* -------------------------------------------------------------- controls ---- */

/*
 * Every control the prompter has, with its real range and its real starting point. Ranges come from
 * `PrompterLimits` and `PrompterSettings.default` — the marker on each track sits where the app
 * actually starts, which is the only reason showing a track beats printing two numbers.
 */

.dials {
  display: grid;
  /* Three columns with the first spanning two: five cards divide by three only if one of them is
     wider, and the widest belongs to the control people reach for first. Auto-fit left the fifth
     card stranded on a row of its own. */
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1rem, 2.5vw, 1.75rem);
  margin-top: clamp(2rem, 4vw, 3rem);
  list-style: none;
  padding: 0;
}

.dial {
  display: flex;
  flex-direction: column;
  padding: clamp(1.1rem, 2.2vw, 1.5rem);
  border-radius: var(--radius-xl);
  border: 1px solid var(--color-border-subtle);
  background:
    linear-gradient(180deg, rgb(245 243 239 / 3%), transparent 44%),
    var(--color-surface-raised);
}

.dials > .dial:first-child {
  grid-column: span 2;
}

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

.dial__head b {
  font-family: var(--font-display);
  font-size: var(--step-1);
  letter-spacing: -0.01em;
}

.dial__head em {
  margin-left: auto;
  font-family: var(--font-mono);
  font-style: normal;
  font-size: 0.5rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-accent-default);
  white-space: nowrap;
}

/* The track, with a thumb where the default sits. */
.dial__track {
  position: relative;
  height: 6px;
  border-radius: var(--radius-full);
  background: var(--color-surface-canvas);
  box-shadow: 0 0 0 1px var(--color-border-subtle) inset;
}

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

.dial__track span::after {
  content: "";
  position: absolute;
  top: 50%;
  right: 0;
  width: 14px;
  height: 14px;
  translate: 50% -50%;
  border-radius: var(--radius-full);
  background: var(--color-text-primary);
  box-shadow: 0 2px 6px rgb(0 0 0 / 60%);
}

/* The one control that is a switch rather than a slider, drawn off, which is what it is. */
.dial__switch {
  position: relative;
  width: 34px;
  height: 18px;
  border-radius: var(--radius-full);
  background: var(--color-surface-canvas);
  box-shadow: 0 0 0 1px var(--color-border-strong) inset;
}

.dial__switch::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 12px;
  height: 12px;
  border-radius: var(--radius-full);
  background: var(--color-text-tertiary);
}

.dial__at {
  margin: var(--space-4) 0 0;
  font-family: var(--font-mono);
  font-size: 0.5rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-text-tertiary);
}

.dial__note {
  margin: var(--space-4) 0 0;
  font-size: 0.8125rem;
  line-height: 1.65;
  color: var(--color-text-secondary);
}

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

.tiers-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.85fr);
  gap: clamp(1.5rem, 4vw, 3rem);
  align-items: start;
  margin-top: clamp(2rem, 4vw, 3rem);
}

/* The column, so the grid keeps exactly two children. The note was landing as a third and getting
   dealt into the right-hand column on its own, which pushed the panel below it into a new row. */
.specs-col {
  display: flex;
  flex-direction: column;
}

.specs {
  list-style: none;
  margin: 0;
  padding: 0;
}

.specs li {
  display: grid;
  gap: var(--space-3);
  padding-block: clamp(1.25rem, 2.5vw, 1.75rem);
  border-bottom: var(--rule);
}

.specs li:first-child {
  padding-top: 0;
}

.specs b {
  font-family: var(--font-display);
  font-size: var(--step-2);
  line-height: 1;
  color: var(--color-accent-default);
  letter-spacing: -0.02em;
}

.specs span {
  font-size: var(--step--1);
  line-height: 1.7;
  color: var(--color-text-secondary);
  max-width: 46ch;
}

/*
 * One line, and it earns its place: the three capabilities above it 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. That is the specific setup for a one-star review — and the App Store shows an
 * "In-App Purchases" badge on the listing regardless, so the silence would not even be believed.
 */
.specs__note {
  margin: clamp(1.25rem, 2.5vw, 1.75rem) 0 0;
  font-family: var(--font-mono);
  font-size: 0.5rem;
  letter-spacing: 0.06em;
  line-height: 1.9;
  text-transform: uppercase;
  color: var(--color-text-tertiary);
  max-width: 62ch;
}

/*
 * The half of a capture claim that usually goes unsaid. Every other app on this shelf lists
 * resolutions; the thing that decides whether you keep the take is what the app does when the phone
 * runs out of room or gets too hot, so that gets equal billing rather than a footnote.
 */
.guard {
  padding: clamp(1.25rem, 2.5vw, 1.75rem);
  border-radius: var(--radius-xl);
  border: 1px solid var(--color-border-subtle);
  background: var(--color-surface-sunken);
}

.guard dl {
  margin: var(--space-5) 0 0;
}

.guard dl > div {
  padding-block: var(--space-4);
  border-top: 1px solid var(--color-border-subtle);
}

.guard dt {
  font-size: 0.875rem;
  font-weight: 600;
  margin-bottom: var(--space-2);
}

.guard dd {
  margin: 0;
  font-size: 0.75rem;
  line-height: 1.65;
  color: var(--color-text-tertiary);
}

.guard__note {
  margin: var(--space-5) 0 0;
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border-subtle);
  font-family: var(--font-mono);
  font-size: 0.5rem;
  letter-spacing: 0.06em;
  line-height: 1.9;
  text-transform: uppercase;
  color: var(--color-text-tertiary);
}

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

  .dials > .dial:first-child {
    grid-column: span 2;
  }
}

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

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

  .dials > .dial:first-child {
    grid-column: auto;
  }
}

/* ---------------------------------------------------------------- langs ---- */

/*
 * Ten languages, shown rather than listed.
 *
 * This was a row of ten small pills of language names set in Martian Mono — which carries no Indic
 * or Arabic glyphs, so half the list fell back to whatever the system had, at 11px, and the section
 * that claims full translation was the least convincing thing on the page.
 *
 * Each card now leads with the language in its own script, over a letter of that script as a
 * watermark, and carries the pace `PacingModel` reads it at — real product data rather than
 * decoration, and the reason the same three hundred words take longer in Tamil than in English.
 * **If those rates change, these cards change with them.**
 */
.langs {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(0.75rem, 1.6vw, 1.1rem);
  margin-top: clamp(2rem, 4vw, 3rem);
  list-style: none;
  padding: 0;
}

/*
 * English runs the full width, which is what makes ten cards fit a three-column grid without a
 * ragged last row — and it earns the place: it is the line the other nine are translations of, so
 * reading it first is the point rather than a way of filling a gap.
 */
.lang--lead {
  grid-column: 1 / -1;
}

.lang {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  min-height: 8.25rem;
  padding: clamp(1.1rem, 2.2vw, 1.45rem);
  border-radius: var(--radius-xl);
  border: 1px solid var(--color-border-subtle);
  background:
    linear-gradient(180deg, rgb(245 243 239 / 3%), transparent 46%),
    var(--color-surface-raised);
  transition: border-color 0.25s ease, background-color 0.25s ease;
}

/*
 * The watermark, and the reason these are cards rather than rows: a script is a set of shapes
 * before it is a language, and at this size one letter of it says so faster than a label can. Each
 * card carries a letter of its own script — Hindi and Marathi draw different letters of the same
 * Devanagari, which is truer to how the two look on a page than repeating one glyph would be. The
 * one rule is that the letter has to have a shape: Arabic carries ain rather than alif, because
 * alif is a bare vertical stroke and reads as a stray line at this size.
 */
.lang__glyph {
  position: absolute;
  top: 50%;
  /* Inside the card, not running off it: clipped at the edge it read as an accident rather than as
     a watermark. */
  right: clamp(1rem, 2.2vw, 1.75rem);
  translate: 0 -50%;
  z-index: 0;
  font-size: clamp(3.5rem, 6vw, 5.25rem);
  font-weight: 700;
  line-height: 0.8;
  color: rgb(245 243 239 / 7%);
  pointer-events: none;
  user-select: none;
  transition: color 0.25s ease;
}

/* The right-to-left card reads right-to-left throughout: watermark where that script starts, label
   ranged to the same edge as the line under it. Mirroring only the glyph left it sitting behind the
   label. */
.lang--rtl .lang__glyph {
  right: auto;
  left: clamp(1.25rem, 3vw, 2.25rem);
}

.lang--rtl .lang__meta {
  justify-content: flex-end;
  flex-direction: row-reverse;
}

.lang--rtl .lang__name {
  margin-inline: auto 0;
  text-align: right;
}

/* Ain descends below its baseline, and the tight line box the other glyphs need was clipping it
   against the card edge. */
.lang--rtl .lang__glyph {
  font-size: clamp(3rem, 5vw, 4.25rem);
  line-height: 1.15;
}

/*
 * The language is the card. An earlier version led with the app's own opening line translated ten
 * times, which is a better proof and a worse card: unlabelled, the same sentence in ten scripts
 * reads as a puzzle rather than as evidence. The claim now sits in the lede and the note under the
 * grid, where it can be stated in words, and the card does the one job a card is good at.
 */
/* Kept clear of the watermark: "Bahasa Indonesia" at this size ran straight through its own B. */
.lang__name {
  position: relative;
  z-index: 1;
  margin: 0 0 var(--space-4);
  max-width: calc(100% - 3rem);
  font-family: var(--font-body);
  font-size: var(--step-2);
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.01em;
  color: var(--color-text-primary);
}

.lang--lead .lang__name {
  font-size: var(--step-3);
  max-width: calc(100% - 5rem);
}

.lang__meta {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  margin: auto 0 0;
}

.lang__meta span {
  font-family: var(--font-mono);
  font-size: 0.5rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-text-tertiary);
  transition: color 0.25s ease;
}

.lang__meta em {
  font-family: var(--font-mono);
  font-style: normal;
  font-size: 0.5rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-accent-default);
}

/*
 * The body stack, not the mono one: its fallbacks are what actually render Devanagari, Bengali,
 * Tamil, Telugu and Arabic, and at this size the scripts have room to be read rather than guessed
 * at. Line height is generous because Indic scripts stack marks above and below the baseline.
 */
.lang--lead .lang__glyph {
  font-size: clamp(4.5rem, 8vw, 7rem);
}

@media (hover: hover) {
  .lang:hover {
    border-color: var(--color-border-strong);
  }

  .lang:hover .lang__glyph {
    color: rgb(232 180 74 / 15%);
  }

  .lang:hover .lang__meta span {
    color: var(--color-text-secondary);
  }
}

.langs__note {
  margin-top: clamp(1.75rem, 4vw, 2.5rem);
  font-size: var(--step--1);
  line-height: 1.7;
  color: var(--color-text-tertiary);
  max-width: 66ch;
}

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

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

/* ------------------------------------------------------------ close CTA ---- */

.close-cta {
  text-align: center;
  display: grid;
  justify-items: center;
  gap: var(--space-6);
  padding-block: clamp(4rem, 9vw, 7rem);
  position: relative;
}

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

.close-cta .actions {
  justify-content: center;
}

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

/* --------------------------------------------------------------- privacy ---- */

/*
 * The policy page. Its own small component set rather than borrowed ones, because a table of what
 * leaves the device is the one place on this site where a real table is the right element — three
 * columns of facts that have to be read across, not a layout.
 */

.policy-date {
  margin-top: var(--space-6);
  font-family: var(--font-mono);
  font-size: 0.5625rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-text-tertiary);
}

/* Wide content scrolls inside its own container so the page body never does. */
.table-scroll {
  overflow-x: auto;
  margin-top: clamp(1.5rem, 3vw, 2.25rem);
  border: var(--rule);
  border-radius: var(--radius-lg);
  background: var(--color-surface-raised);
}

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

.policy-table caption {
  caption-side: top;
  padding: var(--space-5) var(--space-6) 0;
  font-family: var(--font-mono);
  font-size: 0.5rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-text-tertiary);
  text-align: left;
}

.policy-table th {
  padding: var(--space-5) var(--space-6);
  font-family: var(--font-mono);
  font-size: 0.5rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-accent-default);
  border-bottom: var(--rule);
}

.policy-table td {
  padding: var(--space-5) var(--space-6);
  font-size: var(--step--1);
  line-height: 1.6;
  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);
  font-weight: 600;
}

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

.policy-note {
  margin-top: var(--space-6);
  font-size: var(--step--1);
  line-height: 1.7;
  color: var(--color-text-tertiary);
  max-width: 68ch;
}

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

.faq {
  display: grid;
  gap: 1px;
  background: var(--color-border-subtle);
  border: var(--rule);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

.faq > div {
  background: var(--color-surface-raised);
  padding: var(--space-6);
  display: grid;
  gap: var(--space-4);
}

.faq h3 {
  font-size: var(--step-1);
  letter-spacing: -0.02em;
}

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

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

/*
 * A tape running down the page, filling with scroll. On-theme rather than decorative: the product
 * measures things in takes and timecode, and this is the page's own timecode.
 */
.reel {
  position: fixed;
  top: 0;
  left: 0;
  width: 2px;
  height: 100vh;
  z-index: 30;
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    var(--color-accent-default) 0 var(--progress, 0%),
    transparent var(--progress, 0%)
  );
}

@supports (animation-timeline: scroll()) {
  .reel {
    background: var(--color-accent-default);
    transform-origin: top center;
    animation: reel linear both;
    animation-timeline: scroll(root block);
  }

  @keyframes reel {
    from { scale: 1 0; }
    to { scale: 1 1; }
  }
}

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

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

footer {
  border-top: var(--rule);
  padding-block: var(--space-8) var(--space-11);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-6);
  align-items: baseline;
  color: var(--color-text-tertiary);
  font-size: var(--step--1);
}

footer nav {
  margin-left: auto;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-6);
}

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

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

/* ------------------------------------------------------------ page load ---- */

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

  .reveal:nth-child(1) { animation-delay: 0.05s; }
  .reveal:nth-child(2) { animation-delay: 0.13s; }
  .reveal:nth-child(3) { animation-delay: 0.21s; }
  .reveal:nth-child(4) { animation-delay: 0.29s; }
  .reveal:nth-child(5) { animation-delay: 0.37s; }

  .rig {
    animation: rig-in 1s cubic-bezier(0.2, 0.7, 0.2, 1) 0.18s both;
  }

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

  @keyframes rise {
    from {
      opacity: 0;
      transform: translateY(16px);
    }
  }

  @keyframes rig-in {
    from {
      opacity: 0;
      transform: translateY(26px) scale(0.97);
    }
  }
}

/* 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 26%;
    }

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

@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 band still shows the script, held still and readable. */
  .device__band p {
    animation: none;
    transform: none;
  }

  .device__band {
    height: auto;
    mask-image: none;
  }

  .aurora,
  .rig::before,
  .rig__dot,
  .device__pill--rec::before,
  .device__lens {
    animation: none;
  }

  /* The gaze carries meaning, so it is parked at the far end of each reach rather than removed: the
     distance is still legible, it simply does not move. Parking both at the start would flatten the
     one thing the pair exists to show. */
  .vs--them .vs__gaze { top: 88%; }
  .vs--us .vs__gaze { top: 30%; }

  /*
   * The footage becomes its own poster frame. Kept in CSS rather than reached for with a script,
   * which is the page's standing trade — the caveat being that a browser which does not pause
   * `display: none` media keeps decoding it silently. Chrome and Safari both stop it.
   */
  .device__footage {
    display: none;
  }

  .device__preview--over {
    background-image: url("assets/footage/viewfinder.jpg");
    background-size: cover;
    background-position: center;
  }

  body::after {
    display: none;
  }
}
