@charset "UTF-8";

/* =============================================================================
   Meletao — meletao.io
   "The Drawcord" carried from the app onto the web.

   Every colour here mirrors packages/shared/src/tokens.ts (darkTheme). The app
   forbids hardcoded hex outside that token file; this stylesheet is the web's
   copy of the same contract, so when tokens.ts changes, these values change
   with it and nowhere else.

   The three laws the palette depends on, unchanged from the app:
     1. `accent` (jade) is both the accent and the control. Every interactive
        element and every unit of progress is jade, and progress is always drawn
        as cord running in a `track` channel.
     2. `brass` is spent on mastery and nowhere else. Its scarcity is the whole
        reason it reads as an achievement.
     3. `tension` (a revealed word) is NOT `danger` (a destructive action), and
        never carries meaning by colour alone.

   Type discipline, also from the app: Faustina is for scripture ONLY. Every
   other word on this page — headline, body, label, button — is Archivo. That
   is why the verses look like the point and the marketing looks like chrome.
============================================================================= */

/* ---------- tokens ------------------------------------------------------- */

:root {
  /* Surfaces. Ratios noted are against this token's real background. */
  --bg: #12100f;              /* matte logwood-black silk — the page ground */
  --surface: #1b1817;         /* raised silk panel */
  --surface-alt: #232020;     /* sheets and modals, one step above surface */
  --border: rgba(237, 230, 218, 0.09);        /* channel stitching */
  --border-strong: rgba(237, 230, 218, 0.18); /* stitching at emphasis */

  /* Text */
  --text: #ede6da;            /* 15.30:1 on bg, 14.23:1 on surface */
  --text-muted: #9a918a;      /*  6.13:1 on bg,  5.71:1 on surface */
  --text-faint: #6b635e;      /*  captions only — below body threshold by design */

  /* The cord */
  --accent: #2fa98c;          /*  6.48:1 on bg */
  --accent-soft: rgba(47, 169, 140, 0.14);
  --accent-on: #08120f;       /*  6.50:1 on a solid accent fill */

  /* Tension — a revealed word, a missed keystroke. Never a delete button. */
  --tension: #d4553c;         /*  4.67:1 on bg */
  --tension-soft: rgba(212, 85, 60, 0.14);

  /* Oxidised brass — MASTERY ONLY */
  --brass: #b08a48;           /*  5.94:1 on bg */
  --brass-soft: rgba(176, 138, 72, 0.14);

  /* Meters */
  --track: rgba(237, 230, 218, 0.06);
  --heat1: rgba(47, 169, 140, 0.26);
  --heat2: rgba(47, 169, 140, 0.55);
  --heat3: #2fa98c;

  --danger: #d4553c;

  /* Eight dyed silk threads — the avatar hues, warm to cool, straight from
     tokens.ts. A member's hue rides on the person, so the order is stable. */
  --thread-1: #8c3a2a;  /* madder */
  --thread-2: #7e5a1e;  /* ochre */
  --thread-3: #4f5b24;  /* moss */
  --thread-4: #1e5f4e;  /* jade */
  --thread-5: #1f5063;  /* teal */
  --thread-6: #2c3e7a;  /* indigo */
  --thread-7: #5a2e63;  /* aubergine */
  --thread-8: #7a2f47;  /* cochineal */

  /* Type */
  --serif: "Faustina", "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
  --ui: "Archivo", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  /* 4pt spacing scale, extended past the app's xxl for page-scale rhythm */
  --s-xs: 4px;
  --s-sm: 8px;
  --s-md: 12px;
  --s-lg: 16px;
  --s-xl: 24px;
  --s-2xl: 32px;
  --s-3xl: 48px;
  --s-4xl: 72px;
  --s-5xl: 112px;

  /* Cloth, not glass */
  --r-sm: 10px;
  --r-md: 14px;
  --r-lg: 20px;
  --r-pill: 999px;

  /* The cord rail: where the armature sits and how wide its channel is */
  --rail-x: 116px;
  --cord-w: 3px;

  --page: 1180px;
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
}

@media (max-width: 900px) {
  :root {
    --rail-x: 28px;
    --s-4xl: 56px;
    --s-5xl: 76px;
  }
}

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

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

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

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

body {
  margin: 0;

  /* REQUIRED, not cosmetic: `.rail` is absolutely positioned with `top: 0;
     bottom: 0`, so it needs a positioned ancestor to stretch against. Without
     this it resolves against the initial containing block and the cord ends up
     exactly one viewport tall — the armature would stop after the first screen
     and every section below it would hang off nothing. */
  position: relative;

  background-color: var(--bg);
  color: var(--text);
  font-family: var(--ui);
  font-size: 17px;
  line-height: 1.65;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;

  /* Matte silk: a warm sheen falling from the top-left and a fine weave, both
     kept far below the threshold where they'd read as a gradient. */
  background-image:
    radial-gradient(120% 80% at 8% -10%, rgba(237, 230, 218, 0.045), transparent 60%),
    repeating-linear-gradient(
      90deg,
      rgba(237, 230, 218, 0.012) 0 1px,
      transparent 1px 3px
    );
  background-attachment: fixed, scroll;
}

img,
svg {
  display: block;
  max-width: 100%;
}

button {
  font: inherit;
  color: inherit;
}

a {
  color: var(--accent);
  text-decoration-color: var(--accent-soft);
  text-underline-offset: 3px;
  transition: text-decoration-color 160ms var(--ease), color 160ms var(--ease);
}

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

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

::selection {
  background: var(--accent-soft);
  color: var(--text);
}

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

