/* ---------------------------------------------------------------------------
 * HYPER C — site layout
 *
 * Every color, radius and easing below comes from tokens.css. The only literals
 * here live in the :root block immediately following, which names the values
 * this page derives from the palette. scripts/integrity-check.mjs enforces that
 * rule: a color literal outside :root fails the build.
 *
 * Type sizes are a deliberate exception. The showcase design works on a finer
 * ramp than the token scale carries (14, 16, 18, 21, 30, 40 …), so those sizes
 * appear as literals where the design specifies one and as tokens where the
 * scale already has it. Sizes are not themeable the way color is — nothing
 * downstream reads them — so naming a token per heading would add vocabulary
 * without adding meaning.
 * ------------------------------------------------------------------------- */

:root {
  /* The bar is glass rather than opaque: a phone runs underneath it for six
   * screens, and seeing it pass behind the blur is the point. */
  --topbar-bg: rgba(10, 13, 18, 0.88);
  /* The progress strip sits below the bar and reads as a shelf under it —
   * darker than the bar so the two do not merge into one slab. */
  --strip-bg: rgba(5, 7, 10, 0.6);
  /* The tint a scroll-spied nav link wears. Heavier than --brand-subtle, and
   * deliberately so: it sits on blurred glass over a moving page, where the
   * 0.12 wash the flat surfaces use reads as a smudge rather than a marker. */
  --nav-active-bg: rgba(0, 123, 255, 0.28);
  /* And a hairline to give the pill an edge of its own — on glass, fill alone
   * shifts with whatever is passing underneath it. */
  --nav-active-border: rgba(0, 123, 255, 0.45);
  /* The mark behind hyper-c. The brand blue at half strength: at full it is a
   * button-coloured slab in the middle of the headline and the eye lands on it
   * before the sentence. Halved, it still reads as the brand and lets the
   * canvas through, which is what a selection looks like. */
  --wordmark-mark: rgba(0, 123, 255, 0.5);
}

html {
  color-scheme: dark;
  scroll-behavior: smooth;
  /* Clears the fixed bar plus the progress strip it grows when the showcase is
   * on screen, so an anchored section never lands underneath either. */
  scroll-padding-top: 120px;
}

body {
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

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

/* padding-inline, not the padding shorthand. Several blocks are both a
 * .container and something that sets its own vertical padding — the footer
 * bar, the hero, the closing panel — and a shorthand here resets theirs to
 * zero from further down the file. That is exactly what happened to the
 * footer at narrow widths: it collapsed onto its own text and the last line
 * sat flush against the bottom of the screen. */
.container {
  width: 100%;
  max-width: var(--container-max);
  margin: 0 auto;
  padding-inline: var(--space-8);
}

.skip-link {
  position: absolute;
  left: var(--space-4);
  top: calc(var(--space-4) * -6);
  z-index: 20;
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  background: var(--bg-elevated);
  border: 1px solid var(--border-default);
  color: var(--text-primary);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  text-decoration: none;
  transition: top var(--duration-base) var(--ease-standard);
}

.skip-link:focus {
  top: var(--space-4);
}

/* Focus is always visible and always the brand ring — never removed. */
a:focus-visible,
button:focus-visible,
.btn:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: var(--radius-sm);
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: 10px var(--space-4);
  font-family: var(--font-body);
  font-size: var(--text-base);
  font-weight: var(--weight-semibold);
  line-height: 1;
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  text-decoration: none;
  cursor: pointer;
  transition:
    background var(--duration-fast) var(--ease-standard),
    transform var(--duration-fast) var(--ease-standard);
}

.btn:active {
  transform: scale(0.97);
}

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

.btn-primary:hover {
  background: var(--brand-hover);
  color: var(--text-on-brand);
}

.btn-primary:active {
  background: var(--brand-active);
}

.btn-secondary {
  background: transparent;
  color: var(--text-primary);
  border-color: var(--border-default);
}

.btn-secondary:hover {
  background: var(--brand-subtle);
  color: var(--text-primary);
}

.btn-sm {
  padding: 9px 18px;
  font-size: 14px;
  border-radius: var(--radius-md);
}

.btn-lg {
  padding: 13px var(--space-6);
  font-size: var(--text-base);
}

/* Badge — mono, pill, hairline. The dot carries the state color. */
.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 6px 14px;
  border-radius: var(--radius-full);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  background: var(--bg-surface);
  color: var(--text-secondary);
  border: 1px solid var(--border-subtle);
}

