/* Relay — relaythis.com
 *
 * One column, left-aligned, centred on the page. No framework, no build step: the whole
 * site is three static files, which is also why it costs nothing to host.
 */

@font-face {
  font-family: "Relay Fustat";
  src: url("/fonts/fustat-400.woff2?v=3") format("woff2");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "Relay Fustat";
  src: url("/fonts/fustat-600.woff2?v=3") format("woff2");
  font-weight: 600;
  font-display: swap;
}
@font-face {
  font-family: "Relay Fustat";
  src: url("/fonts/fustat-700.woff2?v=3") format("woff2");
  font-weight: 700;
  font-display: swap;
}
@font-face {
  font-family: "Relay Sans Code";
  src: url("/fonts/googlesanscode-400.woff2?v=3") format("woff2");
  font-weight: 400;
  font-display: swap;
}
/* The wordmark's face, the same file the extension ships. */
@font-face {
  font-family: "Relay Stack";
  src: url("/fonts/stacksans-headline.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}
/* Goudy Bookletter 1911 (OFL) — the display face in the new landing-page design. */
@font-face {
  font-family: "Relay Goudy";
  src: url("/fonts/goudybookletter1911-400.ttf?v=3") format("truetype");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
/* Crimson Pro (OFL) — the headline. Both weights are here so the headline can be switched
   between them with a single number, without another font to fetch. */
@font-face {
  font-family: "Relay Crimson";
  src: url("/fonts/crimsonpro-400.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "Relay Crimson";
  src: url("/fonts/crimsonpro-700.woff2") format("woff2");
  font-weight: 700;
  font-display: swap;
}
/* Patrick Hand / Patrick Hand SC (OFL). The handwriting is what makes a story read as a
   person rather than a feature list. Latin subset only — that is all the page needs. */
@font-face {
  font-family: "Relay Hand";
  src: url("/fonts/patrick-hand-400.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "Relay Hand SC";
  src: url("/fonts/patrick-hand-sc-400.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}

:root {
  --ink: #0b0b0d;
  --body: #16161a;
  --muted: #6f6f79;
  --line: #f1f1f1;
  --link: #1a6dff;
  --col: 560px;
}

* {
  box-sizing: border-box;
}

/* `hidden` is an attribute, but the UA gives it only `display: none`, which any later
   `display` rule quietly outranks. The waitlist form (display: flex) went on standing there
   after a successful signup because of exactly this. Hidden means hidden. */
[hidden] {
  display: none !important;
}

html,
body {
  margin: 0;
  padding: 0;
}

html {
  scroll-behavior: smooth;
}

body {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 64px 24px;
  background: #fff;
  color: var(--body);
  font-family: "Relay Fustat", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-size: 18px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

/* Long-form pages (privacy, terms) read from the top, not the middle. */
body.doc {
  justify-content: flex-start;
  padding-top: 72px;
}

/* The home page runs well past the viewport now that the stories are on it. Centring it
   vertically would push the wordmark off the top on a short screen, where there is no way to
   scroll back up to it. */
body.home {
  justify-content: flex-start;
  align-items: flex-start;
  /* Named because one thing other than the padding needs it: the harness reel
     escapes the column and has to know exactly how far left the viewport edge
     is. This page is left-aligned, not centred, so the usual
     `margin-left: calc(50% - 50vw)` full-bleed trick lands in the wrong place. */
  --page-gutter: 88px;
  padding: 88px var(--page-gutter) 90px;
}

/* ── homepage entrance ───────────────────────────────────────────────── */

.relay-intro {
  display: none;
}

.relay-intro-pending body.home {
  overflow-x: hidden;
}

.relay-intro-pending .relay-intro {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: block;
  background: #fff;
  pointer-events: none;
}

.relay-intro-wordmark {
  position: fixed;
  display: inline-block;
  opacity: 0;
  color: var(--ink);
  font-family: "Relay Stack", "Relay Fustat", sans-serif;
  font-size: 50.4px;
  font-weight: 400;
  line-height: 1;
  letter-spacing: -0.02em;
  white-space: nowrap;
  will-change: left, top, font-size;
}

.relay-intro-pending .reference-home .wordmark {
  visibility: hidden;
}

.relay-intro-pending .hero > *,
.relay-intro-pending .problem-section > *,
.relay-intro-pending .solution-section > *,
.relay-intro-pending .why-use > h2,
.relay-intro-pending .why-use > .why-use-intro-heading,
.relay-intro-pending .why-use > .why-use-intro,
.relay-intro-pending .why-use > .reason-image--prompt,
.relay-intro-pending .reason,
.relay-intro-pending .harness-section > *,
.relay-intro-pending .waitlist-panel > *,
.relay-intro-pending body.home > footer {
  opacity: 0;
  filter: blur(10px);
  transform: translateY(20px);
}

/* ── app activation ──────────────────────────────────────────────────── */

body.setup-page {
  min-height: 100vh;
  justify-content: flex-start;
  padding: 40px 32px 28px;
  background: #fff;
}

.setup-shell {
  width: min(100%, 640px);
  max-width: none;
  flex: 1;
  display: flex;
  align-items: center;
  padding: 72px 0 40px;
}

.setup-card {
  width: 100%;
}

.setup-card > div {
  width: 100%;
}

.setup-card h1 {
  margin: 0 0 6px;
  color: #050505;
  font-size: 36px;
  font-weight: 600;
  line-height: 1.16;
  letter-spacing: -0.025em;
}

.setup-copy {
  max-width: 620px;
  margin: 0;
  color: #111;
  font-size: 20px;
  line-height: 1.45;
}

.setup-form {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 10px;
  max-width: 560px;
  margin-top: 30px;
}

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

.setup-form input,
.setup-form button {
  height: 50px;
  border-radius: 9px;
  font: inherit;
}

.setup-form input {
  min-width: 0;
  padding: 0 15px;
  border: 1px solid #bfc0c2;
  background: #fff;
  color: var(--ink);
  font-family: "Relay Sans Code", ui-monospace, monospace;
  font-size: 13px;
  letter-spacing: 0.055em;
  text-transform: uppercase;
  outline: none;
}

.setup-form input:focus {
  border-color: #8f8f99;
  box-shadow: 0 0 0 3px rgba(25, 25, 32, 0.07);
}

.setup-form input[aria-invalid="true"] {
  border-color: #c94940;
}

.setup-form button {
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  min-width: 192px;
  padding: 0 16px;
  border: 1px solid #bfc0c2;
  background: #fff;
  color: #111;
  font-family: "Relay Sans Code", ui-monospace, monospace;
  font-size: 13px;
  text-transform: uppercase;
  cursor: pointer;
  white-space: nowrap;
}

.setup-form button:hover:not(:disabled) {
  border-color: #111;
  background: #f7f7f7;
}

.setup-arrow {
  font-family: "Relay Fustat", sans-serif;
  font-size: 24px;
  line-height: 1;
}

.setup-form button:focus-visible,
.setup-form input:focus-visible {
  outline: 2px solid #155ed1;
  outline-offset: 3px;
}

.setup-form button:focus,
.setup-form input:focus {
  outline: 2px solid #155ed1;
  outline-offset: 3px;
}

.setup-form button:disabled,
.setup-form input:disabled {
  cursor: default;
  opacity: 0.58;
}

.setup-message {
  min-height: 0;
  max-width: 560px;
  margin: 10px 0 0;
  color: #5c5c66;
  font-size: 13px;
}

.setup-message:empty {
  display: none;
}

.setup-message.is-error {
  color: #b73831;
}

.setup-success {
  max-width: 620px;
}

.setup-check {
  display: block;
  margin-bottom: 14px;
  color: #111;
  font-size: 30px;
}

.setup-help {
  margin: 10px 0 0;
  color: #5f5f68;
  font-size: 13px;
}

.setup-help a {
  color: inherit;
}

@media (max-width: 560px) {
  body.setup-page {
    justify-content: flex-start;
    padding: 24px 22px 22px;
  }

  .setup-shell {
    align-items: flex-start;
    padding: min(22vh, 150px) 0 64px;
  }

  .setup-card h1 {
    font-size: 32px;
  }

  .setup-copy {
    font-size: 18px;
  }

  .setup-form {
    grid-template-columns: 1fr;
    max-width: none;
    margin-top: 26px;
  }

  .setup-form button {
    width: 100%;
  }

  .setup-form input {
    font-size: 16px;
  }

}

main {
  width: 100%;
  max-width: var(--col);
}

.wordmark {
  font-family: "Relay Stack", "Relay Fustat", sans-serif;
  font-weight: 400;
  font-size: 30px;
  letter-spacing: -0.02em;
  color: var(--ink);
  margin: 0 0 30px;
  text-decoration: none;
  display: inline-block;
}

h1 {
  font-weight: 700;
  font-size: 38px;
  line-height: 1.18;
  letter-spacing: -0.025em;
  color: var(--ink);
  margin: 0 0 14px;
}

/* The home headline only: set in a serif and ruled in blue, like a sentence someone underlined
   by hand. The rule rides under each line of type, so it breaks where the sentence does. The
   404 and the policy pages keep the plain sans heading. */
body.home h1 {
  font-family: "Relay Goudy", Georgia, serif;
  font-weight: 400;
  font-size: 33px;
  line-height: 1.2;
  text-decoration: none;
}

/* ── reference landing page ───────────────────────────────────────────── */

.reference-home {
  width: 100%;
}

.reference-home main {
  width: 100%;
  max-width: 414px;
}

.reference-home .wordmark {
  font-size: 18px;
  line-height: 1;
  margin: 0 0 24px;
}

.reference-home h1 {
  font-size: 31px;
  line-height: 1.08;
  letter-spacing: -0.018em;
  margin: 0 0 13px;
}

.reference-home p {
  font-size: 15.5px;
  line-height: 1.42;
  margin-bottom: 20px;
}

.reference-home .lede {
  font-size: 15.5px;
  line-height: 1.42;
  margin-bottom: 15px;
}

.reference-home .waitlist-copy {
  margin-bottom: 0;
}

.hero-cta {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 25px;
  padding: 5px 10px;
  border-radius: 5px;
  background: #006bd6;
  color: #f3f9ff;
  box-shadow: inset 0 2px 2px rgba(255, 255, 255, 0.25);
  font-family: "Relay Sans Code", ui-monospace, SFMono-Regular, monospace;
  font-size: 10px;
  line-height: 1;
  letter-spacing: 0.045em;
  text-decoration: none;
  text-transform: uppercase;
  transition: background 140ms ease, transform 140ms ease;
}

.hero-cta:hover {
  opacity: 1;
  background: #006bd6;
  filter: brightness(0.96);
}

.hero-cta:active {
  transform: translateY(1px);
}

.hero-cta:focus-visible {
  outline: 2px solid #155ed1;
  outline-offset: 3px;
}

.hero-cta svg {
  width: 13px;
  height: 13px;
  display: block;
}

/* The hero carries the same signup form as the panel at the foot of the page, so a
   visitor who is already convinced never has to scroll to act. */
.reference-home .signup--hero {
  margin-top: 16px;
}

.landing-story {
  margin-top: 123px;
}

.story-section .section-label {
  margin: 0 0 14px;
  color: #6f6f79;
  font-family: "Relay Sans Code", ui-monospace, SFMono-Regular, monospace;
  font-size: 10px;
  line-height: 1;
  letter-spacing: 0.055em;
  text-transform: uppercase;
}

.reference-home .story-section h2 {
  margin: 0 0 14px;
  font-size: 28px;
  line-height: 1.08;
  letter-spacing: -0.014em;
}

.reference-home .story-section p:not(.section-label) {
  margin-bottom: 20px;
}

.office-image {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 2.38 / 1;
  margin-top: 75px;
  object-fit: cover;
  object-position: center;
}

/* Use cases. Sits between the solution story and the harness section, so it carries the same
   152px separation the other mid-story sections use. .problem-section is the first child of
   .landing-story again and needs no top margin of its own. */
.usecases-section {
  margin-top: 152px;
}

/* ── the mock demo panel: kept only for the translated pages ─────────────
   One panel, moved by script into whichever card is active and anchored to that card's
   right — an annotation on the sentence you are pointing at, not a separate exhibit.

   Gone from the English page. It drew invented content — made-up company names, a
   fabricated "20 applications sent" — and it sat directly above real recordings of the
   same jobs, which is a bad trade: a mockup next to footage makes the footage look
   staged too. The use-case section now carries filmed clips instead (.reel).

   This block, and the .usecase-play / .demo-ready rules further down, stay only because
   website/<lang>/index.html is spliced from the English file by scripts/i18n_extract.py
   and the seventeen locale copies on disk still predate the change — they still render
   this panel, and style.css is shared by all of them. Delete it in the same commit that
   rebuilds the locales (see README.md "Translations"), not before. */
.demo {
  position: absolute;
  top: -10px;
  left: calc(100% + 44px);
  width: 420px;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 260ms ease, transform 260ms ease;
  pointer-events: none;
}

.usecase.is-playing .demo {
  opacity: 1;
  transform: none;
}

.demo-stage {
  position: relative;
  overflow: hidden;
  border: 1px solid #e3e3e7;
  border-radius: 10px;
  background: #fff;
  box-shadow: 0 12px 28px -20px rgba(0, 0, 0, 0.25);
}

.demo-chrome {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 9px 12px;
  border-bottom: 1px solid #ededf1;
  background: #fafafb;
}

.demo-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #dededf;
}

.reference-home .demo-url {
  margin-left: 8px;
  color: #9c9c9c;
  font-family: "Relay Sans Code", ui-monospace, SFMono-Regular, monospace;
  font-size: 11px;
}

.demo-page {
  display: grid;
  gap: 11px;
  padding: 18px 20px 22px;
  min-height: 232px;
  align-content: start;
}

.reference-home .demo-h {
  margin: 0 0 3px;
  color: #111;
  font-size: 13.5px;
  font-weight: 500;
}

.demo-field-label {
  display: block;
  margin-bottom: 4px;
  color: #9c9c9c;
  font-family: "Relay Sans Code", ui-monospace, SFMono-Regular, monospace;
  font-size: 9.5px;
  letter-spacing: 0.055em;
  text-transform: uppercase;
}

/* Not a real <input>: it must never take focus or open a keyboard. The caret is a
   pseudo-element so the typed text stays plain text a screen reader can read. */
.demo-input {
  min-height: 30px;
  padding: 7px 9px;
  border: 1px solid #e3e3e7;
  border-radius: 6px;
  background: #fff;
  color: #1c1c1f;
  font-size: 12.5px;
  line-height: 1.2;
  transition: border-color 180ms ease, background-color 180ms ease;
}

.demo-input.is-active {
  border-color: #bfc0c2;
  background: #fcfcfd;
}

.demo-input.is-typing::after {
  content: "";
  display: inline-block;
  width: 1px;
  height: 12px;
  margin-left: 1px;
  background: #1c1c1f;
  vertical-align: -2px;
  animation: demo-caret 1s steps(1) infinite;
}

@keyframes demo-caret {
  0%, 50% { opacity: 1; }
  50.01%, 100% { opacity: 0; }
}

.demo-submit {
  justify-self: start;
  margin-top: 5px;
  padding: 8px 14px;
  border: 1px solid #16161a;
  border-radius: 6px;
  background: #16161a;
  color: #fff;
  font-family: inherit;
  font-size: 12px;
  transition: transform 140ms ease, opacity 140ms ease;
}

.demo-submit.is-pressed {
  transform: scale(0.97);
  opacity: 0.85;
}

/* ── per-scenario pieces ─────────────────────────────────────────────────
   The scenarios share these rows rather than each inventing its own, so six different mocks
   still read as one product. */
.demo-row {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 9px 10px;
  border: 1px solid #ededf1;
  border-radius: 7px;
  font-size: 12.5px;
  color: #1c1c1f;
  transition: border-color 200ms ease, background-color 200ms ease;
}

.demo-row--status,
.demo-row--price {
  justify-content: space-between;
}

.demo-row--status .demo-field-label,
.demo-row--price .demo-field-label {
  margin-bottom: 0;
}

.demo-value {
  color: #1c1c1f;
  font-size: 12.5px;
  transition: color 220ms ease;
}

.demo-value.is-changed {
  color: #1c7a4a;
  font-weight: 500;
}

.demo-value.is-bad {
  color: #c0392f;
  font-weight: 500;
}

/* The tick is drawn before the row is done and revealed on completion, so the row never
   changes height and nothing below it jumps. */
.demo-tick {
  flex: 0 0 auto;
  width: 13px;
  color: #1c7a4a;
  font-size: 11px;
  opacity: 0;
  transition: opacity 220ms ease;
}

.demo-row.is-done { border-color: #dfe9e3; }
.demo-row.is-done .demo-tick { opacity: 1; }

.demo-file {
  flex: 1;
  color: #6f6f79;
  font-family: "Relay Sans Code", ui-monospace, SFMono-Regular, monospace;
  font-size: 11px;
}

.demo-row.is-done .demo-file { color: #1c1c1f; }

.reference-home .demo-foot,
.reference-home .demo-delta {
  margin: 2px 0 0;
  color: #9c9c9c;
  font-family: "Relay Sans Code", ui-monospace, SFMono-Regular, monospace;
  font-size: 11px;
  opacity: 0;
  transition: opacity 260ms ease, color 260ms ease;
}

.reference-home .demo-foot.is-on { opacity: 1; }
.reference-home .demo-delta.is-on { opacity: 1; color: #c0392f; }

.demo-table {
  display: grid;
  gap: 5px;
}

.demo-row--head,
.demo-row--data {
  display: grid;
  grid-template-columns: 1fr 1fr 42px;
  gap: 8px;
}

.demo-row--head {
  border-color: transparent;
  padding-bottom: 2px;
}

.demo-cell {
  overflow: hidden;
  font-size: 11.5px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.demo-row--head .demo-cell { margin-bottom: 0; }

/* The notification: what the visitor actually receives. It arrives rather than being there
   all along, because the arrival IS the product. */
.demo-note {
  padding: 11px 12px;
  border: 1px solid #e3e3e7;
  border-radius: 8px;
  background: #fafafb;
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 300ms ease, transform 300ms ease;
}

.demo-note.is-on {
  opacity: 1;
  transform: none;
}

.demo-note-from {
  display: block;
  margin-bottom: 4px;
  color: #9c9c9c;
  font-family: "Relay Sans Code", ui-monospace, SFMono-Regular, monospace;
  font-size: 9.5px;
  letter-spacing: 0.055em;
  text-transform: uppercase;
}

.reference-home .demo-note-body {
  margin: 0;
  color: #1c1c1f;
  font-size: 12.5px;
  line-height: 1.35;
}

/* Cursor — ported from primitives-playground: same pointer, same idle float, same status pill
   hanging off the tip side. */
.demo-cursor {
  position: absolute;
  top: 0;
  left: 0;
  pointer-events: none;
  opacity: 0;
  transition: transform 550ms cubic-bezier(0.2, 0.85, 0.24, 1), opacity 260ms ease;
  will-change: transform;
}

.demo-cursor.is-on { opacity: 1; }

.demo-shell {
  position: relative;
  display: block;
  animation: demo-cursor-idle 3.5s ease-in-out infinite;
}

/* Suppressed while travelling — a bobbing cursor mid-flight reads as drift, not intent. */
.demo-cursor.is-moving .demo-shell { animation: none; }

@keyframes demo-cursor-idle {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-5px); }
}

.demo-pointer {
  display: block;
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.32));
}

.demo-label {
  position: absolute;
  top: 29px;
  left: 31px;
  white-space: nowrap;
  border: 2px solid #9c9c9c;
  border-radius: 7px;
  padding: 4px 8px;
  background: #fff;
  color: #000;
  font-family: "Relay Sans Code", ui-monospace, SFMono-Regular, monospace;
  font-size: 10px;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}

.demo-label:empty { display: none; }

.reference-home .demo-status {
  margin: 10px 0 0;
  color: #6f6f79;
  font-family: "Relay Sans Code", ui-monospace, SFMono-Regular, monospace;
  font-size: 11px;
  letter-spacing: 0.02em;
  min-height: 15px;
}

.usecase-list {
  display: grid;
  gap: 46px;
  margin: 52px 0 0;
  padding: 0;
  list-style: none;
}

/* Cards become triggers only once the demo is running (the class is set by script), so they
   never advertise interactivity that isn't there. */
.demo-ready .usecase {
  cursor: pointer;
  transition: border-color 200ms ease;
}

.demo-ready .usecase:hover,
.demo-ready .usecase:focus-within {
  border-left-color: #9c9c9c;
}

.demo-ready .usecase.is-playing {
  border-left-color: #16161a;
}

.usecase-play {
  display: none;
  margin-top: 10px;
  padding: 0;
  border: 0;
  background: none;
  color: #6f6f79;
  font-family: "Relay Sans Code", ui-monospace, SFMono-Regular, monospace;
  font-size: 10px;
  letter-spacing: 0.055em;
  text-transform: uppercase;
  cursor: pointer;
}

.demo-ready .usecase-play { display: inline-block; }
.usecase-play:hover { color: #16161a; }

/* Under ~1000px there is no room beside the column, and an inline panel pushed the card you
   tapped off the screen. A tap presents the demo OVER the page instead: the copy blurs behind
   it and an × closes it. Hover is not offered here — on touch a hover that also fires a click
   would open a demo nobody asked for. */
@media (max-width: 1000px) {
  .demo {
    position: static;
    display: none;
    width: 100%;
    margin-top: 0;
  }

  .demo.is-overlay {
    display: block;
    position: fixed;
    z-index: 1200;
    /* The desktop panel is pointer-events: none — it is an annotation you look at, never touch.
       As an overlay it has a close button, so it has to accept taps again; without this the ×
       is visible, enabled and completely dead, with every tap landing on the backdrop. */
    pointer-events: auto;
    top: 50%;
    left: 16px;
    right: 16px;
    width: auto;
    transform: translateY(-50%);
    opacity: 1;
  }

  .demo.is-overlay .demo-stage {
    display: block;
    box-shadow: 0 24px 50px -28px rgba(0, 0, 0, 0.45);
  }

  .demo.is-overlay .demo-page {
    min-height: 216px;
  }

  .demo-backdrop {
    position: fixed;
    inset: 0;
    z-index: 1199;
    background: rgba(255, 255, 255, 0.72);
    /* The blur is the point: the copy stays visible enough to keep your place, but stops
       competing with the animation for attention. */
    backdrop-filter: blur(7px);
    -webkit-backdrop-filter: blur(7px);
    animation: demo-fade 220ms ease;
  }

  /* No backdrop-filter (older Android WebView, Firefox without the flag) — fall back to a
     near-opaque sheet rather than leaving the text legible and the overlay looking broken. */
  @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .demo-backdrop { background: rgba(255, 255, 255, 0.95); }
  }

  @keyframes demo-fade {
    from { opacity: 0; }
    to { opacity: 1; }
  }

  .demo-close {
    display: flex;
    align-items: center;
    justify-content: center;
    /* Above the panel, right-aligned, and a 40px target — a 24px × is a miss waiting to
       happen on a phone. */
    margin: 0 0 10px auto;
    width: 40px;
    height: 40px;
    padding: 0;
    border: 1px solid #e3e3e7;
    border-radius: 50%;
    background: #fff;
    color: #16161a;
    font-family: inherit;
    font-size: 22px;
    line-height: 1;
  }

  .demo-close:focus-visible {
    outline: 2px solid #155ed1;
    outline-offset: 2px;
  }

  /* Stop the page scrolling underneath the overlay. */
  html.demo-locked,
  html.demo-locked body {
    overflow: hidden;
  }

  .demo-ready .usecase { cursor: pointer; }
}

/* Reduced motion: the script renders the finished state instead of animating, so all that is
   left to do is stop the idle float and the caret blink. */
@media (prefers-reduced-motion: reduce) {
  .demo-shell { animation: none; }
  .demo-cursor { transition: none; }
  .demo-input.is-typing::after { animation: none; }
}

/* The written use-case cards.

   Gone from the English page — the six clips say the same thing and cannot be
   disbelieved, so the cards they illustrated came out.

   These rules, .usecase-list and .usecase-play, stay only because
   website/<lang>/index.html is spliced from the English file by scripts/i18n_extract.py
   and the seventeen locale copies on disk still predate the change — they still render
   the cards, and style.css is shared by all of them. Delete in the same commit that
   rebuilds the locales (see README.md "Translations"), not before.

   The rule marked the quoted instruction as something you TYPE rather than something we
   claim. It was the whole visual idea of the section, so it was deliberately the only
   ornament — a boxed card would have fought the editorial rhythm of the sections below. */
.usecase {
  position: relative;
  padding-left: 18px;
  border-left: 1px solid #e3e3e7;
}

.reference-home .usecase-ask {
  margin: 0 0 10px;
  color: #111;
  font-family: "Relay Sans Code", ui-monospace, SFMono-Regular, monospace;
  font-size: 14.5px;
  line-height: 1.5;
  letter-spacing: -0.008em;
  text-wrap: pretty;
}

.reference-home .usecase-does {
  margin: 0;
  color: #6f6f79;
  font-size: 15px;
  line-height: 1.45;
}

.solution-section {
  margin-top: 152px;
}

/* Browser support ------------------------------------------------------------
 *
 * Two panels rather than a list: everything Relay runs in today sits under one
 * "Supported" tag, and the one that doesn't is fenced off in its own panel, so
 * the eye never has to work out which tag covers which icon.
 *
 * The sizing is tight on purpose. The page's content column is a fixed 414px,
 * and six icons plus two panels only fit across it at 40px with small gaps —
 * below that an app icon stops being recognisable, which is the whole point of
 * using the real icons. Under 520px the panels stack instead of shrinking.
 */
.works-where {
  margin-top: 194px;
}

.reference-home .works-where > h2 {
  margin-bottom: 19px;
  font-size: 28px;
  line-height: 1.08;
  letter-spacing: -0.014em;
}

.works-where-intro-heading {
  margin: 0 0 2px;
  color: var(--muted);
  font-family: "Relay Fustat", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-size: 14px;
  font-weight: 400;
  line-height: 1.35;
}

/* Browser support: the marks, set out in a grid.

   It was two bordered panels with the icons in a row inside them, each icon on the
   white squircle macOS draws behind an app icon. Three borders deep, and the marks
   had to stay small to fit — so the boxes and the squircles are both gone (the
   stripped marks come from scripts/website_browser_marks.py) and the icons are half
   again as big. Nothing is lost by it: the tag still says which set is supported,
   and every browser now carries its own name instead of one label for four of them.

   Three columns rather than a single row of five, so the block stays inside the
   page's column instead of being the one thing that reaches past it. */
.browsers {
  display: flex;
  flex-wrap: wrap;
  gap: 34px 56px;
  /* Wide enough for the "coming soon" set to sit beside the supported one rather
     than under it, and no wider. */
  width: min(560px, calc(100vw - 56px));
  margin-top: 36px;
}

.browser-set {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 16px;
}

/* Three to a row, but sized by its items rather than by a fixed three-column track:
   a one-browser set on a rigid grid still measures three columns wide, which pushed
   "Coming soon" onto its own line under the supported set instead of beside it. */
.browser-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 22px 18px;
  max-width: 270px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.browser-grid li {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 9px;
  width: 78px;
  text-align: center;
}

.browser-grid img {
  display: block;
  width: 58px;
  height: 58px;
}

.browser-grid span {
  color: var(--muted);
  font-family: "Relay Fustat", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-size: 12px;
  line-height: 1.3;
}

/* The four Chromium marks stand for a family, not a whitelist — the old layout said
   so with a "Chromium browsers" label over the group, and naming each one instead
   would have quietly narrowed the claim. */
.browser-note {
  margin: 0;
  color: var(--muted);
  font-family: "Relay Fustat", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-size: 12px;
  line-height: 1.3;
}

/* The bordered panels and the icon row inside them.

   Gone from the English page — see the block above. These rules stay only because
   website/<lang>/index.html is spliced from the English file by scripts/i18n_extract.py
   and the seventeen locale copies on disk still predate the change, and style.css is
   shared by all of them. Delete in the same commit that rebuilds the locales. */
.browsers-supported,
.browsers-soon {
  display: flex;
  flex-direction: column;
  padding: 18px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: #fff;
}

.browsers-supported {
  flex: 0 1 auto;
}

.browsers-soon {
  flex: 0 0 auto;
}

.browser-groups {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
}

.browser-groups > .browser-group + .browser-group {
  padding-left: 18px;
  border-left: 1px solid var(--line);
}

.browser-group {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.browser-icons {
  display: flex;
  gap: 10px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.browser-icons img {
  display: block;
  width: 44px;
  height: 44px;
}

.works-where .browser-group-name {
  margin: 0;
  color: var(--muted);
  font-family: "Relay Fustat", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-size: 12px;
  line-height: 1.3;
  white-space: nowrap;
}

/* margin-top:auto rather than space-between, so the tags line up along the
   bottom of both panels however tall the taller one turns out to be. */
.works-where .browser-tag {
  align-self: flex-start;
  margin: 16px 0 0;
  padding: 4px 10px;
  border-radius: 99px;
  font-family: "Relay Fustat", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.01em;
  line-height: 1.4;
}

/* In the grid the tag heads its set rather than sitting at the foot of a panel.
   Scoped, so the locale pages — which still render the panels — keep their spacing. */
.browser-set .browser-tag {
  margin: 0;
}

.works-where .browser-tag--yes {
  background: #eaf5ee;
  color: #1f6b41;
}

.works-where .browser-tag--soon {
  background: #f3f3f5;
  color: var(--muted);
}

/* The divider is what tells you which label covers which icons, so it stays
   until the groups genuinely wrap — five icons still fit across a 390px phone. */
@media (max-width: 360px) {
  .browser-groups > .browser-group + .browser-group {
    padding-left: 0;
    border-left: 0;
  }
}

.why-use {
  margin-top: 194px;
}

.reference-home .why-use > h2 {
  margin-bottom: 19px;
  font-size: 28px;
  line-height: 1.08;
  letter-spacing: -0.014em;
}

.reference-home .why-use-intro {
  margin-bottom: 0;
}

.why-use-intro-heading,
.reason h3 {
  margin: 0 0 2px;
  color: #6f6f79;
  font-family: "Relay Fustat", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-size: 14px;
  font-weight: 400;
  line-height: 1.35;
}

.reason-image {
  display: block;
  width: 100%;
  max-width: 100%;
  height: auto;
  margin-top: 31px;
  object-fit: contain;
}

.reason-image--prompt {
  margin-top: 38px;
}

/* ── the real screenshots ───────────────────────────────────────────────────
   These are captures of the shipping UI (scripts/marketing/capture_settings.py),
   not drawings of it, and that has a layout consequence: the settings panel is
   ~720 CSS px wide in the product, so inside the 414px column its body text
   lands around 7px and the pane becomes an illegible grey smear — which defeats
   the entire point of using a real screenshot.

   So these step outside the column, the same way the browser row already does
   and for the same reason, anchored LEFT so the page's left edge is untouched
   and widening costs nothing on a wide screen.

   640px is a deliberate compromise. At 820px the panel's body text renders at
   its true 13.5px; at 640px it lands near 10.5px — small, but still read as
   text rather than texture, and the shot stops dominating the column it sits
   beside. Inside the 414px column it would be ~7px, which is the smear this
   breakout exists to avoid, so that is the floor. */
.reason-shot {
  width: min(640px, calc(100vw - 56px));
  max-width: none;
  border-radius: 10px;
}

@media (max-width: 1000px) {
  .reason-shot {
    width: calc(100vw - 56px);
  }
}

.reason-list {
  display: grid;
  gap: 84px;
  margin-top: 84px;
}

.reference-home .reason p {
  margin: 0;
  font-size: 15.5px;
  line-height: 1.42;
}

.reason--last {
  margin-top: 2px;
}

.reason:nth-child(3) {
  margin-top: 10px;
}

.reason:nth-child(4) {
  margin-top: 7px;
}

.reason:nth-child(5) {
  margin-top: 11px;
}

.reason:nth-child(6) {
  margin-top: 32px;
}

.reason:nth-child(7) {
  margin-top: 14px;
}

.harness-section {
  margin-top: 152px;
}

/* The demo images already carry their own hairline frame, so the figure only owns the
   spacing and the caption underneath it. */
.harness-demo {
  margin: 40px 0 0;
}

.harness-demo img {
  display: block;
  width: 100%;
  height: auto;
}

/* ── drawn harness stages: kept only for the translated pages ─────────────
   These five stages replaced screenshots, and have themselves now been replaced by real
   recordings — see .reel below. Nothing on the ENGLISH page uses anything from here down
   to .harness-demo figcaption any more.

   It stays because website/<lang>/index.html is spliced out of the English file by
   scripts/i18n_extract.py, and the seventeen locale copies on disk still predate the
   change: they render these stages, and style.css is shared by every one of them.

   Delete this whole run in the same commit that rebuilds the locales
   (extract -> translate -> build, see README.md "Translations"). Not before, or every
   non-English page loses its harness section's illustrations. */
.hstage {
  position: relative;
  width: 100%;
  overflow: hidden;
  border: 1px solid #e3e3e7;
  border-radius: 9px;
  background: #fff;
}

.hcursor {
  position: absolute;
  top: 0;
  left: 0;
  pointer-events: none;
  opacity: 0;
  transition: transform 420ms cubic-bezier(0.2, 0.85, 0.24, 1), opacity 220ms ease;
  will-change: transform;
}

.hcursor.is-on { opacity: 1; }
/* Tracking a stroke or a selection edge must be exact — a glide would trail its own tip. */
.hcursor.is-instant { transition: opacity 220ms ease; }
.hcursor-pointer {
  display: block;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3));
  transition: transform 150ms ease;
}
.hcursor.is-pressed .hcursor-pointer { transform: scale(0.88); }

/* highlight */
.hlines {
  display: flex;
  align-items: center;
  height: 100%;
  padding: 16px 18px;
  color: #1c1c1f;
  font-size: 13px;
  line-height: 1.75;
}

.hline { width: 100%; }

.hword {
  border-radius: 2px;
  background: transparent;
  transition: background-color 120ms ease;
}

.hword.is-sel { background: #b3d7ff; }

/* drag */
.hbox {
  position: absolute;
  top: 38%;
  left: 9%;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 62px;
  height: 40px;
  border: 1px solid #d7d7dc;
  border-radius: 7px;
  background: #fff;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
  color: #6f6f79;
  font-family: "Relay Sans Code", ui-monospace, SFMono-Regular, monospace;
  font-size: 10px;
  transition: transform 420ms cubic-bezier(0.2, 0.85, 0.24, 1), box-shadow 200ms ease, opacity 200ms ease;
}

.hbox.is-held {
  box-shadow: 0 8px 18px -8px rgba(0, 0, 0, 0.35);
  transform-origin: center;
}

.hbox.is-dropped { opacity: 0; }

.hdrop {
  position: absolute;
  top: 30%;
  right: 10%;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 78px;
  height: 52px;
  border: 1px dashed #cfcfd6;
  border-radius: 8px;
  color: #9c9c9c;
  font-family: "Relay Sans Code", ui-monospace, SFMono-Regular, monospace;
  font-size: 10px;
  transition: border-color 220ms ease, background-color 220ms ease, color 220ms ease;
}

.hdrop.is-filled {
  border-style: solid;
  border-color: #16161a;
  background: #fafafb;
  color: #16161a;
}

/* draw */
.hdraw {
  display: block;
  width: 100%;
  height: 100%;
}

.hdraw-stroke {
  fill: none;
  stroke: #16161a;
  stroke-width: 3.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* co-work: two cursors, one page */
.hco {
  display: grid;
  gap: 10px;
  padding: 16px 18px;
}

.hco-label {
  display: block;
  margin-bottom: 3px;
  color: #9c9c9c;
  font-family: "Relay Sans Code", ui-monospace, SFMono-Regular, monospace;
  font-size: 8.5px;
  letter-spacing: 0.055em;
  text-transform: uppercase;
}

.hco-input {
  min-height: 26px;
  padding: 5px 8px;
  border: 1px solid #e3e3e7;
  border-radius: 6px;
  color: #1c1c1f;
  font-size: 11.5px;
  line-height: 1.3;
  transition: border-color 180ms ease, box-shadow 180ms ease;
}

/* The focus ring is tinted per actor, so at a glance you can see WHICH of you is in which
   field — the same information the two cursor colours carry, for anyone who missed the
   pointers moving. */
.hco-input.is-agent.is-active {
  border-color: #4a90d9;
  box-shadow: 0 0 0 2px rgba(74, 144, 217, 0.16);
}

.hco-input.is-you.is-active {
  border-color: #8f8f99;
  box-shadow: 0 0 0 2px rgba(75, 75, 83, 0.12);
}

.hcursor--you {
  position: absolute;
  top: 0;
  left: 0;
  pointer-events: none;
  opacity: 0;
  transition: transform 420ms cubic-bezier(0.2, 0.85, 0.24, 1), opacity 220ms ease;
}

.hcursor--you.is-on { opacity: 1; }
.hcursor--you svg { display: block; filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.25)); }

.hco-pill {
  position: absolute;
  top: 19px;
  left: 17px;
  padding: 2px 6px;
  border: 1.5px solid #c9c9cf;
  border-radius: 5px;
  background: #fff;
  color: #4b4b53;
  font-family: "Relay Sans Code", ui-monospace, SFMono-Regular, monospace;
  font-size: 8.5px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* browser window + tabs */
.hwin {
  display: flex;
  flex-direction: column;
  height: 100%;
  background: #f2f2f5;
}

.hwin-bar {
  display: flex;
  align-items: flex-end;
  gap: 4px;
  padding: 7px 8px 0;
}

/* A tab, not a chip: rounded on top only, and the active one loses its bottom edge so it
   joins the page beneath it. That join is what makes the shape read as a browser. */
.htab {
  position: relative;
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 1 1 0;
  min-width: 0;
  padding: 6px 9px 7px;
  border: 1px solid #e2e2e8;
  border-bottom: 0;
  border-radius: 8px 8px 0 0;
  background: #e8e8ee;
  overflow: hidden;
}

.htab.is-active {
  background: #fff;
  border-color: #dcdce2;
}

/* Lifts the active tab over the body's top border so the two become one surface. */
.htab.is-active::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 1px;
  background: #fff;
}

.htab-fav {
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  border-radius: 2px;
  background: #4a90d9;
  animation: htab-pulse 1.4s ease-in-out infinite;
}

.htab-fav.is-you {
  background: #cfcfd6;
  animation: none;
}

.htab.is-done .htab-fav {
  background: #1c7a4a;
  animation: none;
}

@keyframes htab-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}

.htab-label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  color: #6f6f79;
  font-family: "Relay Sans Code", ui-monospace, SFMono-Regular, monospace;
  font-size: 9px;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.htab.is-active .htab-label { color: #1c1c1f; }

/* The progress sits on the tab's own bottom edge, the way a loading tab reads in a browser. */
.htab-fill {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 0;
  height: 2px;
  background: #4a90d9;
  transition: width 190ms linear, background-color 200ms ease;
}

.htab.is-done .htab-fill { background: #1c7a4a; }

.hwin-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 7px;
  padding: 0 14px;
  border-top: 1px solid #dcdce2;
  background: #fff;
}

.hwin-line {
  height: 6px;
  border-radius: 3px;
  background: #f0f0f4;
}

.harness-demo figcaption {
  margin-top: 9px;
  color: #6f6f79;
  font-family: "Relay Fustat", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-size: 14px;
  line-height: 1.35;
}

/* ── the harness reel ─────────────────────────────────────────────────────
   Six of the harness claims are now recordings of Relay doing the thing, in a
   real browser, on a real site, framed in a MacBook. The drawn stages above
   were honest about being drawings; a drawing still cannot settle the question
   the section raises, which is whether the harness really moves like a hand.

   The reel deliberately leaves the 560px column — these are whole-desktop
   captures, and at column width the page inside the laptop is unreadable, which
   would throw away the only thing the clip is for. It starts at the column
   though: left edge on the paragraph above it, running off to the right. */
.reel {
  position: relative;
  /* Full-bleed on a LEFT-ALIGNED page. `calc(50% - 50vw)` is the usual trick and
     it is wrong here — it assumes the column is centred, and this one is pinned
     to the left gutter, so the reel landed 425px off the viewport. Pulling back
     by exactly the gutter puts the left edge on the viewport edge, and 100vw
     then ends flush with the right edge rather than overflowing the page. */
  width: 100vw;
  margin: 40px 0 0 calc(-1 * var(--page-gutter, 0px));
}

.reel-viewport {
  /* Scroll-snap does the paging, so the reel is swipeable on a trackpad and on
     a phone, and it still works with the JS removed — every clip stays
     reachable and every poster frame visible. */
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  /* Snapping ignores the track's padding — a `start` snap puts the slide's edge on
     the SCROLLPORT's edge, which slid the first clip back to the window edge no
     matter what the padding said. scroll-padding is the mechanism that moves the
     snap line itself, so the resting position is the column the prose starts at. */
  scroll-padding-left: max(24px, var(--page-gutter, 24px));
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}

.reel-viewport::-webkit-scrollbar {
  display: none;
}

.reel-track {
  display: flex;
  /* Stretch, so every slide is as tall as the tallest — see .reel-slide .mbp below,
     which is what actually lines the laptops up. */
  align-items: stretch;
  gap: 22px;
  /* Starts where the section's prose starts. The reel used to centre its slides in
     the viewport, which floated it away from the column it belongs to — the copy
     began at the gutter and the clip that illustrates it began somewhere else. The
     left edge is now the paragraph's left edge; the strip still runs off the right,
     which is what says there is more of it. */
  padding-left: max(24px, var(--page-gutter, 24px));
  margin: 0;
  list-style: none;
}

/* Room after the last slide, so it can reach the left edge like every other one.
   Without it the strip runs out of scroll before the last clip gets there: it rests
   mid-window, and the resting position is then nearest the SECOND-TO-LAST slide, so
   the reel snapped backwards a slide at the end of every round.

   A real list item rather than padding-right on the track: a scroll container's end
   padding is not part of its scrollable overflow (the padding computed to the 88px
   asked for and the scroll width behaved as if it were zero), and a ::after on the
   track measured 892px wide without moving the scroll width either. An element in
   the flow does what both were meant to. */
.reel-spacer {
  flex: 0 0 max(24px, calc(100vw - min(940px, 88vw) - var(--page-gutter, 24px)));
  /* Needs a height. Empty, it laid out in the right place at the right width and the
     scroll width came back as if it were not there at all — a zero-area box
     contributes nothing to scrollable overflow. One pixel is enough and is invisible
     against the slide beside it. */
  height: 1px;
  list-style: none;
}

.reel-slide {
  display: flex;
  flex-direction: column;
  flex: 0 0 auto;
  width: min(940px, 88vw);
  padding: 34px 34px 0;
  border-radius: 26px;
  background: #f4f4f6;
  scroll-snap-align: start;
  /* Neighbours recede but stay legible, so it reads as "there is more" without
     competing with the clip you are meant to be watching. */
  opacity: 0.45;
  transition: opacity 420ms ease;
}

.reel-slide[data-active] {
  opacity: 1;
}

.reel-caption {
  max-width: 30em;
  margin: 0 auto 26px;
  color: #6f6f79;
  font-family: "Relay Fustat", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-size: 20px;
  line-height: 1.32;
  letter-spacing: -0.012em;
  text-align: center;
}

.reel-caption strong {
  color: var(--ink);
  font-weight: 600;
}

/* Pushed to the bottom of the slide, which is how the laptops line up with each
   other. Captions run to two lines or three depending on the clip, and with the
   slides top-aligned that difference went straight into the artwork: the laptop
   beside a three-line caption sat 22px lower than its neighbour and the two screens
   ended at different heights. Equal-height slides plus a laptop pinned to the
   bottom of each means the extra line eats into the space above the caption
   instead, where nobody can see it. */
.reel-slide .mbp {
  margin-top: auto;
}

/* ── MacBook shell ───────────────────────────────────────────────────────
   Drawn, not a PNG frame: it has to hold up from 940px down to a phone, and the
   notch has to sit *over* the captured menu bar the way the real one sits over
   the real menu bar.

   .mbp-screen's aspect ratio is the captured display's own. If it and
   TARGET_WIDTH/TARGET_HEIGHT in scripts/marketing/encode_demo.py ever disagree,
   the video letterboxes inside its own bezel, which reads as fake instantly. */
.mbp {
  position: relative;
  margin: 0;
  padding: 10px 10px 0;
  border-radius: 20px 20px 0 0;
  background: linear-gradient(#3a3a3f, #1b1b1f 40%);
  box-shadow: 0 22px 44px -20px rgba(0, 0, 0, 0.45);
}

.mbp-screen {
  position: relative;
  aspect-ratio: 1512 / 982;
  overflow: hidden;
  border-radius: 11px 11px 0 0;
  background: #000;
}

.mbp-screen video,
.mbp-screen img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
}

/* ~175pt wide and ~33pt deep on a 1512pt display, kept as percentages so it
   stays proportional at every card width. */
.mbp-notch {
  position: absolute;
  top: 0;
  left: 50%;
  width: 11.6%;
  height: 3.4%;
  transform: translateX(-50%);
  border-radius: 0 0 7px 7px;
  background: #1b1b1f;
  pointer-events: none;
}

/* Which browser the clip was actually filmed in. Worth naming: the section
   below claims Relay works in the browser you already use, and a different
   badge on each slide demonstrates that rather than asserting it. */
.mbp-browser {
  position: absolute;
  right: 16px;
  bottom: 14px;
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 5px 10px 5px 6px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.82);
  backdrop-filter: blur(8px);
  color: #3a3a42;
  font-family: "Relay Fustat", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-size: 12px;
  line-height: 1;
}

.mbp-browser img {
  width: 16px;
  height: 16px;
}

.reel-controls {
  display: flex;
  align-items: center;
  /* Under the strip's left edge, not the window's centre. Centred controls read as
     belonging to the page rather than to the reel now that the reel itself starts
     at the column. */
  justify-content: flex-start;
  gap: 12px;
  margin-top: 26px;
  padding-left: max(24px, var(--page-gutter, 24px));
}

.reel-btn {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: #f0f0f2;
  color: #3a3a42;
  cursor: pointer;
  transition: background 160ms ease;
}

.reel-btn:hover {
  background: #e6e6ea;
}

.reel-btn:focus-visible {
  outline: 2px solid #155ed1;
  outline-offset: 3px;
}

.reel-btn svg {
  display: block;
  width: 15px;
  height: 15px;
}

/* `display: block` above beats the `hidden` attribute, which is only `display: none`
   from the user-agent stylesheet — so BOTH icons rendered, one on top of the other,
   and the button looked identical whether the reel was playing or paused. The script
   was toggling `hidden` correctly the whole time; nothing downstream honoured it. */
.reel-btn svg[hidden] {
  display: none;
}

.reel-dots {
  display: flex;
  align-items: center;
  gap: 9px;
  height: 40px;
  padding: 0 18px;
  border-radius: 999px;
  background: #f0f0f2;
}

.reel-dot {
  width: 7px;
  height: 7px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: #c9c9d0;
  cursor: pointer;
  transition: width 320ms ease, background 320ms ease;
}

.reel-dot[aria-current="true"] {
  width: 30px;
  background: #3a3a42;
}

.reel-dot:focus-visible {
  outline: 2px solid #155ed1;
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .reel-slide,
  .reel-dot {
    transition: none;
  }
}

@media (max-width: 700px) {
  .reel-slide {
    padding: 22px 18px 0;
    border-radius: 18px;
  }

  .reel-caption {
    margin-bottom: 18px;
    font-size: 16px;
  }
}

.landing-context {
  margin-top: 92px;
}

.context-section + .context-section {
  margin-top: 72px;
}

.reference-home .context-section h2 {
  margin: 0 0 14px;
  font-family: "Relay Goudy", Georgia, serif;
  font-size: 27px;
  font-weight: 400;
  line-height: 1.12;
  letter-spacing: -0.01em;
}

.reference-home .context-section p:last-child {
  margin-bottom: 0;
}

.reference-home .feature-intro {
  margin: 136px 0 0;
  font-family: "Relay Goudy", Georgia, serif;
  font-size: 27px;
  font-weight: 400;
  line-height: 1.12;
  letter-spacing: -0.01em;
  color: var(--ink);
}

.feature-intro a {
  color: inherit;
  text-decoration-color: currentColor;
  text-decoration-thickness: 1px;
  text-underline-offset: 1px;
}

.reference-home .signup {
  gap: 7px;
  margin-top: 11px;
}

.reference-home .signup input,
.reference-home .signup button {
  height: 37px;
  border-color: #cfcfd3;
  border-radius: 8px;
  font-size: 10px;
}

.reference-home .signup input {
  width: 100%;
  padding: 0 11px;
}

.reference-home .email-field {
  position: relative;
  flex: 1 1 224px;
  min-width: 0;
}

.reference-home .email-field label {
  position: absolute;
  top: 50%;
  left: 11px;
  z-index: 1;
  transform: translateY(-50%);
  font-family: "Relay Sans Code", ui-monospace, SFMono-Regular, monospace;
  font-size: 10px;
  line-height: 1;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #73737a;
  pointer-events: none;
}

.reference-home .email-field:focus-within label,
.reference-home .email-field:has(input:not(:placeholder-shown)) label {
  opacity: 0;
}

.reference-home .signup button {
  gap: 8px;
  padding: 0 12px;
  border-color: #006bd6;
  background: #006bd6;
  color: #f3f9ff;
  box-shadow: inset 0 3px 2.4px rgba(255, 255, 255, 0.25);
  transition: filter 140ms ease, transform 140ms ease, opacity 140ms ease;
}

.reference-home .signup button:hover:not(:disabled) {
  border-color: #006bd6;
  background: #006bd6;
  filter: brightness(0.96);
}

.reference-home .signup button:active:not(:disabled) {
  transform: translateY(1px);
}

.reference-home .signup button svg {
  width: 14px;
  height: 14px;
}

.reference-home .signup button:focus-visible,
.reference-home .signup input:focus-visible,
.reference-home .signup button:focus,
.reference-home .signup input:focus {
  outline: 2px solid #155ed1;
  outline-offset: 3px;
}

.reference-home .note {
  min-height: 0;
  margin: 10px 0 0;
  font-size: 13px;
}

.reference-home .note:empty {
  display: none;
}

.feature-list {
  margin-top: 47px;
}

.feature {
  margin-bottom: 52px;
  scroll-margin-top: 32px;
}

.feature:last-child {
  margin-bottom: 0;
}

.reference-home .feature-label {
  display: flex;
  align-items: center;
  gap: 5px;
  margin: 0 0 6px;
  font-family: "Relay Sans Code", ui-monospace, SFMono-Regular, monospace;
  font-size: 12px;
  line-height: 1;
  letter-spacing: 0.035em;
  text-transform: uppercase;
  color: var(--ink);
}

.feature-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 15px;
  height: 15px;
  flex: 0 0 15px;
}

.feature-icon svg {
  display: block;
  width: 100%;
  height: 100%;
}

.reference-home h2 {
  margin: 0 0 9px;
  font-family: "Relay Goudy", Georgia, serif;
  font-size: 21px;
  font-weight: 400;
  line-height: 1.15;
  letter-spacing: -0.005em;
}

.reference-home .feature p:last-child {
  margin-bottom: 0;
}

.waitlist-panel {
  margin-top: 161px;
  scroll-margin-top: 48px;
}

.reference-home .waitlist-panel p {
  font-size: 13px;
  line-height: 1.42;
}

h2 {
  font-weight: 700;
  font-size: 21px;
  line-height: 1.3;
  letter-spacing: -0.015em;
  color: var(--ink);
  margin: 0 0 6px;
}

p {
  margin: 0 0 16px;
}

.lede {
  font-size: 19px;
  margin-bottom: 46px;
}

a {
  color: var(--link);
  text-decoration: underline;
  text-underline-offset: 2px;
}

a:hover {
  opacity: 0.75;
}

/* ── waitlist ─────────────────────────────────────────────────────────── */

.signup {
  display: flex;
  gap: 12px;
  margin-top: 22px;
}

.signup input,
.signup button {
  font-family: "Relay Sans Code", ui-monospace, SFMono-Regular, monospace;
  font-size: 13px;
  letter-spacing: 0.06em;
  height: 52px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: #fff;
  color: var(--ink);
  outline: none;
}

/* The BUTTON is uppercase; what the user types is not. The uppercase used to sit on the
   shared rule, so an email typed into the field rendered as SOMEONE@EXAMPLE.COM. It only
   changed the display — the value submitted was always what they typed — but it looked
   like we were shouting their address back at them. */
.signup button {
  text-transform: uppercase;
}

.signup input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 0 16px;
  text-transform: none;
  font-size: 14px;
}

/* Only the empty-state hint is uppercase — that is the label, not their data. */
.signup input::placeholder {
  color: #a3a3ad;
  font-size: 13px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.signup input:focus {
  border-color: #9a9aa5;
}

.signup input[aria-invalid="true"] {
  border-color: #d0453c;
  background: #fdf4f3;
}

.signup button {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 0 20px;
  cursor: pointer;
  transition: border-color 0.15s ease, opacity 0.15s ease;
}

.signup button:hover {
  border-color: #9a9aa5;
}

.signup button:disabled {
  opacity: 0.5;
  cursor: default;
}

.signup button svg {
  display: block;
}

.note {
  margin-top: 14px;
  font-size: 16px;
  color: var(--body);
  min-height: 1.5em;
}

.note--ok {
  color: #1c7a4a;
}

.note--error {
  color: #c0392f;
}

/* ── footer ───────────────────────────────────────────────────────────── */

footer {
  width: 100%;
  max-width: var(--col);
  margin-top: 92px;
  display: flex;
  flex-wrap: wrap;
  gap: 10px 22px;
  font-size: 14px;
}

/* Language switcher. Quiet by design — it sits below the footer because it is wayfinding,
   not navigation: the reader who needs it is looking for it, and the reader who does not
   should be able to ignore a row of small grey words. */
.langs {
  width: 100%;
  max-width: none;
  margin: 22px 0 0;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 14px;
  font-size: 12.5px;
  line-height: 1.6;
}

.langs a {
  color: #8a8a93;
  text-decoration: none;
}

.langs a:hover {
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* The current language is stated, not just styled — a grey word among grey words is not an
   answer to "which one am I on". */
.langs a[aria-current="true"] {
  color: var(--ink);
}

/* The home page's footer is centred under the column rather than ranged left with it. */
body.home footer {
  max-width: none;
  justify-content: flex-start;
  margin-top: 85px;
}

/* A footer link is one thing to click. It may move to the next row, but it must never break
   across two — "Terms and / Conditions" reads as two links to the eye, and the address has
   to stay whole to be an address. */
footer a {
  color: var(--body);
  text-decoration: none;
  white-space: nowrap;
}

footer a:hover {
  text-decoration: underline;
}

/* ── five working lives ───────────────────────────────────────────────── */

/* One column, on the same measure as the rest of the page. The stories sit below the fold on
   purpose: the pitch and the form come first, and the lives are what you find if you stay. */
.split {
  width: 100%;
  max-width: var(--col);
}

.lives {
  min-width: 0;
}

/* The seam between the pitch and the stories. Short and centred, so it reads as a breath
   rather than a border. */
.lives::before {
  content: "";
  display: block;
  width: 104px;
  height: 1px;
  background: var(--line);
  margin: 92px auto 104px;
}

/* The names sit UNDER the story: you read one life, then you see whose it was and who else
   is here. The strip is the footer of the section, not its heading. */
.tabs {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;
  gap: 8px;
  margin-top: 10px;
}

/* Each tab sizes to its own name, so the strip reads as five people rather than five
   buttons of equal weight. */
.tab {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 9px 13px 10px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: #fff;
  text-align: left;
  cursor: pointer;
  font-family: inherit;
  transition: border-color 0.15s ease, background 0.15s ease;
}

.tab:hover {
  border-color: #9a9aa5;
}

/* A border that changes colour is not a focus indicator. Keyboard users get a real ring. */
.tab:focus-visible,
.signup button:focus-visible,
.signup input:focus-visible {
  outline: 2px solid var(--link);
  outline-offset: 2px;
}

.tab.is-on {
  background: #f3f3f5;
  border-color: #c6c6ce;
}

.tab-name {
  font-family: "Relay Hand SC", "Bradley Hand", cursive;
  font-size: 17px;
  color: var(--ink);
  line-height: 1.2;
}

/* Not var(--muted): at 10.5px that grey lands at 4.48:1 on the selected tab's tint, just
   under the 4.5 AA floor. This one clears it. */
.tab-meta {
  font-family: "Relay Sans Code", ui-monospace, SFMono-Regular, monospace;
  font-size: 10.5px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #5c5c66;
  line-height: 1.2;
}

/* The separator is type, not a border, so it does not follow --line: at #f1f1f1 it vanished
   and "38 | RADIOGRAPHER" read as two stray words. */
.tab-meta i {
  font-style: normal;
  margin: 0 4px;
  color: #c9c9d1;
}

/* The strip sits below the story, so an unequal story would drag the buttons up and down as
   you switched between them — you would click a name and watch the row move out from under
   the cursor. The floor is the tallest of the five, which holds the strip still. */
.story {
  min-height: 670px;
  scroll-margin-top: 32px;
}

.story:focus-visible {
  outline: 2px solid var(--link);
  outline-offset: 8px;
  border-radius: 4px;
}

.meet {
  font-family: "Relay Hand", "Bradley Hand", cursive;
  font-size: 19px;
  color: var(--ink);
  margin-bottom: 4px;
}

.story strong {
  font-weight: 600;
  color: var(--ink);
}

.kicker {
  margin-bottom: 0;
}

/* ── long-form documents ──────────────────────────────────────────────── */

.doc main {
  max-width: 680px;
}

.doc h1 {
  font-size: 32px;
  margin-bottom: 6px;
}

.doc h2 {
  font-size: 18px;
  margin: 34px 0 8px;
}

.doc .updated {
  color: var(--muted);
  font-size: 15px;
  margin-bottom: 34px;
}

.doc p,
.doc li {
  font-size: 16.5px;
  line-height: 1.62;
}

.doc ul {
  margin: 0 0 16px;
  padding-left: 22px;
}

.doc li {
  margin-bottom: 7px;
}

.doc table {
  width: 100%;
  border-collapse: collapse;
  margin: 8px 0 18px;
  font-size: 15.5px;
}

.doc th,
.doc td {
  text-align: left;
  vertical-align: top;
  padding: 10px 12px 10px 0;
  border-bottom: 1px solid #ececf0;
}

.doc th {
  font-weight: 600;
  color: var(--ink);
}

.back {
  display: inline-block;
  margin-bottom: 28px;
  font-size: 15px;
}

@media (max-width: 560px) {
  h1 {
    font-size: 30px;
  }
  .signup {
    flex-direction: column;
  }
  .signup button {
    justify-content: center;
  }
}

@media (max-width: 560px) {
  body.home {
    --page-gutter: 24px;
    padding: 36px var(--page-gutter) 48px;
  }

  .reference-home main {
    width: 75vw;
  }

  .reference-home .wordmark {
    margin-bottom: 24px;
  }

  .landing-story {
    margin-top: 60px;
  }

  .reference-home .story-section h2,
  .reference-home .why-use > h2 {
    font-size: 21px;
  }

  body.home h1 {
    font-size: 27px;
    line-height: 1.1;
  }

  .reference-home p,
  .reference-home .lede,
  .reference-home .reason p {
    font-size: 13.5px;
    line-height: 1.46;
  }

  .reason h3 {
    font-size: 12px;
  }

  .solution-section {
    margin-top: 72px;
  }

  .why-use {
    margin-top: 104px;
  }

  .harness-section {
    margin-top: 72px;
  }

  .harness-demo figcaption {
    font-size: 12px;
  }

  .reference-home .signup {
    flex-direction: row;
  }

  .reference-home .signup button {
    justify-content: center;
  }

  body.home footer {
    margin-top: 72px;
  }
}

@media (max-width: 390px) {
  .reference-home .signup {
    flex-direction: column;
  }

  .reference-home .email-field {
    flex-basis: auto;
  }
}

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

  .relay-intro {
    display: none !important;
  }
}

/* ── support ──────────────────────────────────────────────────────────── */

.support-h1 {
  font-size: 34px;
  margin-bottom: 8px;
}

.support-lede {
  margin-bottom: 30px;
  max-width: 480px;
}

.support {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.support-row {
  display: flex;
  gap: 12px;
}

.support input,
.support textarea,
.support button {
  font-family: "Relay Sans Code", ui-monospace, SFMono-Regular, monospace;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: #fff;
  color: var(--ink);
  outline: none;
  /* The label is uppercase; what the user types is not. Shouting someone's own message back
     at them is the bug we just fixed on the waitlist form. */
  text-transform: none;
  font-size: 14px;
}

.support input {
  flex: 1 1 auto;
  min-width: 0;
  height: 52px;
  padding: 0 16px;
}

.support textarea {
  padding: 15px 16px;
  line-height: 1.5;
  resize: vertical;
  min-height: 150px;
}

.support input::placeholder,
.support textarea::placeholder {
  color: #a3a3ad;
  font-size: 13px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.support input:focus,
.support textarea:focus {
  border-color: #9a9aa5;
}

.support input[aria-invalid="true"],
.support textarea[aria-invalid="true"] {
  border-color: #d0453c;
  background: #fdf4f3;
}

.support-foot {
  display: flex;
  justify-content: flex-end;
  margin-top: 2px;
}

.support button {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  height: 52px;
  padding: 0 22px;
  font-size: 13px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
  transition: border-color 0.15s ease, opacity 0.15s ease;
}

.support button:hover {
  border-color: #9a9aa5;
}

.support button:disabled {
  opacity: 0.5;
  cursor: default;
}

/* Honeypot: off-screen rather than display:none — some bots skip hidden fields, and the
   point is that they FILL it. */
.hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

@media (max-width: 560px) {
  .support-row {
    flex-direction: column;
  }
}

/* ── the roaming cursor ─────────────────────────────────────────────────
   Relay's pointer drifting through the empty gutter beside the column. Fixed, so scrolling
   keeps it in view without a scroll handler. Inert by construction: pointer-events none, and
   a z-index below the extension's launch takeover so it can never sit over that. */
.roamer {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 900;
  pointer-events: none;
  opacity: 0;
  transition: transform 1700ms cubic-bezier(0.22, 0.7, 0.2, 1), opacity 600ms ease;
  will-change: transform;
}

.roamer.is-on { opacity: 1; }

/* Dragging is the one time the cursor must track exactly, not glide: the selection is being
   extended under the tip, and a lagging pointer would trail behind its own highlight. */
.roamer.is-dragging { transition: opacity 600ms ease; }

.roamer.is-pressed .roamer-pointer {
  transform: scale(0.88);
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.34));
}

.roamer-pointer { transition: transform 160ms ease; }

/* The press has to have a visible consequence, but nothing on the page may actually change —
   so the consequence is a ring that expands and fades where the tip landed. */
.roamer-ring {
  position: fixed;
  z-index: 899;
  width: 10px;
  height: 10px;
  margin: -5px 0 0 -5px;
  border: 1.5px solid #4a90d9;
  border-radius: 50%;
  pointer-events: none;
  animation: roamer-ring 620ms cubic-bezier(0.2, 0.7, 0.3, 1) forwards;
}

@keyframes roamer-ring {
  from { opacity: 0.75; transform: scale(1); }
  to { opacity: 0; transform: scale(3.6); }
}

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

.roamer-shell {
  position: relative;
  display: block;
  animation: demo-cursor-idle 3.5s ease-in-out infinite;
}

.roamer-pointer {
  display: block;
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.28));
}

.roamer-label {
  position: absolute;
  top: 29px;
  left: 31px;
  white-space: nowrap;
  border: 2px solid #9c9c9c;
  border-radius: 7px;
  padding: 4px 8px;
  background: #fff;
  color: #000;
  font-family: "Relay Sans Code", ui-monospace, SFMono-Regular, monospace;
  font-size: 10px;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}

.roamer-label:empty { display: none; }

/* No gutter to play in, no roaming: it would cross the copy. */
@media (max-width: 1000px) {
  .roamer { display: none; }
}

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

/* Turnstile's demo gate. It is never `hidden` and never display:none — a widget told to
   render inside a non-rendered element yields a shell with no challenge iframe and no
   callback, which is exactly how the demo session silently failed to mint. Empty and
   fixed, it takes up nothing; `.is-live` is added only while a challenge is on screen,
   so a visitor who has to click one can actually see it. */
#relay-demo-gate {
  position: fixed;
  right: 16px;
  bottom: 16px;
  z-index: 2147483000;
}

#relay-demo-gate.is-live {
  padding: 12px;
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.92);
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.18);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