.skip-link {
  position: absolute;
  left: var(--s-lg);
  top: var(--s-lg);
  z-index: 100;
  padding: var(--s-sm) var(--s-lg);
  background: var(--surface-alt);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-pill);
  color: var(--text);
  text-decoration: none;
  transform: translateY(-200%);
  transition: transform 200ms var(--ease);
}

.skip-link:focus-visible {
  transform: translateY(0);
}

/* ---------- type ------------------------------------------------------- */

h1,
h2,
h3,
h4 {
  margin: 0;
  font-family: var(--ui);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.05;
  text-wrap: balance;
}

/* The offer, set to sit UNDER the scripture beside it. Scripture is the hero on
   every surface of this product; a headline that out-scales the verse it is
   describing has inverted the whole point. */
.h-display {
  font-size: clamp(2.05rem, 3.1vw, 2.95rem);
  line-height: 1.06;
  letter-spacing: -0.032em;
}

.h-section {
  font-size: clamp(1.85rem, 3.6vw, 3rem);
  line-height: 1.06;
  letter-spacing: -0.032em;
}

.h-sub {
  font-size: clamp(1.15rem, 1.6vw, 1.4rem);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.25;
}

p {
  margin: 0;
}

/* Body measure held to 65–75ch wherever prose runs. */
.prose {
  max-width: 68ch;
  color: var(--text-muted);
  font-size: clamp(1rem, 1.1vw, 1.125rem);
  line-height: 1.7;
}

.prose strong {
  color: var(--text);
  font-weight: 600;
}

.prose + .prose {
  margin-top: var(--s-lg);
}

.lede {
  max-width: 62ch;
  color: var(--text-muted);
  font-size: clamp(1.1rem, 1.45vw, 1.3rem);
  line-height: 1.6;
}

.lede strong {
  color: var(--text);
  font-weight: 600;
}

/* Functional label — a legend or a field name. Never an eyebrow over a heading. */
.label {
  font-family: var(--ui);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.caption {
  font-size: 0.82rem;
  line-height: 1.5;
  color: var(--text-faint);
}

/* Scripture. The ONLY thing on this page set in Faustina, at 500 because
   Faustina's regular washes out on this ground. */
.scripture {
  font-family: var(--serif);
  font-weight: 500;
  color: var(--text);
  letter-spacing: -0.005em;
}

.ref {
  font-family: var(--ui);
  font-size: 0.82rem;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-faint);
}

/* ---------- page frame -------------------------------------------------- */

.wrap {
  max-width: var(--page);
  margin: 0 auto;
  padding-left: calc(var(--rail-x) + 64px);
  padding-right: var(--s-xl);
}

@media (max-width: 900px) {
  .wrap {
    padding-left: calc(var(--rail-x) + 30px);
    padding-right: var(--s-xl);
  }
}

/* ---------- the cord armature ------------------------------------------ */

/* One continuous waxed cord, pulled down the page as you read. The track is
   the empty channel; the cord fills it to the scroll position; the aglet is
   brass because reaching the end of the ladder is mastery and nothing else in
   this world may wear brass. */
/* The channel is anchored to the CENTRED content column, not to the viewport
   edge — otherwise on a wide screen the cord strands itself in the left margin
   and the sections stop reading as hanging off it. */
:root {
  --gutter: max(0px, calc((100vw - var(--page)) / 2));
}

.rail {
  position: absolute;
  top: 0;
  bottom: 0;
  left: calc(var(--gutter) + var(--rail-x));
  width: var(--cord-w);
  pointer-events: none;
  z-index: 0;
}

.rail__track {
  position: absolute;
  inset: 0;
  background: var(--track);
  border-radius: var(--r-pill);
}

.rail__cord {
  position: absolute;
  inset: 0;
  border-radius: var(--r-pill);
  background:
    /* the twist of a waxed cord, catching light on one side */
    repeating-linear-gradient(
      -68deg,
      rgba(8, 18, 15, 0.32) 0 2px,
      rgba(255, 255, 255, 0.09) 2px 4px,
      transparent 4px 7px
    ),
    linear-gradient(90deg, #22705f 0%, var(--accent) 55%, #62c4ad 100%);
  box-shadow: 0 0 12px rgba(47, 169, 140, 0.28);

  /* Drawn by scroll. clip-path rather than scaleY so the twist never squashes. */
  --draw: 0%;
  clip-path: inset(0 0 calc(100% - var(--draw)) 0);
}

.rail__aglet {
  position: absolute;
  left: 50%;
  bottom: -2px;
  width: 11px;
  height: 30px;
  transform: translateX(-50%);
  border-radius: 2px 2px 4px 4px;
  background: linear-gradient(180deg, #d8b478 0%, var(--brass) 45%, #7d5f2c 100%);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.55);
  opacity: 0;
  transition: opacity 500ms var(--ease);
}

.rail__aglet[data-lit="1"] {
  opacity: 1;
}

/* Where a section attaches to the cord: a hairline stitch running out of the
   channel into the section. This is the page's one entrance idea. */
.stitch {
  position: absolute;
  left: calc(var(--gutter) + var(--rail-x) + var(--cord-w));
  width: 42px;
  height: 1px;
  background: var(--border-strong);
  transform-origin: left center;
  transition: transform 620ms var(--ease), background-color 620ms var(--ease);
}

.js .stitch {
  transform: scaleX(0);
}

.stitch::after {
  content: "";
  position: absolute;
  left: -7px;
  top: -3.5px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--bg);
  border: 1.5px solid var(--track);
  transition: border-color 500ms var(--ease), box-shadow 500ms var(--ease);
}

.js .is-attached .stitch {
  transform: scaleX(1);
}

.is-attached .stitch::after {
  border-color: var(--accent);
  box-shadow: 0 0 9px rgba(47, 169, 140, 0.45);
}

@media (max-width: 900px) {
  .stitch {
    width: 18px;
  }
}

/* ---------- masthead --------------------------------------------------- */

.masthead {
  position: relative;
  z-index: 3;
  padding-top: var(--s-xl);
  padding-bottom: var(--s-xl);
}

.masthead__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-lg);
}

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: var(--s-md);
  text-decoration: none;
  color: var(--text);
}