.badge-dot {
  width: 6px;
  height: 6px;
  border-radius: var(--radius-full);
  background: var(--success);
  flex: none;
}

/* Status pills are filled containers rather than washes — see tokens.css. */
.pill {
  display: inline-flex;
  align-items: center;
  flex: none;
  padding: 5px 12px;
  border-radius: var(--radius-full);
  font-family: var(--font-mono);
  font-size: 10.5px;
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  white-space: nowrap;
}

.pill-brand {
  background: var(--brand-container);
  color: var(--on-brand-container);
}

.pill-warning {
  background: var(--warning-container);
  color: var(--warning);
}

.pill-neutral {
  background: var(--neutral-container);
  color: var(--on-neutral-container);
}

.card {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  padding: 28px;
}

.section-label {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-secondary);
  margin: 0;
}

/* The one gradient the system allows. Positioned by the modifier classes. */
.glow {
  position: absolute;
  border-radius: 50%;
  background: radial-gradient(circle, var(--glow-brand), transparent 65%);
  pointer-events: none;
}

/* --- topbar -------------------------------------------------------------- */

.topbar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 10;
  background: var(--topbar-bg);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--border-subtle);
}

.topbar-inner {
  display: flex;
  align-items: center;
  gap: 28px;
  height: 64px;
}

.brand {
  display: inline-flex;
  align-items: center;
  flex: none;
  text-decoration: none;
}

/* Sized by the tile, which is the lockup's full height — so the C sits at the
 * 22px the bar's type would have, and the wordmark comes along at whatever
 * ratio the generator set. Width follows, and the intrinsic width/height
 * attributes reserve the space before the bytes land. */
.brand-logo {
  display: block;
  height: 22px;
  width: auto;
  flex: none;
}

.topbar-nav {
  margin-left: auto;
}

.topbar-nav ul {
  display: flex;
  gap: var(--space-1);
  list-style: none;
  margin: 0;
  padding: 0;
}

.topbar-nav a {
  display: block;
  padding: 7px var(--space-3);
  border-radius: var(--radius-md);
  font-size: 14px;
  font-weight: var(--weight-semibold);
  color: var(--text-secondary);
  text-decoration: none;
  transition:
    color var(--duration-base) var(--ease-standard),
    background var(--duration-base) var(--ease-standard);
}

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

/* Set by showcase.js when the reader reaches the closing panel the button
 * points at. The glow is the design system's own brand halo rather than a
 * second blue: the button is already --brand, so lighting it is a matter of
 * giving it an edge and a bloom, not a new fill. */
.topbar .btn-primary {
  /* Scoped to this one button rather than added to .btn: every button uses
   * box-shadow for its focus ring, and a ring that fades in is a worse focus
   * indicator than one that simply appears. --duration-base keeps even this
   * one's ring quick. */
  transition:
    background var(--duration-fast) var(--ease-standard),
    transform var(--duration-fast) var(--ease-standard),
    box-shadow var(--duration-base) var(--ease-standard);
}

.topbar .btn-primary.is-lit {
  box-shadow: var(--shadow-glow-brand);
}

/* Set by showcase.js as the reader passes each section. */
.topbar-nav a.is-active {
  color: var(--text-primary);
  background: var(--nav-active-bg);
  box-shadow: inset 0 0 0 1px var(--nav-active-border);
}

/* The strip is built by showcase.js and starts closed, so a page without the
 * script has no empty shelf under the bar. */
.topbar-progress {
  height: 0;
  overflow: hidden;
  background: var(--strip-bg);
  border-top: 1px solid var(--border-subtle);
  transition: height var(--duration-slow) var(--ease-standard);
}

.topbar-progress.is-open {
  height: 36px;
}

.progress-inner {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  height: 36px;
}

.progress-label,
.progress-position {
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  white-space: nowrap;
  margin: 0;
}

.progress-label {
  color: var(--text-secondary);
}

.progress-position {
  color: var(--text-primary);
  margin-left: auto;
  font-variant-numeric: tabular-nums;
}

.progress-track {
  display: flex;
  gap: 6px;
  flex: 1;
  max-width: 320px;
}

.progress-seg {
  flex: 1;
  height: 3px;
  border-radius: var(--radius-full);
  background: var(--border-subtle);
  transition: background var(--duration-slow) var(--ease-standard);
}

.progress-seg.is-done {
  background: var(--brand);
}

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

.hero {
  position: relative;
  overflow: hidden;
  min-height: 76vh;
  display: flex;
  align-items: center;
}

.glow-hero {
  top: -240px;
  right: -160px;
  width: 720px;
  height: 720px;
  max-width: 140vw;
}

.hero-inner {
  position: relative;
  padding-top: 128px;
  padding-bottom: var(--space-16);
}

/* The company name is already inside the verb, so the heading marks those
 * letters rather than spelling HYPER C out a second time — and it marks them
 * the way a reader would, by dragging a cursor across them. Recolouring the
 * glyphs was tried and failed: at 84px a tinted word reads as a second colour
 * of headline. A highlight leaves the letters alone and puts the emphasis
 * behind them.
 *
 * Painted as a gradient rather than a background colour so the block can be
 * drawn part-way: background-size is what lets it be swept across the word.
 * The resting state is the finished selection, so nothing depends on the sweep
 * having run. `clone` so a wrap would highlight each line rather than stretch
 * one box across the gap, and `nowrap` so that wrap does not happen at the
 * hyphen and leave a lone highlighted c at the start of a line. */
.wordmark-echo {
  background-image: linear-gradient(var(--wordmark-mark), var(--wordmark-mark));
  background-repeat: no-repeat;
  background-size: 100% 100%;
  color: var(--text-on-brand);
  white-space: nowrap;
  padding: 0 0.04em;
  border-radius: var(--radius-sm);
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}

/* Left edge fixed, right edge travelling. The delay lets the headline land
 * first — a highlight already there when the page paints is a colour, not a
 * gesture — and `backwards` holds the unswept state through it rather than
 * flashing the finished block for a frame. Guarded on its own rather than left
 * to the global reduced-motion switch: that shortens durations, and a 0.01ms
 * sweep is a flicker where no sweep is simply the highlight. */
@media (prefers-reduced-motion: no-preference) {
  .wordmark-echo {
    animation: wordmark-select 820ms var(--ease-out) 560ms backwards;
  }
}

@keyframes wordmark-select {
  from {
    background-size: 0% 100%;
  }
}

.hero h1 {
  font-family: var(--font-display);
  font-weight: var(--weight-bold);
  font-size: clamp(44px, 6.5vw, 84px);
  line-height: 1.08;
  letter-spacing: var(--tracking-tight);
  margin: 28px 0 0;
  max-width: 840px;
  text-wrap: balance;
}

.hero-sub {
  font-size: 18px;
  color: var(--text-secondary);
  max-width: 560px;
  margin: var(--space-6) 0 0;
  text-wrap: pretty;
}

.hero-actions {
  display: flex;
  gap: var(--space-3);
  margin-top: 36px;
  flex-wrap: wrap;
}

/* --- what we do ---------------------------------------------------------- */

.principles {
  border-top: 1px solid var(--border-subtle);
  padding: var(--space-20) 0;
}

.principles-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--space-3);
  margin-top: 28px;
}