.wordmark__mark {
  width: 34px;
  height: 34px;
  border-radius: 9px;
  border: 1px solid var(--border);
  background: #100e0d;
  flex: none;
}

.wordmark__name {
  font-family: var(--ui);
  font-size: 1.06rem;
  font-weight: 600;
  letter-spacing: 0.015em;
}

.wordmark__greek {
  font-family: var(--serif);
  font-weight: 500;
  font-size: 0.95rem;
  color: var(--text-faint);
  letter-spacing: 0;
}

@media (max-width: 620px) {
  .wordmark__greek {
    display: none;
  }
}

.masthead__nav {
  display: flex;
  align-items: center;
  gap: var(--s-xl);
}

.masthead__nav a {
  color: var(--text-muted);
  font-size: 0.94rem;
  font-weight: 500;
  text-decoration: none;
}

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

/* Narrow: the section links go, but the one action does not. A masthead with no
   route to the primary action is a masthead that stopped doing its job. */
@media (max-width: 780px) {
  .masthead__nav a:not([data-cta]) {
    display: none;
  }

  .masthead__nav a[data-cta] {
    color: var(--accent);
    font-weight: 600;
  }
}

/* ---------- sections --------------------------------------------------- */

section {
  position: relative;
}

.band {
  position: relative;
  padding-top: var(--s-5xl);
  padding-bottom: var(--s-2xl);
}

.band--tight {
  padding-top: var(--s-4xl);
}

/* More space above a heading than below it, everywhere. */
.band__head {
  margin-bottom: var(--s-2xl);
}

.band__head .h-section + .lede,
.band__head .h-section + .prose {
  margin-top: var(--s-lg);
}

/* ---------- buttons and the waitlist form ------------------------------ */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-sm);
  min-height: 48px;
  padding: 0 var(--s-xl);
  border: 0;
  border-radius: var(--r-pill);
  background: var(--accent);
  color: var(--accent-on);
  font-family: var(--ui);
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  cursor: pointer;
  transition: transform 160ms var(--ease), box-shadow 200ms var(--ease),
    background-color 160ms var(--ease);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.4), 0 0 0 0 var(--accent-soft);
}

.btn:hover {
  background: #3cbd9e;
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.45), 0 0 0 5px var(--accent-soft);
}

.btn:active {
  transform: translateY(1px);
}

.btn:disabled {
  cursor: not-allowed;
  background: var(--accent-soft);
  color: var(--text-faint);
  box-shadow: none;
}

.btn--ghost {
  background: transparent;
  color: var(--text);
  border: 1px solid var(--border-strong);
  box-shadow: none;
}

.btn--ghost:hover {
  background: rgba(237, 230, 218, 0.04);
  border-color: var(--text-faint);
  box-shadow: none;
}

.signup {
  margin-top: var(--s-2xl);
  max-width: 520px;
}

.signup__row {
  display: flex;
  gap: var(--s-md);
  align-items: stretch;
}

.signup__field {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
}

.signup input[type="email"] {
  width: 100%;
  min-height: 48px;
  padding: 0 var(--s-lg);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-pill);
  color: var(--text);
  font-family: var(--ui);
  font-size: 1rem;
  transition: border-color 180ms var(--ease), box-shadow 180ms var(--ease);
}

.signup input[type="email"]::placeholder {
  color: #8a827c; /* 4.85:1 on --surface — placeholders hold the body floor */
}

.signup input[type="email"]:hover {
  border-color: var(--text-faint);
}

.signup input[type="email"]:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 4px var(--accent-soft);
}

.signup input[type="email"][aria-invalid="true"] {
  border-color: var(--tension);
  box-shadow: 0 0 0 4px var(--tension-soft);
}

.signup__note {
  margin-top: var(--s-md);
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--text-faint);
}

.signup__msg {
  margin-top: var(--s-md);
  font-size: 0.9rem;
  font-weight: 500;
  min-height: 1.4em;
}

.signup__msg[data-state="error"] {
  color: var(--tension);
}

.signup__msg[data-state="ok"] {
  color: var(--accent);
}

/* The error carries an icon as well as a hue — tension never speaks alone. */
.signup__msg svg {
  display: inline-block;
  vertical-align: -3px;
  margin-right: 6px;
}

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

  .signup .btn {
    width: 100%;
  }
}

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

.hero {
  padding-top: var(--s-3xl);
  padding-bottom: var(--s-4xl);
}

.hero__verse {
  margin: 0 0 var(--s-3xl);
  max-width: 21ch;
  font-size: clamp(1.95rem, 3.8vw, 3.5rem);
  line-height: 1.34;
}

.hero__verse .ref {
  display: block;
  margin-top: var(--s-lg);
}

.hero__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.08fr) minmax(0, 0.92fr);
  gap: var(--s-4xl);
  align-items: start;
}