.principle {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.principle-index {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: var(--tracking-wide);
  color: var(--brand);
  margin: 0;
}

.principle h3 {
  font-family: var(--font-display);
  font-weight: var(--weight-semibold);
  font-size: 21px;
  letter-spacing: var(--tracking-tight);
  margin: 0;
}

.principle p {
  font-size: var(--text-base);
  color: var(--text-secondary);
  margin: 0;
}

/* The cards arrive as the reader reaches them, from a class the script adds.
 * `animation-timeline: view()` did this without any script and did nothing at
 * all on the desktop browsers that have not shipped it, which is not a trade
 * worth making for an entrance.
 *
 * Two details, both learned the hard way. The transition lives on the *arrived*
 * state, not on the hidden one — the script is deferred, so the card has
 * already been painted at full strength by the time it is hidden, and a
 * transition on the from-state animates that: the reader watches three cards
 * fade out before they ever fade in. Hidden instantly, the change happens in
 * the frame the script runs.
 *
 * And it is a per-card class rather than one on the grid, so the script can
 * leave a card alone if it is already properly on screen. Hiding something the
 * reader is looking at is never right, whatever the timing. */
@media (prefers-reduced-motion: no-preference) {
  .principle.is-pending {
    opacity: 0;
    transform: translateY(26px);
  }

  .principle.is-pending.is-in {
    opacity: 1;
    transform: none;
    transition:
      opacity 640ms var(--ease-out),
      transform 640ms var(--ease-out);
  }

  /* Staggered left to right, so the row resolves in the order it is read. */
  .principle.is-pending:nth-child(2).is-in {
    transition-delay: 90ms;
  }

  .principle.is-pending:nth-child(3).is-in {
    transition-delay: 180ms;
  }
}

/* --- product ------------------------------------------------------------- */

.product {
  border-top: 1px solid var(--border-subtle);
  /* The showcase inside runs its own sticky column; clipping it would break the
   * pinning, so only the horizontal overflow is contained. */
  overflow-x: clip;
}

.product-intro {
  padding-top: var(--space-20);
}

.product-head {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  margin-top: 28px;
  flex-wrap: wrap;
}

.product-icon {
  width: 64px;
  height: 64px;
  flex: none;
  /* 22% of the box — the proportion iOS uses, which is what makes a square
   * image read as an app icon rather than as a picture. */
  border-radius: var(--radius-lg);
  border: 1px solid var(--border-subtle);
}

.product-id {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.product-eyebrow {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-secondary);
  margin: 0;
}

.product-name-row {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}

.product-name {
  font-family: var(--font-display);
  font-weight: var(--weight-bold);
  font-size: var(--text-3xl);
  letter-spacing: var(--tracking-tight);
  margin: 0;
}

/* The claim is set as display type rather than body: it is the sentence the
 * whole product reduces to, and at body size it read as a caption. */
.product-claim {
  font-family: var(--font-display);
  font-weight: var(--weight-semibold);
  font-size: clamp(22px, 3vw, 30px);
  letter-spacing: var(--tracking-tight);
  line-height: 1.25;
  max-width: 680px;
  margin: var(--space-8) 0 0;
  text-wrap: balance;
}

.product-blurb {
  font-size: 16px;
  color: var(--text-secondary);
  line-height: var(--leading-relaxed);
  max-width: 640px;
  margin: 18px 0 0;
  text-wrap: pretty;
}

.product-facts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-6) var(--space-10);
  margin: var(--space-8) 0 0;
}

.fact {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.fact dt {
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-secondary);
}

.fact dd {
  font-size: var(--text-base);
  font-weight: var(--weight-semibold);
  margin: 0;
}

/* --- store badges ---------------------------------------------------------
 * The shape of a store badge without the promise of one: nothing here is a
 * link, nothing hovers, nothing lifts on press. Muted type and a desaturated
 * mark do the greying, rather than an opacity on the whole badge — that dims
 * the label past reading as well as past living.
 * ---------------------------------------------------------------------- */

.stores {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-6);
}

.store-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-4);
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  cursor: default;
  user-select: none;
}

/* The marks are the app's own sign-in assets: one full colour, one already
 * grey. Desaturating both is what makes the pair read as one dead control
 * instead of a live badge sitting next to a spent one. */
.store-badge-mark {
  width: 24px;
  height: 24px;
  flex: none;
  filter: grayscale(1);
  opacity: 0.8;
}

.store-badge-lines {
  display: flex;
  flex-direction: column;
  line-height: var(--leading-tight);
}

/* Secondary rather than tertiary: "coming soon" is the whole point of the
 * badge, not a caption on it, so it has to clear contrast on its own. */
.store-badge-lead {
  font-size: var(--text-xs);
  color: var(--text-secondary);
}

.store-badge-name {
  font-family: var(--font-display);
  font-size: var(--text-md);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-tight);
  color: var(--text-secondary);
}

/* --- showcase -------------------------------------------------------------
 * Eight steps, each with the screen it describes beside it. The phone used to be
 * a single sticky frame that the copy scrolled past; it scrolls with its own
 * step now, so what is being read and what is being looked at travel together.
 * That costs the crossfade between screens and buys back the thing a pinned
 * layer always costs: a reader can look away from the phone and it stays where
 * they left it.
 * ---------------------------------------------------------------------- */

/* The bottom matches the gap between steps: the last phone is followed by the
 * Next up rule, and at 64px the two sat closer together than any two steps did,
 * which read as the section ending before the reader had left it. */
.showcase {
  padding: var(--space-12) 0 152px;
}

.showcase-step {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-16);
}

/* Past the end of the spacing scale on purpose. The step is a 700px phone
 * beside four lines of copy, and at 96px two of them read as one block with a
 * seam in it rather than as two things — the gap has to grow with what it is
 * separating. */
.showcase-step + .showcase-step {
  margin-top: 152px;
}

.step-copy {
  display: flex;
  flex-direction: column;
  gap: 14px;
  max-width: 440px;
}

.step-meta {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: 0;
}

.step-count {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: var(--tracking-wide);
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}