@media (max-width: 1020px) {
  .hero__grid {
    grid-template-columns: 1fr;
    gap: var(--s-3xl);
  }

  /* Stacked, the grid gap already separates verse from offer — the verse's own
     bottom margin would double it into a hole. */
  .hero__verse {
    max-width: 26ch;
    margin-bottom: 0;
  }
}

.hero__foot {
  margin-top: var(--s-3xl);
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-md) var(--s-xl);
  align-items: center;
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--s-sm);
  padding: 6px 14px 6px 11px;
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  background: var(--surface);
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--text-muted);
}

.chip svg {
  color: var(--accent);
  flex: none;
}

/* ---------- the ladder (focal moment) ---------------------------------- */

.ladder {
  position: relative;
}

.ladder__stage {
  position: relative;
  margin-top: var(--s-2xl);
  padding: clamp(var(--s-xl), 4vw, var(--s-3xl));
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: 0 24px 60px -30px rgba(0, 0, 0, 0.8);
}

/* No min-height reserved for rung changes: because a blank keeps the true width
   of the word it hides, the line breaks are identical at every rung, so the
   passage cannot reflow as you climb. That property is doing the work a
   defensive min-height would otherwise have to. */
.ladder__passage {
  font-size: clamp(1.4rem, 2.9vw, 2.35rem);
  line-height: 1.62;
  margin: 0;
}

/* A word and its blank. The body glyphs go transparent and the underline is
   painted at the word's TRUE width — exactly what the app does, and the reason
   you can still count the words and judge their lengths at level 5. */
.w {
  display: inline-block;
}

/* Sized by the body alone, so the rule stops at the word and never runs on
   under its comma. */
.w__slot {
  position: relative;
  display: inline-block;
}

.w__body {
  transition: color 420ms var(--ease);
}

/* Scales with the type, so a blank is a cord at every size rather than a hairline
   at display scale and a slab at 11px. */
.w__rule {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0.06em;
  height: max(1.5px, 0.055em);
  border-radius: 2px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 420ms var(--ease), opacity 420ms var(--ease);
  opacity: 0;
}

.w[data-hidden="1"] .w__body {
  color: transparent;
}

.w[data-hidden="1"] .w__rule {
  transform: scaleX(1);
  opacity: 0.88;
}

/* Punctuation and verse numbers survive every rung. */
.w__punct {
  color: var(--text-muted);
}

.rungs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-sm);
  margin-top: var(--s-2xl);
  padding-top: var(--s-xl);
  border-top: 1px solid var(--border);
}

/* A stage mark is a square rotated 45°; a habit mark is a round dot. The app
   keeps those two shapes apart on purpose, so the page does too. */
.rung {
  display: inline-flex;
  align-items: center;
  gap: var(--s-sm);
  min-height: 44px;
  padding: 0 var(--s-lg) 0 var(--s-md);
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  color: var(--text-muted);
  font-size: 0.9rem;
  font-weight: 500;
  cursor: pointer;
  transition: color 200ms var(--ease), border-color 200ms var(--ease),
    background-color 200ms var(--ease);
}

.rung__mark {
  width: 9px;
  height: 9px;
  flex: none;
  transform: rotate(45deg);
  background: var(--track);
  border: 1px solid var(--border-strong);
  transition: background-color 240ms var(--ease), border-color 240ms var(--ease);
}

.rung:hover {
  color: var(--text);
  border-color: var(--text-faint);
}

.rung[aria-pressed="true"] {
  color: var(--text);
  background: var(--accent-soft);
  border-color: var(--accent);
}

.rung[aria-pressed="true"] .rung__mark,
.rung[data-passed="1"] .rung__mark {
  background: var(--accent);
  border-color: var(--accent);
}

.ladder__readout {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-md);
  margin-top: var(--s-xl);
}

.ladder__helper {
  font-size: 0.98rem;
  color: var(--text-muted);
}

.ladder__helper b {
  color: var(--text);
  font-weight: 600;
}

/* ---------- the channel: THE progress form ----------------------------- */

.channel {
  position: relative;
  height: 6px;
  border-radius: var(--r-pill);
  background: var(--track);
  overflow: hidden;
}

.channel--thin {
  height: 3px;
}

.channel__cord {
  position: absolute;
  inset: 0;

  /* Driven by a scaleX rather than a width so the meters never animate layout.
     `--k` is a unitless 0–1 fraction, matching the `fraction` prop the app's own
     Channel component takes. */
  width: 100%;
  transform: scaleX(var(--k, 0));
  transform-origin: left center;
  border-radius: var(--r-pill);
  background: var(--accent);
  transition: transform 700ms var(--ease);
}

.channel__cord--muted {
  background: var(--accent);
  opacity: 0.45;
}

.channel__cord--brass {
  background: var(--brass);
}

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

.phone {
  position: relative;
  width: 100%;
  max-width: 340px;
  aspect-ratio: 402 / 874;
  margin: 0 auto;
  padding: 11px;
  border-radius: 46px;
  background: linear-gradient(155deg, #2b2726 0%, #131110 46%, #2b2726 100%);
  box-shadow:
    0 0 0 1px rgba(237, 230, 218, 0.11),
    0 40px 80px -34px rgba(0, 0, 0, 0.9),
    inset 0 1px 1px rgba(237, 230, 218, 0.08);
}

.phone__screen {
  position: relative;
  height: 100%;
  border-radius: 36px;
  background: var(--bg);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.phone__island {
  position: absolute;
  top: 9px;
  left: 50%;
  transform: translateX(-50%);
  width: 78px;
  height: 21px;
  border-radius: var(--r-pill);
  background: #000;
  z-index: 4;
}

.phone__home {
  position: absolute;
  bottom: 7px;
  left: 50%;
  transform: translateX(-50%);
  width: 108px;
  height: 4px;
  border-radius: var(--r-pill);
  background: rgba(237, 230, 218, 0.26);
  z-index: 4;
}

/* Screen internals, scaled to the frame. Everything inside a phone is small
   demonstration chrome, so it drops below the page's body floor by design and
   is mirrored in real prose beside it. */
.scr {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  padding: 40px 15px 22px;
  font-size: 11px;
  line-height: 1.45;
  min-height: 0;
}

.scr__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding-bottom: 11px;
}

.scr__back {
  color: var(--text-muted);
}

.scr__title {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text);
}

.scr__marks {
  display: inline-flex;
  gap: 4px;
  align-items: center;
  padding: 3px;
  border-radius: var(--r-pill);
}

.scr__marks[data-mastered="1"] {
  box-shadow: inset 0 0 0 1px var(--brass);
}

.scr__mark {
  width: 6px;
  height: 6px;
  transform: rotate(45deg);
  background: var(--track);
  border: 1px solid var(--border-strong);
}

.scr__mark[data-on="1"] {
  background: var(--accent);
  border-color: var(--accent);
}

/* Scripture takes the room on a drill screen, the same as it does in the app —
   app chrome loses to the text wherever the two compete for space. */
.scr__verse {
  font-size: 17.5px;
  line-height: 1.66;
  margin: 14px 0 0;
}

/* The verse-of-the-day quote on the Pray screen: secondary to the list, so it
   sits a clear step below the screen title rather than a half-pixel under it. */
.scr__quote {
  font-size: 12.5px;
  color: var(--text-muted);
  margin-bottom: 6px;
}

/* Sits directly under the progress channel, where TypingDrill puts it — not
   pinned to the bottom of the screen. */
.scr__helper {
  padding-top: 9px;
  color: var(--text-muted);
  font-size: 9.5px;
}


.scr__label {
  font-size: 8.5px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-faint);
}

/* A row of stitched ledger rows — the app's list idiom. */
.scr__row {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
}

.scr__row:last-child {
  border-bottom: 0;
}

.scr__row svg {
  color: var(--accent);
  flex: none;
}

.scr__rowtext {
  flex: 1 1 auto;
  min-width: 0;
}

.scr__rowtitle {
  color: var(--text);
  font-weight: 500;
  font-size: 10.5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.scr__rowmeta {
  color: var(--text-faint);
  font-size: 9px;
}

.scr__trail {
  color: var(--text-faint);
  font-size: 9px;
  flex: none;
}

/* ---------- the live drill passage ------------------------------------
   Reproduces TypingDrill's word states one for one, because this is a picture
   of the app rather than a diagram of the idea:

   - EVERY word carries an underline, so a blank sits flush with the words on
     either side of it and resolving a word changes only its glyphs. (iOS does
     this with textDecorationColor; the same trick works here.)
   - A blanked word that has not been reached yet: invisible glyphs, bone rule.
   - The word in hand: jade — its glyphs if visible, its rule if not.
   - A wrong keystroke: a brief danger flash on the word in hand.
   - Resolved by typing: the real word, in bone.
   - Revealed after a miss: the real word, in tension AND underlined, never
     colour alone.
*/
.scr__verse .w__body {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  text-decoration-color: currentColor;
  transition: color 170ms var(--ease), text-decoration-color 170ms var(--ease);
}

/* The drill draws its blanks with the underline above, so the diagram's
   separate rule element stands down inside a phone. */
.scr__verse .w__rule {
  display: none;
}

.scr__verse .w[data-hidden="1"] .w__body {
  color: transparent;
  text-decoration-color: var(--text);
}

.scr__verse .w[data-state="current"] .w__body,
.scr__verse .w[data-hidden="1"][data-state="current"] .w__body {
  text-decoration-color: var(--accent);
}

.scr__verse .w[data-state="current"]:not([data-hidden="1"]) .w__body {
  color: var(--accent);
}

.scr__verse .w[data-state="flash"] .w__body,
.scr__verse .w[data-hidden="1"][data-state="flash"] .w__body {
  text-decoration-color: var(--danger);
}

.scr__verse .w[data-state="flash"]:not([data-hidden="1"]) .w__body {
  color: var(--danger);
}

.scr__verse .w[data-state="missed"] .w__body {
  color: var(--tension);
  text-decoration-color: var(--tension);
}

/* Progress tracks the cursor, so it steps roughly once a word and must not
   lag a beat behind the word that caused it. */
.channel__cord--live {
  transition: transform 260ms linear;
}

/* A suggestion of the iOS keyboard. The drill IS typed, so on a real drill
   screen the keyboard is up and the passage is scrolled to keep the word in
   hand above it — a phone frame showing the drill without it would be showing a
   state the app never actually has. Decorative, so it stays out of the a11y
   tree entirely. */
.kbd {
  /* `auto` on top: the keyboard takes the screen's slack and sits on the bottom
     edge, so the passage rests against it the way it does during a real drill. */
  margin: auto -15px -22px;
  padding: 6px 2px 14px;
  background: var(--surface-alt);
  border-top: 1px solid var(--border);
}

.kbd__row {
  display: flex;
  gap: 3px;
  justify-content: center;
  padding: 2.5px 4px;
}

.kbd__k {
  flex: 1 1 0;
  min-width: 0;
  height: 25px;
  border-radius: 4px;
  background: #3b3735;
  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.55);
  display: grid;
  place-items: center;
  font-size: 10.5px;
  line-height: 1;
  color: var(--text);
}