.showcase-step h3 {
  font-family: var(--font-display);
  font-weight: var(--weight-semibold);
  font-size: 30px;
  letter-spacing: var(--tracking-tight);
  line-height: 1.15;
  margin: 0;
  text-wrap: balance;
}

.step-copy > p:last-child {
  font-size: 16px;
  color: var(--text-secondary);
  margin: 0;
  text-wrap: pretty;
}

.step-media {
  position: relative;
  display: flex;
  justify-content: center;
}

.glow-phone {
  inset: -80px;
}

/* Sized from the width now that the frame is not competing with a viewport-tall
 * sticky column — larger than the pinned phone was, which is the point of
 * taking it off the layer. The vh term is a floor for genuinely short
 * windows, not a general cap. At 43vh it bound on ordinary laptops and quietly
 * handed back the size it had just been given, which reads as the setting not
 * having taken; at 60vh the phone is the same width on every normal desktop and
 * only gives ground on a window short enough that a full-height phone would be
 * absurd. */
.showcase-frame {
  position: relative;
  width: clamp(250px, min(26vw, 60vh), 329px);
  aspect-ratio: 390 / 844;
  border-radius: 28px;
  border: 1px solid var(--border-default);
  background: var(--bg-surface);
  overflow: hidden;
}

/* A step that owns two screens stacks them in one frame and swaps on a timer;
 * every other frame holds a single screen that is simply shown. `cover` from
 * the top because the captures are a hair taller than the frame's ratio — the
 * crop lands on the home indicator rather than the status bar.
 *
 * The incoming screen rises and settles rather than just fading: a crossfade
 * alone between two dark screenshots reads as a flicker, and the small travel
 * is what makes it read as one replacing the other. */
.showcase-media {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top;
  opacity: 0;
  transform: translateY(20px) scale(0.985);
  transition:
    opacity 0.48s var(--ease-standard),
    transform 0.48s var(--ease-standard);
}

.showcase-media.is-shown {
  opacity: 1;
  transform: none;
}

/* Each step arrives as the reader reaches it, the copy a beat ahead of the
 * phone. The pin used to be the section's motion; with the phone scrolling
 * normally the steps would otherwise sit dead still while the cards above them
 * animate. Same two rules as the cards above: the transition is on the arrived
 * state so hiding is instant, and the pending class is per step so one already
 * on screen is left alone. */
@media (prefers-reduced-motion: no-preference) {
  .showcase-step.is-pending .step-copy,
  .showcase-step.is-pending .step-media {
    opacity: 0;
    transform: translateY(28px);
  }

  .showcase-step.is-pending.is-in .step-copy,
  .showcase-step.is-pending.is-in .step-media {
    opacity: 1;
    transform: none;
    transition:
      opacity 640ms var(--ease-out),
      transform 640ms var(--ease-out);
  }

  .showcase-step.is-pending.is-in .step-media {
    transition-delay: 110ms;
  }
}

/* --- next up ------------------------------------------------------------- */

.next {
  border-top: 1px solid var(--border-subtle);
  padding: var(--space-20) 0;
}

.next-title {
  font-family: var(--font-display);
  font-weight: var(--weight-bold);
  font-size: var(--text-2xl);
  letter-spacing: var(--tracking-tight);
  margin: var(--space-5) 0 0;
}

.next-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: 28px;
  max-width: 640px;
}

.next-row {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-5) var(--space-6);
}

/* A named row has more to say than a slug, so it stops being a single line and
 * the pill moves to the top of the block rather than floating mid-paragraph. */
.next-row:not(.next-row-quiet) {
  align-items: flex-start;
}

.next-id {
  flex: 1;
}

.next-slug {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  letter-spacing: var(--tracking-mono);
  color: var(--text-primary);
  margin: 0;
}

.next-name {
  font-family: var(--font-display);
  font-weight: var(--weight-semibold);
  font-size: var(--text-lg);
  letter-spacing: var(--tracking-tight);
  margin: var(--space-2) 0 var(--space-1);
}

.next-desc {
  font-size: var(--text-base);
  color: var(--text-secondary);
  line-height: var(--leading-relaxed);
  max-width: 52ch;
  margin: 0;
  text-wrap: pretty;
}

.next-row-quiet .pill {
  margin-left: auto;
}

/* --- contact ------------------------------------------------------------- */

.contact {
  position: relative;
  overflow: hidden;
  border-top: 1px solid var(--border-subtle);
  /* A closing panel rather than a footer strip: after six screens of product it
   * wants air around it, and the ask is the last thing on the page. */
  min-height: 48vh;
  display: flex;
  align-items: center;
}