.kbd__k--dark {
  background: #292625;
  flex: 1.4 1 0;
}

.kbd__k--space {
  flex: 4.5 1 0;
}

/* The app's real tab bar: Today, Verses, Pray, Groups, You. */
.tabs {
  margin: auto -15px -22px;
  padding: 8px 4px 15px;
  background: var(--surface-alt);
  border-top: 1px solid var(--border);
  display: flex;
  justify-content: space-around;
}

/* `textFaint` here measured 2.75:1 on the sheet surface. The active tab is
   already distinguished by being jade, so the inactive ones do not need to be
   dim as well — they can hold the body threshold and still read as inactive. */
.tabs__t {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  font-size: 7.5px;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--text-muted);
}

.tabs__t[data-on="1"] {
  color: var(--accent);
}

/* ---------- two-up: figure beside prose -------------------------------- */

.duo {
  display: grid;
  grid-template-columns: minmax(0, 340px) minmax(0, 1fr);
  gap: var(--s-4xl);
  align-items: center;
}

.duo--flip {
  grid-template-columns: minmax(0, 1fr) minmax(0, 340px);
}

.duo--flip .duo__figure {
  order: 2;
}

@media (max-width: 940px) {
  .duo,
  .duo--flip {
    grid-template-columns: 1fr;
    gap: var(--s-2xl);
  }

  .duo--flip .duo__figure {
    order: 0;
  }
}

/* ---------- the cadence ceiling diagram -------------------------------- */

.ceiling {
  position: relative;
  margin-top: var(--s-2xl);
  padding: var(--s-xl) var(--s-xl) var(--s-lg);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
}

.ceiling__grid {
  margin-top: var(--s-lg);
}

/* The plot is its own box so a stem's `height: 68%` and the ceiling line's
   `bottom: 68%` resolve against exactly the same height. With the axis labels
   inside the bars, the two disagreed by the height of a label and the ceiling
   drew below the bars it was supposed to be capping. */
.ceiling__plot {
  position: relative;
  display: flex;
  align-items: flex-end;
  gap: clamp(6px, 1.4vw, 16px);
  height: 150px;
}

.ceiling__bar {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  align-items: flex-end;
  height: 100%;
}

.ceiling__axis {
  display: flex;
  gap: clamp(6px, 1.4vw, 16px);
  margin-top: 7px;
}

.ceiling__axis span {
  flex: 1 1 0;
  min-width: 0;
  text-align: center;
  font-size: 0.72rem;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}

.ceiling__stem {
  width: 100%;
  border-radius: var(--r-sm) var(--r-sm) 2px 2px;
  background: var(--accent);
  height: var(--h, 4%);
  min-height: 3px;

  /* Grown with a transform rather than a height so eight bars animating at
     once never touch layout. Full height is the DEFAULT; only `.js` collapses
     them, so the chart is never invisible waiting on a script. */
  transform-origin: bottom center;
  transition: transform 900ms var(--ease);
  transition-delay: var(--d, 0ms);
}

.js .ceiling__stem {
  transform: scaleY(0);
}

.js .is-attached .ceiling__stem {
  transform: scaleY(1);
}

.ceiling__bar[data-capped="1"] .ceiling__stem {
  background: repeating-linear-gradient(
      -45deg,
      rgba(8, 18, 15, 0.3) 0 3px,
      transparent 3px 6px
    ),
    var(--accent);
}

/* The ceiling itself. Brass, because a verse that reaches its ceiling has been
   mastered — the one meaning this colour is allowed to carry. Placed by an
   inline `bottom` matching the capped bars' height. */
.ceiling__line {
  position: absolute;
  left: 0;
  right: 0;
  height: 0;
  border-top: 1px dashed var(--brass);
  pointer-events: none;
  z-index: 1;
}

/* Labelled at the LEFT, where the early intervals are short — at the right it
   sat on top of the very bars it annotates. */
.ceiling__line span {
  position: absolute;
  left: 0;
  top: -1.35rem;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--brass);
}

/* ---------- lapse bands ------------------------------------------------ */

.bands {
  margin-top: var(--s-2xl);
  border-top: 1px solid var(--border);
}

.bands__row {
  display: grid;
  grid-template-columns: 88px 1fr minmax(0, 128px);
  gap: var(--s-lg);
  align-items: center;
  padding: var(--s-lg) 0;
  border-bottom: 1px solid var(--border);
}

.bands__acc {
  font-family: var(--ui);
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--text);
  letter-spacing: -0.01em;
}

.bands__to {
  font-size: 0.95rem;
  color: var(--text-muted);
}

.bands__to b {
  color: var(--text);
  font-weight: 600;
}

.bands__marks {
  display: inline-flex;
  gap: 5px;
  justify-content: flex-end;
}

.bands__mark {
  width: 9px;
  height: 9px;
  transform: rotate(45deg);
  background: var(--track);
  border: 1px solid var(--border-strong);
}

.bands__mark[data-on="1"] {
  background: var(--accent);
  border-color: var(--accent);
}

@media (max-width: 620px) {
  .bands__row {
    grid-template-columns: 72px 1fr;
  }

  .bands__marks {
    display: none;
  }
}

/* ---------- library / scripture counts -------------------------------- */

.counts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 1px;
  margin-top: var(--s-2xl);
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  overflow: hidden;
}

.counts__cell {
  padding: var(--s-lg) var(--s-lg) var(--s-md);
  background: var(--surface);
}

.counts__n {
  font-family: var(--ui);
  font-size: clamp(1.5rem, 2.4vw, 2rem);
  font-weight: 700;
  letter-spacing: -0.035em;
  color: var(--text);
  line-height: 1.1;
}

.counts__what {
  margin-top: 2px;
  font-size: 0.86rem;
  color: var(--text-muted);
}

.translations {
  margin-top: var(--s-xl);
  border-top: 1px solid var(--border);
}

.translations__row {
  display: flex;
  align-items: baseline;
  gap: var(--s-md);
  padding: var(--s-md) 0;
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
}

.translations__code {
  flex: none;
  width: 52px;
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
}

.translations__name {
  flex: 1 1 180px;
  font-size: 0.96rem;
  color: var(--text);
}

.translations__meta {
  flex: none;
  font-size: 0.82rem;
  color: var(--text-faint);
}

/* ---------- habit heatmap --------------------------------------------- */

.heat {
  margin-top: var(--s-2xl);
  padding: var(--s-xl);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
}

.heat__strip {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  grid-auto-rows: 1fr;
  gap: clamp(4px, 0.9vw, 7px);
  max-width: 330px;
}

.heat__cell {
  aspect-ratio: 1;
  border-radius: 3px;
  background: var(--track);
  transition: transform 420ms var(--ease), opacity 420ms var(--ease),
    background-color 420ms var(--ease);
}

/* Same inversion: the strip is drawn by default, and only a live script hides
   it long enough to fill in. */
.js .heat__cell {
  transform: scale(0.72);
  opacity: 0;
}

.js .is-attached .heat__cell {
  transform: scale(1);
  opacity: 1;
}

.heat__cell[data-h="1"] {
  background: var(--heat1);
}

.heat__cell[data-h="2"] {
  background: var(--heat2);
}

.heat__cell[data-h="3"] {
  background: var(--heat3);
}

.heat__legend {
  display: flex;
  align-items: center;
  gap: var(--s-md);
  margin-top: var(--s-lg);
  flex-wrap: wrap;
}

.heat__key {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.8rem;
  color: var(--text-faint);
}

.heat__swatch {
  width: 10px;
  height: 10px;
  border-radius: 2px;
}

/* Habits are round dots. Stages are squares. The shapes never trade places. */
.habits {
  display: flex;
  gap: var(--s-xl);
  flex-wrap: wrap;
  margin-top: var(--s-xl);
  padding-top: var(--s-lg);
  border-top: 1px solid var(--border);
}

.habit {
  display: inline-flex;
  align-items: center;
  gap: var(--s-sm);
  font-size: 0.92rem;
  color: var(--text-muted);
}

.habit__dot {
  width: 13px;
  height: 13px;
  border-radius: 50%;
  border: 1.5px solid var(--border-strong);
  flex: none;
}

.habit__dot[data-kept="1"] {
  background: var(--accent);
  border-color: var(--accent);
}

/* ---------- group roster ---------------------------------------------- */

.roster {
  margin-top: var(--s-2xl);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: hidden;
}

.roster__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-md);
  padding: var(--s-lg) var(--s-xl);
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
}

.roster__code {
  display: inline-flex;
  align-items: center;
  gap: var(--s-sm);
  font-size: 0.8rem;
  color: var(--text-faint);
}

.roster__code b {
  font-family: var(--ui);
  font-size: 0.92rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  color: var(--accent);
}

.roster__row {
  display: grid;
  grid-template-columns: 34px minmax(0, 1fr) minmax(84px, 132px) 46px;
  gap: var(--s-md);
  align-items: center;
  padding: var(--s-md) var(--s-xl);
  border-bottom: 1px solid var(--border);
}

.roster__row:last-child {
  border-bottom: 0;
}

.avatar {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text);
  flex: none;
}

.roster__name {
  display: block;
  font-size: 0.96rem;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.roster__stage {
  display: block;
  font-size: 0.8rem;
  color: var(--text-muted);
}

.roster__pct {
  font-size: 0.82rem;
  color: var(--text-muted);
  text-align: right;
  font-variant-numeric: tabular-nums;
}

@media (max-width: 620px) {
  .roster__row {
    grid-template-columns: 30px minmax(0, 1fr) 46px;
  }

  .roster__row .channel {
    display: none;
  }
}

/* ---------- offline ledger -------------------------------------------- */

.ledger {
  margin-top: var(--s-2xl);
  border-top: 1px solid var(--border);
}

.ledger__row {
  display: grid;
  grid-template-columns: minmax(0, 260px) minmax(0, 1fr);
  gap: var(--s-lg) var(--s-2xl);
  padding: var(--s-lg) 0;
  border-bottom: 1px solid var(--border);
  align-items: baseline;
}

.ledger__what {
  display: flex;
  align-items: center;
  gap: var(--s-md);
  font-size: 1rem;
  font-weight: 600;
  color: var(--text);
  letter-spacing: -0.01em;
}

.ledger__what svg {
  color: var(--accent);
  flex: none;
}

.ledger__detail {
  font-size: 0.96rem;
  line-height: 1.6;
  color: var(--text-muted);
  max-width: 62ch;
}

@media (max-width: 720px) {
  .ledger__row {
    grid-template-columns: 1fr;
    gap: var(--s-sm);
  }
}

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

.close {
  padding-top: var(--s-5xl);
  padding-bottom: var(--s-5xl);
}

/* The mastery emblem. Five stage marks filled, wrapped in a brass ring — the
   app's own `StageDots` mastered state, reproduced exactly. */
.mastered {
  margin: 0 0 var(--s-xl);
}

.mastered__marks {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 11px 15px;
  border-radius: var(--r-pill);
  box-shadow: inset 0 0 0 1px var(--brass);
}

.mastered__mark {
  width: 11px;
  height: 11px;
  transform: rotate(45deg);
  background: var(--accent);
}

.mastered__cap {
  margin-top: var(--s-md);
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--brass);
}