.glow-contact {
  bottom: -320px;
  left: 50%;
  margin-left: -360px;
  width: 720px;
  height: 720px;
  max-width: 140vw;
}

.contact-inner {
  position: relative;
  padding-top: 96px;
  padding-bottom: 96px;
  text-align: center;
}

.contact h2 {
  font-family: var(--font-display);
  font-weight: var(--weight-bold);
  font-size: clamp(32px, 4vw, 48px);
  letter-spacing: var(--tracking-tight);
  margin: 0;
}

.contact p {
  font-size: 16px;
  color: var(--text-secondary);
  max-width: 440px;
  margin: 18px auto 0;
  text-wrap: pretty;
}

.contact .btn {
  margin-top: var(--space-8);
}

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

.footer {
  border-top: 1px solid var(--border-subtle);
}

.footer-inner {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding-top: 28px;
  padding-bottom: 28px;
  flex-wrap: wrap;
}

.footer-meta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-secondary);
  margin: 0;
}

/* Desaturated rather than redrawn — the same treatment the store marks get, and
 * for the same reason: the footer is the quietest line on the page and a full
 * brand-blue tile down there pulls harder than the text it belongs to. The file
 * stays the one the tab and the home screen use, so it cannot drift. */
.footer-mark {
  width: 16px;
  height: 16px;
  flex: none;
  filter: grayscale(1);
  opacity: 0.7;
}


/* --- 404 ----------------------------------------------------------------- */

.notfound {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  min-height: 70vh;
  padding: var(--space-16) 0;
}

.notfound h1 {
  font-family: var(--font-display);
  font-size: var(--text-3xl);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-tight);
  margin: var(--space-5) 0 var(--space-3);
}

.notfound p {
  font-size: 16px;
  color: var(--text-secondary);
  margin: 0 0 var(--space-8);
}

/* --- responsive ----------------------------------------------------------- */

@media (max-width: 860px) {
  .container {
    padding-inline: var(--space-5);
  }

  .topbar-nav {
    display: none;
  }

  /* With the links gone the button is the only thing left to sit opposite the
   * lockup, so it takes over the shove the nav was doing. Without this it
   * clings to the wordmark and leaves the whole right half of the bar empty. */
  .topbar .btn-primary {
    margin-left: auto;
  }

  /* The strip goes: its track had 34px to draw six segments in at this width,
   * which is a sliver rather than a progress bar. Each step's own meta row says
   * the same thing in the copy, where there is room. */
  .topbar-progress {
    display: none;
  }

  .hero {
    min-height: 0;
  }

  .hero-inner {
    padding-top: 120px;
    padding-bottom: var(--space-16);
  }

  .principles,
  .next {
    padding: var(--space-16) 0;
  }

  .product-intro {
    padding-top: var(--space-16);
  }

  .product-name {
    font-size: var(--text-2xl);
  }

  .showcase {
    padding: var(--space-8) 0 var(--space-24);
  }

  /* One column: the copy, then the screen it is describing. Nothing is pinned
   * here either, so the pair simply reads top to bottom. */
  .showcase-step {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-6);
  }

  .showcase-step + .showcase-step {
    margin-top: var(--space-24);
  }

  .step-copy {
    max-width: none;
  }

  .showcase-step h3 {
    font-size: var(--text-xl);
  }

  .showcase-frame {
    width: min(64vw, 300px);
  }

  .glow-phone {
    inset: -40px;
  }

  .contact {
    min-height: 0;
  }

  .contact-inner {
    padding-top: var(--space-20);
    padding-bottom: var(--space-20);
  }

  /* stretch, not flex-start. Turned upright, flex-start sizes each line to its
   * max-content width — the text becomes one unbreakable run and the
   * overflow-wrap on it is dead letter, which is how a line a word longer than
   * the gutter ends up hanging off the edge. Stretched, the lines are the
   * column's width and wrap like text. */
}

@media (max-width: 520px) {
  .hero-actions .btn {
    width: 100%;
  }

  .next-row:not(.next-row-quiet) {
    flex-direction: column;
    align-items: flex-start;
  }
}

/* Motion in this system is functional, so there is little to disable — but
 * honor the preference completely where it exists. The showcase's clips are
 * held still by showcase.js, which reads the same preference. */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }

  .btn:active {
    transform: none;
  }

  /* A screen that slid and scaled into place is decoration; a screen that
   * simply is the one being described is the information. */
  .showcase-media {
    transform: none;
  }
}