/* The secondary way in, for someone who does not want to wait for launch. It is
   stitched off from the waitlist rather than sat beside it, so it reads as the
   other door and never competes with the primary action. */
.beta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-lg) var(--s-xl);
  margin-top: var(--s-4xl);
  padding-top: var(--s-xl);
  border-top: 1px solid var(--border);
  max-width: 720px;
}

.beta__cta {
  flex: none;
}

@media (max-width: 560px) {
  .beta__cta {
    width: 100%;
  }
}

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

.foot {
  position: relative;
  z-index: 2;
  border-top: 1px solid var(--border);
  padding-top: var(--s-2xl);
  padding-bottom: var(--s-3xl);
  margin-top: var(--s-2xl);
}

.foot__grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-xl) var(--s-4xl);
  justify-content: space-between;
  align-items: flex-start;
}

.foot__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-md) var(--s-xl);
  list-style: none;
  margin: 0;
  padding: 0;
}

.foot__links a {
  color: var(--text-muted);
  font-size: 0.92rem;
  text-decoration: none;
}

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

.foot__legal {
  font-size: 0.85rem;
  line-height: 1.6;
  color: var(--text-faint);
  max-width: 58ch;
}

.foot__legal a {
  color: var(--text-muted);
}

/* ---------- legal pages (privacy, terms) ------------------------------ */

/* Caps the reading measure only. The gutter and the rail offset stay with
   `.wrap` on the parent — putting a narrow max-width on the wrap itself
   re-centred the whole column and stranded the cord out in the margin. */
.doc {
  padding-top: var(--s-3xl);
  padding-bottom: var(--s-4xl);
  max-width: 74ch;
}

.doc h1 {
  font-size: clamp(2.1rem, 4.4vw, 3.1rem);
}

.doc__meta {
  margin-top: var(--s-lg);
  font-size: 0.9rem;
  color: var(--text-faint);
}

.doc h2 {
  margin-top: var(--s-3xl);
  margin-bottom: var(--s-md);
  font-size: clamp(1.25rem, 2.1vw, 1.55rem);
  font-weight: 600;
  letter-spacing: -0.022em;
  line-height: 1.2;
}

.doc h3 {
  margin-top: var(--s-xl);
  margin-bottom: var(--s-sm);
  font-size: 1.02rem;
  font-weight: 600;
  letter-spacing: -0.012em;
  color: var(--text);
}

.doc p,
.doc li {
  color: var(--text-muted);
  font-size: 1.02rem;
  line-height: 1.72;
}

/* A paragraph needs its top margin after a list or a table too, not only after
   another paragraph — without this the sentence following a <ul> reads as the
   tail of the final bullet. */
.doc p + p,
.doc ul + p,
.doc ol + p,
.doc table + p,
.doc__wrap + p {
  margin-top: var(--s-lg);
}

.doc ul,
.doc ol {
  margin: var(--s-lg) 0 0;
  padding-left: 1.35em;
}

.doc li + li {
  margin-top: var(--s-sm);
}

.doc li::marker {
  color: var(--text-faint);
}

.doc strong {
  color: var(--text);
  font-weight: 600;
}

.doc__toc {
  margin-top: var(--s-2xl);
  padding: var(--s-lg) var(--s-xl);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
}

.doc__toc ol {
  margin: var(--s-md) 0 0;
  padding-left: 1.3em;
}

.doc__toc a {
  text-decoration: none;
}

.doc__toc a:hover {
  text-decoration: underline;
}

.doc__table {
  width: 100%;
  margin-top: var(--s-lg);
  border-collapse: collapse;
  font-size: 0.95rem;
}

.doc__table th,
.doc__table td {
  text-align: left;
  vertical-align: top;
  padding: var(--s-md) var(--s-lg) var(--s-md) 0;
  border-bottom: 1px solid var(--border);
  color: var(--text-muted);
  line-height: 1.55;
}

.doc__table th {
  color: var(--text);
  font-weight: 600;
  font-size: 0.8rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.doc__wrap {
  overflow-x: auto;
}

/* An unresolved factual claim, marked so loudly it cannot ship by accident.
   Delete the whole element once the fact is confirmed. */
.todo {
  display: block;
  margin-top: var(--s-md);
  padding: var(--s-sm) var(--s-md);
  border: 1px dashed var(--tension);
  border-radius: var(--r-sm);
  background: var(--tension-soft);
  color: var(--tension);
  font-size: 0.85rem;
  font-weight: 600;
  line-height: 1.5;
}

.todo code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.95em;
  font-weight: 400;
}

.doc__note {
  margin-top: var(--s-2xl);
  padding: var(--s-lg) var(--s-xl);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  font-size: 0.95rem;
  line-height: 1.65;
  color: var(--text-muted);
}

/* ---------- reduced motion -------------------------------------------- */

/* Content is visible by default and no meaning lives only in motion, so the
   whole motion layer can be switched off without losing anything. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }

  .stitch {
    transform: scaleX(1);
  }

  .rail__cord {
    --draw: 100%;
  }

  .rail__aglet {
    opacity: 1;
  }

  .heat__cell {
    transform: scale(1);
    opacity: 1;
  }
}
