/* ══════════════════════════════════════════════════════════════════════════
   Velora, brand site layout.

   This sheet adds LAYOUT and nothing else. Every colour, radius, size, easing
   and duration below resolves to a token from the design system (`/ds`). If a
   value here looks like a raw number it is a grid, breakpoint or viewport
   measurement with no token equivalent.

   Mobile is the primary edition: single column, one thing per screen, chrome
   that floats clear of the notch and the home indicator. The wide layout is
   the same page given more room, never a second design.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Sky ink ───────────────────────────────────────────────────────────────
   The design system ships the static `.sky-*` presets but not the per-frame
   ink the Atmosphere Engine derives inside the app. These are the site's own
   approximations: each one is the preset's own bloom (or city glow) hue,
   chroma-capped and lightness-floored so it clears the scrim, exactly as the
   engine's rule describes. A colourless sky yields bone white, which is the
   truth. Delete this block the day the real engine runs here. ─────────────── */
.sky-goldenhour   { --ink: rgb(255,205,150); --ink-glow: rgba(255,205,150,0.38); --ink-soft: rgba(255,205,150,0.16); }
.sky-aurora       { --ink: rgb(150,236,205); --ink-glow: rgba(150,236,205,0.38); --ink-soft: rgba(150,236,205,0.16); }
.sky-highsun      { --ink: rgb(255,246,222); --ink-glow: rgba(255,246,222,0.38); --ink-soft: rgba(255,246,222,0.16); }
.sky-thunderstorm { --ink: rgb(178,196,232); --ink-glow: rgba(178,196,232,0.38); --ink-soft: rgba(178,196,232,0.16); }
.sky-fog          { --ink: rgb(232,236,241); --ink-glow: rgba(232,236,241,0.38); --ink-soft: rgba(232,236,241,0.16); }
.sky-blizzard     { --ink: rgb(236,242,250); --ink-glow: rgba(236,242,250,0.38); --ink-soft: rgba(236,242,250,0.16); }
.sky-neoncity     { --ink: rgb(255,190,146); --ink-glow: rgba(255,190,146,0.38); --ink-soft: rgba(255,190,146,0.16); }
.sky-desertevening{ --ink: rgb(255,198,140); --ink-glow: rgba(255,198,140,0.38); --ink-soft: rgba(255,198,140,0.16); }

/* ── Page frame ───────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }

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

/* Nothing on this page moves sideways.
   `overflow-x: hidden` alone was not enough: on a phone it still rubber-banded
   on a horizontal swipe, sliding the whole document off-centre and exposing the
   fixed sky behind it, which does not move with the page, so the seam showed.
   Three things are needed together, on BOTH elements:
     overscroll-behavior-x: none  stops the bounce at the horizontal edges
     overflow-x: clip             clips without creating a scroll container
                                  (hidden makes one, and a scroll container is
                                  exactly what rubber-bands)
     touch-action: pan-y pinch-zoom   tells the compositor sideways drags are
                                  not ours, so the gesture never starts
   The carousel opts back in to horizontal panning on itself. */
html, body {
  overscroll-behavior-x: none;
  overflow-x: clip;
  touch-action: pan-y pinch-zoom;
}

/* The atmosphere stage is fixed at z-index:-1, so this background must stay on
   <body> and off <html>. With no background on the root element, the body's own
   propagates to the canvas and paints beneath the stage; put one on <html> and
   the body background becomes an ordinary block background instead, painting
   over the sky and hiding it completely. */
body {
  margin: 0;
  background: var(--color-bg-deep);
}

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

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

/* ── The session gate ─────────────────────────────────────────────────────
   The page's one rule about who is looking at it. `data-session` is written on
   <html> before first paint (see the probe in index.html) and corrected by
   site.js once it has read the session properly; every element that belongs to
   one state or the other carries `data-auth="in"` or `data-auth="out"` and is
   shown or removed by this pair.

   Removed, not dimmed: a signed-in visitor being asked to create an account is
   the page telling them it does not know them, and there is no half-measure of
   that worth shipping.

   `!important` is deliberate and is the reason this is one rule rather than a
   condition repeated in every component. The elements it gates already carry
   their own display, `.nav-cta` is none-then-inline-flex across a breakpoint,
   `.dock` is flex-then-none, and a visibility gate that loses to a media query
   is a signed-out CTA back on screen. There is no state in which a gated
   element should win.

   Absent the attribute (no scripting, storage blocked) the page is signed out,
   which is the safe reading: the worst case is being asked to sign in again,
   never being told you are signed in when you are not. ───────────────────── */
html:not([data-session="in"]) [data-auth="in"],
html[data-session="in"] [data-auth="out"] { display: none !important; }

/* The card's two headings are focusable so that a provider round trip can land
   the reader (and a screen reader) on the answer instead of at the top of the
   document. They are tabindex="-1", so Tab never reaches them and the ring is
   only ever drawn on a focus the page moved itself, where it reads as a stray
   box around a headline. Nothing is lost: a ring's whole job is telling a
   keyboard user where they are, and no keystroke can put them here. */
[tabindex="-1"]:focus { outline: none; }

.skip-link {
  position: absolute; top: var(--space-2); left: var(--space-2);
  z-index: 100;
  display: inline-flex; align-items: center;
  min-height: var(--touch-min);
  padding: var(--space-2) var(--space-4);
  transform: translateY(-200%);
  border-radius: var(--radius-full);
  background: var(--color-bg-dark);
  border: 1px solid var(--line-soft);
  color: var(--color-text-primary);
  font-family: var(--font-mono);
  font-size: var(--text-label);
  text-transform: uppercase;
  letter-spacing: var(--track-nav);
}
.skip-link:focus-visible { transform: none; }

/* ── Type primitives ──────────────────────────────────────────────────────── */
/* Sentence case for headlines and body; UPPERCASE tracked mono for every
   label, caption and footnote. Never mono for a sentence. */
.eyebrow {
  margin: 0 0 var(--space-4);
  font-family: var(--font-mono);
  font-size: var(--guide-eyebrow);
  font-weight: 700;
  line-height: 1.4;
  text-transform: uppercase;
  letter-spacing: var(--guide-eyebrow-track);
  color: var(--color-text-muted);
}

.section-title,
.hero-title {
  margin: 0 0 var(--space-5);
  font-family: var(--font-sans);
  font-weight: 700;
  line-height: 1.04;
  letter-spacing: var(--track-tight);
  text-wrap: balance;
  color: var(--color-text-primary);
}
/* Text over a live sky takes a shadow, never a box. */
.hero-title {
  font-size: clamp(var(--guide-title), 11vw, 4rem);
  text-shadow: var(--ink-shadow-title);
}
.section-title { font-size: clamp(1.65rem, 7vw, 2.6rem); }

/* The one script word. Marck Script sits smaller and higher on the line than
   Inter at the same size, so it is scaled up and pulled back down onto the sans
   baseline, otherwise it reads as a mistake rather than a flourish. The tail of
   the final letter needs room on the right or it collides with the full stop. */
.hero-title .accent-script {
  font-size: 1.18em;
  font-weight: 400;
  letter-spacing: 0;
  padding-right: 0.08em;
  /* Keeps the taller glyphs from forcing the line box open. */
  line-height: 0.9;
}

/* One inked word per section. It migrates with the sky on the colour timeline
   rather than switching, the word is never seen changing, only found already
   belonging to the new sky. */
.inked {
  font-style: normal;
  color: var(--ink);
  text-shadow: 0 0 28px var(--ink-glow);
  transition: var(--transition-ink), text-shadow 1.5s var(--ease-atmo);
}

.hero-body,
.section-body {
  margin: 0;
  max-width: var(--measure);
  font-size: var(--guide-body);
  line-height: var(--guide-lead);
  font-weight: 300;
  color: var(--color-text-secondary);
}
@media (min-width: 700px) {
  .hero-body, .section-body { font-size: var(--text-choice); }
}

.caption {
  margin: var(--space-4) 0 0;
  max-width: var(--measure);
  font-family: var(--font-mono);
  font-size: var(--text-label-xs);
  line-height: 1.9;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: var(--track-nav);
  color: var(--color-text-faint);
}

/* ── Buttons ──────────────────────────────────────────────────────────────── */
.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--touch-min);
  padding: var(--pad-button);
  border: 0;
  border-radius: var(--radius-full);
  font-family: var(--font-mono);
  font-size: var(--text-label);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--track-button);
  text-align: center;
  cursor: pointer;
  transition: background-color var(--dur-base) ease, border-color var(--dur-base) ease,
              box-shadow var(--dur-base) ease, color var(--dur-base) ease, opacity var(--dur-base) ease;
}
.btn:disabled { opacity: 0.5; cursor: default; }

/* ── The waiting button ───────────────────────────────────────────────────
   A round trip to auth is the one moment on this page where nothing is on
   screen to look at, and a button that has only gone half-transparent reads as
   a tap that missed. The label steps back and a ring turns in its place, so the
   button says "working" rather than "unavailable".

   T3 feedback: the ring is the acknowledgment, and it is on the control that
   was pressed, not somewhere else on the page. */
.btn[data-busy="true"] > * { opacity: 0.25; }
.btn[data-busy="true"]::after {
  content: "";
  position: absolute;
  width: 15px;
  height: 15px;
  border-radius: var(--radius-full);
  border: 2px solid currentColor;
  border-top-color: transparent;
  animation: btn-spin 0.72s linear infinite;
}
@keyframes btn-spin { to { transform: rotate(360deg); } }

/* Reduce Motion: the ring would be frozen mid-arc by the design system's own
   blanket animation clamp, which reads as a broken glyph rather than as
   progress. The disabled dim and the live feedback line carry it instead. */
@media (prefers-reduced-motion: reduce) {
  .btn[data-busy="true"]::after { display: none; }
  .btn[data-busy="true"] > * { opacity: 1; }
}

/* The primary ask. Its only hover is a white halo, no lift, no hue change. */
.btn-primary { background: #fff; color: #0a0a0c; }
@media (hover: hover) { .btn-primary:not(:disabled):hover { box-shadow: var(--shadow-primary-hover); } }

.btn-quiet {
  background: var(--fill-2);
  border: 1px solid var(--line-soft);
  color: var(--color-text-primary);
  font-size: var(--text-label-sm);
  letter-spacing: var(--track-quiet);
}
@media (hover: hover) { .btn-quiet:hover { background: var(--fill-3); border-color: var(--line-strong); color: var(--color-text-primary); } }

.btn-block { width: 100%; }

/* ── Nav ──────────────────────────────────────────────────────────────────── */
/* Detached, floating, safe-area padded, the atmosphere runs under it. */
.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-3) var(--pad-screen);
  padding-top: calc(var(--safe-t) + var(--space-3));
  transition: background-color var(--dur-panel) ease, backdrop-filter var(--dur-panel) ease,
              border-color var(--dur-panel) ease;
  border-bottom: 1px solid transparent;
}
/* Only once the hero is behind it does the bar become a surface. */
.nav[data-stuck="true"] {
  background: rgba(4, 5, 9, 0.72);
  backdrop-filter: blur(var(--blur-nav)) saturate(1.4);
  -webkit-backdrop-filter: blur(var(--blur-nav)) saturate(1.4);
  border-bottom-color: var(--line-hairline);
}
@media (min-width: 700px) { .nav { padding-inline: var(--pad-screen-wide); } }

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2-5);
  min-height: var(--touch-min);
  color: var(--color-text-primary);
}
.brand:hover { color: var(--color-text-primary); }
/* The V alone, not the app-icon tile. The tile is a dusk-gradient rounded
   square, which is right on a home screen and wrong beside the wordmark, where
   it reads as an app icon pasted into a logo. Inline rather than an <img> so
   the glyph takes the wordmark's own colour: currentColor cannot cross an
   <img> boundary. */
.brand-mark {
  width: auto;
  height: 0.92em;
  flex: none;
}
.brand-word {
  font-family: var(--font-sans);
  font-size: var(--text-field);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.34em;
  /* The tracking pushes optical weight right; pull it back. */
  padding-left: 0.34em;
  text-indent: -0.02em;
}
.brand-sm .brand-word { font-size: var(--text-row-note); }

/* On a narrow phone the dock carries the ask; the bar stays a wordmark. */
.nav-cta { display: none; }
@media (min-width: 700px) { .nav-cta { display: inline-flex; } }

/* ── The account chip ───────────────────────────────────────────────────────
   What sits where the ask was, once there is a session. Persistent rather than
   a one-time confirmation: the bar is the one thing on screen at every scroll
   position, so it is where "the page knows who you are" belongs.

   Unlike the CTA it replaces, this stays on a phone. The address is what drops
   at narrow widths, not the state, an initial and a way out still fit, and
   those are the two things that matter. */
.account-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1);
  padding-right: var(--space-1);
  border-radius: var(--radius-full);
  border: 1px solid var(--line-hairline);
  background: var(--fill-1);
}

.chip-id {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2-5);
  min-width: 0;
  min-height: 36px;
  padding-right: var(--space-2);
  border-radius: var(--radius-full);
  color: var(--color-text-primary);
}

/* A dot of the sky's own light with a letter in it. Same ink as the swatch on
   the sky button and the halo behind the screens, so the signed-in state
   belongs to whatever weather is overhead rather than introducing a colour. */
.account-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  flex: none;
  border-radius: var(--radius-full);
  border: 1px solid var(--line-soft);
  background: var(--ink-soft);
  box-shadow: 0 0 14px var(--ink-glow);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: var(--text-label-sm);
  font-weight: 700;
  line-height: 1;
  text-transform: uppercase;
  transition: var(--transition-ink);
}

.chip-lines {
  display: none;
  min-width: 0;
  flex-direction: column;
  gap: 1px;
}
@media (min-width: 700px) { .chip-lines { display: flex; } }

.chip-label {
  font-family: var(--font-mono);
  font-size: var(--text-label-2xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--track-nav);
  color: var(--color-text-faint);
}

/* One line, always. An address is arbitrarily long and the bar is not. */
.chip-email {
  max-width: 22ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--font-sans);
  font-size: var(--text-row-note);
  font-weight: 500;
  color: var(--color-text-secondary);
}

.chip-signout {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 36px;
  padding: 0 var(--space-3-5);
  border: 0;
  border-radius: var(--radius-full);
  background: var(--fill-2);
  color: var(--color-text-muted);
  font-family: var(--font-mono);
  font-size: var(--text-label-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--track-nav);
  white-space: nowrap;
  cursor: pointer;
  transition: background-color var(--dur-base) ease, color var(--dur-base) ease;
}
@media (hover: hover) {
  .chip-id:hover .chip-email { color: var(--color-text-primary); }
  .chip-signout:hover { background: var(--fill-3); color: var(--color-text-primary); }
}

/* ── Hero ─────────────────────────────────────────────────────────────────── */
.hero {
  position: relative;
  display: flex;
  align-items: flex-end;
  /* Fills the phone minus the bar it sits under, never more than the glass. */
  min-height: 82svh;
  padding: var(--space-10) var(--pad-screen);
  /* The hero runs UP under the transparent bar, so its scrim covers the top of
     the screen too. Without this the scrim stops at the bar's bottom edge and
     the raw sky above it reads as a lighter block across the header.
     padding-top pays back the pull-up (72px) and then clears the bar. */
  margin-top: calc(-1 * (var(--safe-t) + 72px));
  padding-top: calc(var(--safe-t) + 164px);
  overflow: hidden;
}
@media (min-width: 700px) {
  .hero {
    align-items: center;
    min-height: 88svh;
    padding-inline: var(--pad-screen-wide);
  }
}

/* The hero's own scrim, this is what buys legibility, not a page-wide dimmer.
   Six stops, weighted where the chrome and copy sit and open through the middle,
   so there is always a window of untouched sky. On a wide screen it is also
   weighted left, because the copy is a column in the corner of the weather and
   the light source is usually not. */
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(180deg,
    rgba(4, 6, 12, 0.78) 0%,
    rgba(4, 6, 12, 0.34) 22%,
    rgba(4, 6, 12, 0.40) 62%,
    rgba(4, 6, 12, 0.30) 88%,
    rgba(4, 6, 12, 0.14) 100%);
}
/* Below the copy the scrim LIFTS rather than deepens. The engine paints the
   horizon colour at the bottom of the dome, the orange band over Wadi Rum, the
   city glow over Tromsø, which is the best thing in the frame and sits exactly
   where a conventional bottom-heavy scrim would bury it. */
@media (min-width: 900px) {
  .hero::before {
    background:
      linear-gradient(180deg,
        rgba(4, 6, 12, 0.58) 0%,
        rgba(4, 6, 12, 0.08) 20%,
        transparent 54%,
        transparent 78%,
        rgba(4, 6, 12, 0.10) 100%),
      linear-gradient(90deg,
        rgba(4, 6, 12, 0.74) 0%,
        rgba(4, 6, 12, 0.42) 40%,
        transparent 68%);
  }
}

.hero-inner {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: 1080px;
  margin: 0 auto;
}

/* ── The fallback sky ──────────────────────────────────────────────────────
   What a visitor sees when /atmo/atmo.js never loads or never runs: the design
   system's static .sky-* presets, cross-faded. It occupies exactly the same
   place as the real stage, fixed, full-viewport, behind the document, so the
   page looks the same either way and the engine's arrival is a change of
   fidelity, not of layout. atmo.ts removes this element once its own stage is
   mounted, never before, so there is no frame without a sky.

   Deliberately NOT called .atmo-stage: that class belongs to the engine, and
   two different definitions of it fighting over position is a bug waiting to
   happen. */
.atmo-fallback {
  position: fixed;
  inset: 0;
  height: 100lvh;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
}

/* Each preset is a full stage of its own; only opacity cross-fades, so the
   layers underneath keep breathing through the change (T0 ambient). */
.atmo-layer {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 3.6s var(--ease-atmo);
  will-change: opacity;
}

/* Weighted top and bottom, where the bar and the hero copy sit. Deepens only
   under a bright sky, a night preset needs almost none of it. (The live engine
   drives .atmo-brand-veil from the frame's own illumination instead; this is
   the fallback's coarse equivalent.) */
.atmo-legibility { opacity: 0.58; }
[data-lum="bright"] .atmo-legibility { opacity: 0.88; }

/* The after-dark band flies its own sky locally, over the page's. */
.band-sky {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}

/* ── The scrim floor ──────────────────────────────────────────────────────
   Everything below the hero. The sky stays behind the whole document, but
   long-form copy is read on ground, not against moving rain, so the page
   closes over it after the first screen and rises out of it at the seam. */
.ground {
  position: relative;
  background: var(--color-bg-deep);
}
/* Short on purpose: long enough that the floor rises out of the sky rather than
   cutting it, short enough that it does not eat the horizon band above it. */
.ground::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 100%;
  height: 12svh;
  background: linear-gradient(180deg, transparent, var(--color-bg-deep));
  pointer-events: none;
}

/* The two providers side by side, email under them. On a phone they stack full
   width, which is what every sign-in sheet on the platform does. */
.hero-actions {
  display: grid;
  gap: var(--space-2-5);
  margin-top: var(--space-8);
  max-width: var(--measure);
}
@media (min-width: 620px) {
  .hero-actions {
    grid-template-columns: 1fr 1fr;
    max-width: 560px;
  }
  /* Email spans both, quietly, under the two that people actually tap. */
  .hero-actions .btn-quiet { grid-column: 1 / -1; }
}

/* ── The hero, answered ─────────────────────────────────────────────────────
   Occupies the space the three auth buttons had. No card, no border: the hero
   copy is set straight on the sky with a shadow rather than a box, and an
   acknowledgment that arrived in a panel would read as a notification about the
   page instead of as part of it. */
.hero-welcome {
  margin-top: var(--space-8);
  max-width: var(--measure);
}

.welcome-line {
  display: flex;
  align-items: center;
  gap: var(--space-2-5);
  margin: 0;
  font-size: var(--text-choice);
  font-weight: 300;
  color: var(--color-text-secondary);
  text-shadow: var(--ink-shadow-window);
}

/* The one mark on the page that says a thing succeeded. It takes the sky's ink
   rather than a semantic green: this is not a status, it is the page
   recognising someone. */
.welcome-tick {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  flex: none;
  border-radius: var(--radius-full);
  background: var(--ink-soft);
  box-shadow: 0 0 16px var(--ink-glow);
  color: var(--ink);
  transition: var(--transition-ink);
}
.welcome-tick svg { width: 13px; height: 13px; }

.welcome-email {
  font-weight: 600;
  color: var(--color-text-primary);
  /* An address is one token; it wraps as one or it overflows the hero. */
  overflow-wrap: anywhere;
}

.welcome-note {
  margin: var(--space-3) 0 0;
  max-width: var(--measure);
  font-size: var(--guide-body);
  line-height: var(--guide-lead);
  font-weight: 300;
  color: var(--color-text-muted);
  text-shadow: var(--ink-shadow-window);
}

.welcome-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2-5);
  margin-top: var(--space-6);
}

/* ── The sky control ────────────────────────────────────────────────────────
   One button, not a row of eight toggles. The toggles were a control panel for
   a thing nobody asked to control; this names the sky overhead and offers
   another. It reads as a status line that happens to be tappable. */
.sky-control { margin-top: var(--space-10); }

.sky-button {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  min-height: var(--touch-min);
  padding: 0 var(--space-5) 0 var(--space-4);
  border-radius: var(--radius-full);
  border: 1px solid var(--line-hairline);
  background: var(--fill-1);
  color: var(--color-text-secondary);
  font-family: var(--font-mono);
  font-size: var(--text-label-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--track-nav);
  cursor: pointer;
  transition: var(--transition-ink), border-color var(--dur-base) ease,
              background-color var(--dur-base) ease;
}
@media (hover: hover) {
  .sky-button:hover { background: var(--fill-2); border-color: var(--line-soft); color: var(--color-text-primary); }
  .sky-button:hover .sky-turn { transform: rotate(-90deg); }
}

/* A dot of the sky's own light, so the button carries the current scene's
   colour without printing a hex anywhere. */
.sky-swatch {
  width: 7px;
  height: 7px;
  flex: none;
  border-radius: var(--radius-full);
  background: var(--ink);
  box-shadow: 0 0 12px var(--ink-glow);
  transition: var(--transition-ink);
}

.sky-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--ink);
  transition: var(--transition-ink);
}

.sky-turn {
  width: 13px;
  height: 13px;
  flex: none;
  opacity: 0.5;
  transition: transform var(--dur-panel) var(--ease-travel), opacity var(--dur-base) ease;
}
.sky-button:active .sky-turn { transform: rotate(-180deg); }

/* Two captions, one truth each, see index.html. The live one only exists once
   the engine has said so on the root element. */
[data-caption-live] { display: none; }
html[data-atmo="live"] [data-caption-live] { display: block; }
html[data-atmo="live"] [data-caption-static] { display: none; }

/* ── Bands ──────────────────────────────────────────────────────────────────
   No top hairline. A rule between every section is scaffolding showing through;
   the space is the separator. */
.band {
  position: relative;
  padding: var(--space-10) var(--pad-screen) 88px;
}
@media (min-width: 700px) {
  .band { padding: 96px var(--pad-screen-wide) 140px; }
}

.band-inner {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: 1080px;
  margin: 0 auto;
}
.band-inner-narrow { max-width: 720px; }


/* ── The tour ───────────────────────────────────────────────────────────────
   A paged deck of screens, one at a time, phone and claim together.

   Native scroll-snap does the work: the swipe, the momentum, the rubber-band at
   the ends, keyboard arrows and the reduced-motion behaviour are all the
   platform's. The script only lights the right dot. That is why the dots are
   real anchors, with no script at all this is still a horizontal scroller with
   a working table of contents.

   `touch-action` is re-enabled here specifically, because the document turns
   horizontal panning off globally to stop the page rubber-banding sideways. */
.tour {
  position: relative;
  /* The dock is fixed over the bottom of the viewport by the time anyone is
     reading this section, and the dots live at the bottom of it. --dock-h is
     measured and published by site.js. */
  padding: 72px 0 calc(var(--space-8) + var(--dock-h, 0px));
}
@media (min-width: 900px) { .tour { padding: 110px 0 var(--space-10); } }

.deck {
  display: flex;
  margin: 0;
  padding: 0;
  list-style: none;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  touch-action: pan-x pan-y pinch-zoom;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  /* The deck itself is focusable, so arrow keys page it. */
  outline-offset: -2px;
}
.deck::-webkit-scrollbar { display: none; }

.slide {
  flex: 0 0 100%;
  scroll-snap-align: center;
  scroll-snap-stop: always;
  /* Off-centre slides sit back rather than sliding past at full strength, so a
     swipe reads as a crossfade between two screens instead of a whip. */
  opacity: 0.28;
  transition: opacity var(--dur-slow) var(--ease-travel);
  display: grid;
  justify-items: center;
  align-content: start;
  gap: var(--space-6);
  padding: 0 var(--pad-screen);
}
@media (min-width: 900px) {
  .slide {
    grid-template-columns: minmax(0, 420px) minmax(0, 460px);
    justify-content: center;
    align-items: center;
    align-content: center;
    gap: 72px;
    padding: 0 var(--pad-screen-wide);
  }
}

.slide.is-current { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .slide { opacity: 1; transition: none; }
}
/* Set while a dot jump is in flight: the deck dips out, moves with no
   animation, and comes back, so a tap never drags four screens past the eye. */
.deck[data-jumping="true"] { opacity: 0; transition: opacity 0.16s ease; }
.deck { transition: opacity 0.26s ease; }

.slide-copy { max-width: var(--measure); text-align: center; }
@media (min-width: 900px) { .slide-copy { text-align: left; } }

.slide-title {
  margin: 0 0 var(--space-4);
  font-family: var(--font-sans);
  font-size: clamp(1.5rem, 6.6vw, 2rem);
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: var(--track-tight);
  text-wrap: balance;
  color: var(--color-text-primary);
}
@media (min-width: 900px) { .slide-title { font-size: clamp(1.9rem, 2.8vw, 2.7rem); } }

.slide-body {
  margin: 0;
  font-size: var(--guide-body);
  line-height: var(--guide-lead);
  font-weight: 300;
  color: var(--color-text-secondary);
}
@media (min-width: 900px) { .slide-body { font-size: var(--text-choice); } }

/* ── Dots ───────────────────────────────────────────────────────────────────
   Tap targets are the full 44px; the mark inside is 7px. Padding does the
   work so the row still reads as five small dots. */
.dots {
  display: flex;
  justify-content: center;
  gap: var(--space-1);
  margin-top: var(--space-8);
}
.dots a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--touch-min);
  height: var(--touch-min);
  border-radius: var(--radius-full);
}
.dots a::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: var(--radius-full);
  background: var(--fill-4);
  transition: background-color var(--dur-base) ease, transform var(--dur-base) var(--ease-travel),
              box-shadow var(--dur-base) ease;
}
.dots a[aria-current="true"]::before {
  background: var(--ink);
  transform: scale(1.25);
  box-shadow: 0 0 14px var(--ink-glow);
}
@media (hover: hover) { .dots a:hover::before { background: var(--color-text-muted); } }

/* ── The screens ────────────────────────────────────────────────────────────
   The mockups carry their own device chassis, so there is no frame here to draw, adding a second one around a photographed phone is the tell of a template.
   All this does is float the screen and put the sky's own ink behind it.

   Sized to leave room for the claim beneath it on a phone: both have to be on
   screen at once, which is the whole point of the deck. */
.shot {
  position: relative;
  margin: 0;
  width: 100%;
  max-width: min(232px, 56vw);
}
@media (min-width: 560px) { .shot { max-width: 260px; } }
@media (min-width: 900px) { .shot { max-width: 340px; justify-self: end; } }

/* Ink halo. Follows the live sky on the colour timeline like everything else. */
.shot::before {
  content: "";
  position: absolute;
  inset: 4% 0 6%;
  z-index: -1;
  border-radius: 50%;
  background: radial-gradient(ellipse at 50% 45%, var(--ink-soft), transparent 68%);
  filter: blur(38px);
  transition: var(--transition-ink);
  pointer-events: none;
}

.shot img {
  width: 100%;
  height: auto;
  aspect-ratio: 978 / 2000;
  object-fit: contain;
  /* A phone casts a shadow; it does not sit in a lit box. */
  filter: drop-shadow(0 24px 52px rgba(2, 4, 10, 0.62));
}

/* A screen that did not arrive. site.js sets this the moment an image fails, so
   the slide keeps its shape instead of dropping the browser's broken-image
   glyph into the deck. Quiet on purpose: "not yet", not an error. */
.shot[data-pending="true"] img { display: none; }
.shot[data-pending="true"]::after {
  content: "Screen coming soon";
  display: flex;
  align-items: flex-end;
  justify-content: center;
  aspect-ratio: 978 / 2000;
  padding: var(--space-6) var(--space-4);
  border: 1px solid var(--line-hairline);
  border-radius: 38px;
  background: linear-gradient(180deg, var(--fill-2), var(--fill-1));
  font-family: var(--font-mono);
  font-size: var(--text-label-xs);
  text-transform: uppercase;
  letter-spacing: var(--track-nav);
  color: var(--color-text-faint);
  text-align: center;
}

/* ── Account card ─────────────────────────────────────────────────────────── */
.join-card {
  position: relative;
  overflow: hidden;
  padding: var(--space-8) var(--space-5);
  border-radius: var(--radius-xl);
}
@media (min-width: 700px) { .join-card { padding: var(--space-10) var(--pad-screen-wide); } }

.join-hairline { position: absolute; top: 0; left: 0; right: 0; height: 1px; }

.join-form { margin-top: var(--space-8); max-width: var(--measure); }

.btn-oauth {
  padding: var(--pad-field);
  background: var(--fill-2);
  border: 1px solid var(--line-soft);
  color: var(--color-text-primary);
  font-size: var(--text-label-sm);
  letter-spacing: var(--track-quiet);
}
@media (hover: hover) { .btn-oauth:not(:disabled):hover { background: var(--fill-3); border-color: var(--line-strong); } }
.oauth-mark { display: inline-flex; }
.oauth-mark svg { width: 15px; height: 15px; }

/* The card's own copy of the two providers. Stacked on a phone, side by side
   once there is room, same as the hero's. */
.provider-row { display: grid; gap: var(--space-2-5); }
@media (min-width: 620px) { .provider-row { grid-template-columns: 1fr 1fr; } }

/* A hairline with a word sitting in it, rather than a full-width rule and a
   floating "or" that has to be positioned over it. */
.rule-or {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin: var(--space-6) 0;
  font-family: var(--font-mono);
  font-size: var(--text-label-xs);
  text-transform: uppercase;
  letter-spacing: var(--track-nav);
  color: var(--color-text-faint);
}
.rule-or::before,
.rule-or::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--line-hairline);
}

.field { margin-bottom: var(--space-3); }
.field-label {
  display: block;
  margin-bottom: var(--space-1-5);
  font-family: var(--font-mono);
  font-size: var(--text-label-xs);
  text-transform: uppercase;
  letter-spacing: var(--track-nav);
  color: var(--color-text-muted);
}

/* Focus lifts the edge rather than ringing it. */
.cta-input {
  width: 100%;
  min-height: var(--touch-min);
  padding: var(--pad-field);
  border-radius: var(--radius-lg);
  border: 1px solid var(--line-soft);
  background: rgba(255, 255, 255, 0.06);
  color: var(--color-text-primary);
  font-family: var(--font-sans);
  font-size: var(--text-field);
  outline: none;
  transition: border-color var(--dur-base) ease, background-color var(--dur-base) ease;
}
.cta-input::placeholder { color: var(--color-text-faint); }
.cta-input:focus { background: rgba(255, 255, 255, 0.09); border-color: var(--line-strong); }
.cta-input[aria-invalid="true"] { border-color: var(--color-negative); }

/* ── A field with a control in it ───────────────────────────────────────────
   The reveal is a real 44px button overlaid on the field's right edge, and the
   input is padded out of its way rather than the button being floated over
   live text. Anything tappable on this page clears the touch floor, including
   the ones that look like icons. */
.field-with-action { position: relative; }
.field-with-action .cta-input { padding-right: 52px; }

.field-action {
  position: absolute;
  top: 50%;
  right: var(--space-1);
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--touch-min);
  height: var(--touch-min);
  padding: 0;
  border: 0;
  border-radius: var(--radius-full);
  background: transparent;
  color: var(--color-text-faint);
  cursor: pointer;
  transition: color var(--dur-base) ease, background-color var(--dur-base) ease;
}
@media (hover: hover) { .field-action:hover { color: var(--color-text-secondary); background: var(--fill-1); } }
.field-action svg { width: 17px; height: 17px; }

/* One icon on screen at a time: an open eye while the password is masked (tap
   to show), a struck one while it is legible (tap to hide). aria-pressed is
   what the assistive layer reads; this is the same fact, drawn. */
.field-action .eye-hidden { display: none; }
.field-action[aria-pressed="true"] .eye-shown { display: none; }
.field-action[aria-pressed="true"] .eye-hidden { display: block; }
.field-action[aria-pressed="true"] { color: var(--color-text-secondary); }

.join-form .btn-primary { margin-top: var(--space-4); }

/* ── The mode switch ────────────────────────────────────────────────────────
   The way out of the wrong half of the card. Quiet, and a real button rather
   than a second pill: it changes what this card is, it is not a third thing to
   consider alongside Google and Apple. */
.mode-switch {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
  margin: var(--space-5) 0 0;
  font-family: var(--font-mono);
  font-size: var(--text-label-xs);
  text-transform: uppercase;
  letter-spacing: var(--track-nav);
  color: var(--color-text-faint);
}

.link-button {
  padding: 0;
  border: 0;
  background: none;
  color: var(--ink);
  font: inherit;
  text-transform: inherit;
  letter-spacing: inherit;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
  transition: var(--transition-ink), opacity var(--dur-base) ease;
}
@media (hover: hover) { .link-button:hover { opacity: 0.75; } }

/* ── The card, answered ─────────────────────────────────────────────────────
   The signed-in face. One row that names the account, and the one action left. */
.account-panel {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin: var(--space-8) 0 var(--space-6);
  padding: var(--pad-row);
  border-radius: var(--radius-lg);
  border: 1px solid var(--line-hairline);
  background: var(--fill-1);
}

.account-avatar-lg {
  width: 40px;
  height: 40px;
  font-size: var(--text-label);
}

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

.account-panel-email {
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: var(--text-choice);
  font-weight: 500;
  color: var(--color-text-primary);
  overflow-wrap: anywhere;
}

.account-panel-meta {
  font-family: var(--font-mono);
  font-size: var(--text-label-xs);
  text-transform: uppercase;
  letter-spacing: var(--track-nav);
  color: var(--color-text-faint);
}
/* Empty until site.js knows the date; an empty line still reserves its height
   and leaves a gap under the address that reads as a missing word. */
.account-panel-meta:empty { display: none; }

.feedback {
  display: block;
  margin: var(--space-4) 0 0;
  font-family: var(--font-mono);
  font-size: var(--text-label-xs);
  line-height: 1.7;
  text-transform: uppercase;
  letter-spacing: 0.14em;
}
.feedback[data-tone="error"] { color: var(--color-negative); }
.feedback[data-tone="ok"]    { color: var(--color-positive-soft); }
.feedback[data-tone="busy"]  { color: var(--color-text-muted); }
/* Neither a failure nor a success: the card redirecting someone to the half of
   itself that can actually help them. Red would call it their mistake. */
.feedback[data-tone="note"]  { color: var(--color-text-secondary); }

.join-footnote { margin-top: var(--space-6); }

/* ── Footer ───────────────────────────────────────────────────────────────── */
.footer {
  padding: var(--space-8) var(--pad-screen) var(--space-10);
  /* Clears the dock on a phone so the last link is never under it. */
  padding-bottom: calc(var(--space-10) + var(--dock-h, 0px));
  border-top: 1px solid var(--line-hairline);
  background: var(--color-bg-dark);
}
@media (min-width: 700px) { .footer { padding-inline: var(--pad-screen-wide); } }

.footer-inner {
  width: 100%;
  max-width: 1080px;
  margin: 0 auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4) var(--space-6);
}

.footer-links {
  display: flex;
  gap: var(--space-5);
  margin: 0;
  padding: 0;
  list-style: none;
  font-family: var(--font-mono);
  font-size: var(--text-label-xs);
  text-transform: uppercase;
  letter-spacing: var(--track-nav);
}
/* Short words, so the target is grown rather than the label, 44px on both
   edges, not just the tall one. */
.footer-links a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--touch-min);
  min-height: var(--touch-min);
  color: var(--color-text-muted);
}
.footer-links a:hover { color: var(--color-text-primary); }

.footer-note {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--text-label-xs);
  text-transform: uppercase;
  letter-spacing: var(--track-nav);
  color: var(--color-text-faint);
}
@media (min-width: 860px) { .footer-links { margin-left: auto; } }

/* ── Dock (mobile) ────────────────────────────────────────────────────────── */
/* Detached pill above the home indicator. Hidden while the account card it
   points at is on screen, chrome never covers its own destination. */
/* A pill, not a bar. Full width it sat across the bottom of every screen and
   covered whatever was behind it; centred and sized to its label it reads as
   chrome floating over the page. Still 44px on its short edge. */
.dock {
  position: fixed;
  left: var(--space-4);
  right: var(--space-4);
  bottom: 0;
  z-index: 40;
  display: flex;
  justify-content: center;
  padding-bottom: calc(var(--safe-b) + var(--space-3));
  transform: translateY(0);
  transition: transform var(--dur-panel) var(--ease-travel), opacity var(--dur-base) ease;
}
.dock .btn {
  width: auto;
  min-height: var(--touch-min);
  padding: 0 var(--space-6);
  font-size: var(--text-label-sm);
  letter-spacing: var(--track-quiet);
}
.dock[data-away="true"] { transform: translateY(140%); opacity: 0; pointer-events: none; }
@media (min-width: 700px) { .dock { display: none; } }

/* ── Toasts ─────────────────────────────────────────────────────────────────
   Acknowledgment that finds the reader. The feedback line inside the card is
   the record of what happened and it stays there, but a provider round trip
   returns to whatever scroll position the visitor left from, and a confirmation
   nobody is looking at is not a confirmation.

   Below the bar rather than above the home indicator: the dock already owns the
   bottom of a phone, and stacking a message on top of a CTA is how you get one
   covering the other. Announced by the region's own aria-live, so the message
   is spoken once when it is inserted. */
.toasts {
  position: fixed;
  top: calc(var(--safe-t) + 72px);
  left: var(--space-4);
  right: var(--space-4);
  z-index: 60;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  pointer-events: none;
}

.toast {
  display: flex;
  align-items: center;
  gap: var(--space-2-5);
  margin: 0;
  max-width: min(420px, 100%);
  padding: var(--space-3) var(--space-5);
  border-radius: var(--radius-full);
  border: 1px solid var(--line-soft);
  background: rgba(9, 10, 15, 0.86);
  backdrop-filter: blur(var(--blur-panel)) saturate(1.4);
  -webkit-backdrop-filter: blur(var(--blur-panel)) saturate(1.4);
  box-shadow: var(--shadow-lift), var(--edge-lit-soft);
  font-family: var(--font-mono);
  font-size: var(--text-label-xs);
  line-height: 1.6;
  text-transform: uppercase;
  letter-spacing: var(--track-nav);
  color: var(--color-text-secondary);
  text-align: center;
  /* T2 in, and out faster than in, exits always are. */
  animation: toast-in var(--dur-panel) var(--ease-travel) both;
}
.toast[data-leaving="true"] { animation: toast-out var(--dur-fast) var(--ease-out) both; }

/* A dot of the outcome's colour. The message says what happened; this is what
   makes it readable as good or bad before it is read. */
.toast::before {
  content: "";
  width: 6px;
  height: 6px;
  flex: none;
  border-radius: var(--radius-full);
  background: var(--color-text-faint);
}
.toast[data-tone="ok"]::before    { background: var(--color-positive-soft); box-shadow: 0 0 10px rgba(52, 211, 153, 0.5); }
.toast[data-tone="error"]::before { background: var(--color-negative); box-shadow: 0 0 10px rgba(248, 113, 113, 0.5); }

@keyframes toast-in  { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: none; } }
@keyframes toast-out { from { opacity: 1; } to { opacity: 0; transform: translateY(-6px); } }

/* ── Arrival (T2 content, staggered 35ms) ─────────────────────────────────── */
/* Gated on .js: without scripting every section is simply present. Content is
   never hidden by a stylesheet that is waiting on an observer to fire. */
.js [data-reveal] {
  opacity: 0;
  transform: translateY(10px) translateZ(0);
  transition: opacity var(--dur-reveal) var(--ease-travel), transform var(--dur-reveal) var(--ease-travel);
  transition-delay: var(--reveal-delay, 0ms);
  will-change: opacity, transform;
}
.js [data-reveal].is-in { opacity: 1; transform: none; }

/* Reduce Motion collapses ambient and environment motion; nothing is hidden,
   and tap acknowledgment survives. */
@media (prefers-reduced-motion: reduce) {
  .js [data-reveal] { opacity: 1; transform: none; transition: none; }
  .atmo-layer { transition: none; }
  .dock { transition: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   THE EVIDENCE SECTIONS
   Corpus · Fit Score · The alternative · The cards

   These sit below the tour and they are the first thing on this page with
   real figures in it. The README records that a card grid and a set of
   invented fit scores were cut from roughly this spot; both objections were
   about substance, not about shape, and the shape rule they left behind still
   holds here. Nothing below is boxed. Every rule is a hairline BETWEEN rows,
   the way a statement rules its columns, and no element carries a border on
   all four sides.

   Numbers are mono with tabular figures throughout, so a column of them lines
   up and a value that changes under a live update does not reflow the line it
   sits in.

   TYPE SCALE. These sections carry far more text than anything above them, and
   the first pass set it in the sizes the rest of the page uses for CHROME, --text-detail for prose, --text-label-xs for labels. Those are correct for a
   caption nobody has to read and wrong for a paragraph making an argument: at
   8px a mono label is decoration, not information. Everything here resolves to
   the three below instead, which step up together on a wider screen.

   Body copy inside these sections deliberately sits ABOVE the page's own
   --guide-body. The reading distance is the same; the density is not. ────── */
:where(#database, #fit, #versus, #cards) {
  --ev-body: 14.5px;    /* prose inside a row, cell or column */
  --ev-label: 11px;     /* a mono label that carries meaning, not chrome */
  --ev-note: 12.5px;    /* citations, sources, the small print */
}
@media (min-width: 700px) {
  :where(#database, #fit, #versus, #cards) {
    --ev-body: 15.5px;
    --ev-label: 11.5px;
    --ev-note: 13px;
  }
}
/* The section intro reads at the same size as the rows beneath it. It was a
   step larger than its own evidence, which made the argument look like the
   caption and the evidence look like the footnote. */
:where(#database, #fit, #versus, #cards) .section-body {
  font-size: var(--ev-body);
  line-height: 1.7;
  max-width: 62ch;
}
/* Captions in these sections carry qualifications that matter, what the
   exhibit is and is not, what has never been audited. The page's own .caption
   is 8px tracked mono, which is right for chrome nobody has to read and wrong
   for a disclaimer that is doing real work. */
/* ── Band rhythm in the evidence sections ──────────────────────────────────
   The page's own .band is spaced for one claim per screen, which is right for
   the hero and wrong for four sections of argument: at 96/140 the reader was
   scrolling past empty space to reach the next fact, and no section fit a
   screen. Tightened so each section can be taken in roughly at once, which is
   the whole point of them.

   The separator is still space, just less of it, and the sections still have
   nothing between them. */
.band-tight { padding: var(--space-8) var(--pad-screen) var(--space-10); }
@media (min-width: 700px) {
  .band-tight { padding: 64px var(--pad-screen-wide) 72px; }
}
/* Same reason: the sub-heads inside a section were spaced like new sections. */
.band-tight .sub-title { margin-top: 56px; }
.band-tight .section-title { margin-bottom: var(--space-4); }

:where(#database, #fit, #versus, #cards) .caption {
  font-size: var(--ev-note);
  line-height: 1.75;
  letter-spacing: 0.06em;
  max-width: 70ch;
  color: var(--color-text-muted);
}

/* A sub-head inside a band, one step under .section-title. Sans, because it is
   a sentence; the mono eyebrow above it is the label. */
.sub-title {
  margin: 72px 0 var(--space-4);
  font-family: var(--font-sans);
  font-size: clamp(1.15rem, 4vw, 1.5rem);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: var(--track-tight);
  text-wrap: balance;
  color: var(--color-text-primary);
}

/* An identifier quoted inside a sentence. Not a code block, just the one word
   that is literally a column name. */
.code {
  font-family: var(--font-mono);
  font-size: 0.92em;
  color: var(--color-text-primary);
}

/* Superscript reference into the sources list at the foot of #versus. */
.cite-mark {
  font-family: var(--font-mono);
  font-size: 0.8em;
  vertical-align: super;
  line-height: 0;
  color: var(--color-text-faint);
}

/* ── The tally ──────────────────────────────────────────────────────────────
   Everything Velora knows, in one figure, at a size nothing else on the page
   competes with. It is the sum of the parts underneath it, so it is allowed to
   be the loudest thing here: a reader who takes only this away has taken away
   something true.

   Ruled top and bottom rather than boxed, like every other exhibit in these
   sections. The number is the sky's own ink with a wider bloom than the ledger
   figures get, which is the whole hierarchy: one figure glows, the rest state.

   Sized off the viewport, not off a fixed step, because its job is to fill the
   line it is on at any width. Capped at 6.5rem so it does not become a poster
   on a desktop monitor. */
.tally {
  margin: var(--space-8) 0 0;
  padding: var(--space-6) 0 var(--space-8);
  border-top: 1px solid var(--line-hairline);
  border-bottom: 1px solid var(--line-hairline);
}

.tally-n {
  display: block;
  font-family: var(--font-mono);
  font-size: clamp(3.2rem, 17vw, 6.5rem);
  font-weight: 700;
  line-height: 0.95;
  font-variant-numeric: tabular-nums;
  /* Tightened as it grows: at this size the default tracking opens gaps a
     five-figure number reads as two numbers across. */
  letter-spacing: -0.035em;
  color: var(--ink);
  text-shadow: 0 0 60px var(--ink-glow);
  transition: var(--transition-ink), text-shadow 1.5s var(--ease-atmo);
}

.tally-k {
  margin: var(--space-3) 0 0;
  font-family: var(--font-mono);
  font-size: var(--ev-label);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--track-nav);
  color: var(--color-text-primary);
}

.tally-note {
  margin: var(--space-4) 0 0;
  max-width: 56ch;
  font-size: var(--ev-body);
  line-height: 1.7;
  font-weight: 300;
  color: var(--color-text-muted);
}

/* ── A half ─────────────────────────────────────────────────────────────────
   The tally arrives in two of these, and each one leads with its own subtotal
   so the arithmetic is legible without the page ever printing a sum sign. The
   subtotal sits at half the tally's size and above the figures it is made of,
   which is the same relationship one level down.

   Stacked on a phone; from 760px the figure sits to the left of the sentence
   explaining it, so the eye can run down the two subtotals in a column and
   still read either one across. */
/* 56px is the same standing start `.band-tight` gives a sub-head, which is
   what each of these is: a new heading inside a section, not a new section. */
.tier { margin: 56px 0 0; }

.tier-head {
  display: grid;
  gap: var(--space-3);
  align-items: baseline;
}
/* Wide enough for five figures and a separator at the size below, so the
   sentence beside it never has to reflow around a number that grew. */
@media (min-width: 760px) {
  .tier-head { grid-template-columns: minmax(0, 13rem) minmax(0, 1fr); gap: var(--space-6); }
}

/* ONE STEP UNDER THE TALLY AND ONE STEP OVER ITS PARTS, in size and in ink.
   The first pass set this smaller than the ledger figures underneath it, so a
   subtotal was quieter than the numbers it was the sum of, and the section read
   as four unrelated scales. The rule now is plain: a total glows and is larger
   than anything it contains. */
.tier-n {
  font-family: var(--font-mono);
  font-size: clamp(2.4rem, 9vw, 3.4rem);
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.025em;
  color: var(--ink);
  text-shadow: 0 0 40px var(--ink-glow);
  transition: var(--transition-ink), text-shadow 1.5s var(--ease-atmo);
}

.tier-k {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--ev-label);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--track-nav);
  color: var(--color-text-primary);
}

.tier-note {
  margin: var(--space-2-5) 0 0;
  max-width: 58ch;
  font-size: var(--ev-body);
  line-height: 1.7;
  font-weight: 300;
  color: var(--color-text-muted);
}

/* Inside a half the ledger is already the next thing down, so it does not need
   the standing-start margin it carries on its own. */
.tier .ledger { margin-top: var(--space-6); }

/* ── The ledger ─────────────────────────────────────────────────────────────
   Four figures, hairline-ruled. On a phone they stack and the rule is
   horizontal; from 700px they sit in a row and the rule turns vertical, which
   is the same statement read the other way round. */
.ledger {
  display: grid;
  gap: 0;
  margin: var(--space-8) 0 0;
  padding: 0;
  list-style: none;
}
@media (min-width: 700px) { .ledger { grid-template-columns: repeat(4, 1fr); } }

.ledger-cell {
  display: grid;
  gap: var(--space-1-5);
  align-content: start;
  padding: var(--space-4) 0;
  border-top: 1px solid var(--line-hairline);
}
@media (min-width: 700px) {
  .ledger-cell { padding: var(--space-2) var(--space-5) var(--space-4) 0; }
  /* The rule runs between the columns, never around them, and never down the
     outside of the first one. */
  .ledger-cell + .ledger-cell { border-left: 1px solid var(--line-hairline); padding-left: var(--space-5); }
}

/* A part, not a total, so it states rather than glows. These were the sky's own
   ink when they were the largest figures in the section; they are not any more,
   and ink on every figure at once left nothing for the total to be. It also
   buys back the live update: the settle pulse below fades FROM ink, which is
   invisible on a number that was already ink and legible on one that was not. */
.ledger-n {
  font-family: var(--font-mono);
  font-size: clamp(2rem, 8vw, 2.9rem);
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
  color: var(--color-text-primary);
}

.ledger-k {
  font-family: var(--font-mono);
  font-size: var(--ev-label);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--track-nav);
  color: var(--color-text-secondary);
}


/* ── Source kinds ─────────────────────────────────────────────────────────
   The five accepted kinds of source, on one wrapped line under the research
   half. Deliberately the smallest type in the section: it is the audit trail
   for the figure above it, not a figure in its own right, and it must never
   read as a sixth statistic. */
.kinds {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-6);
  margin: var(--space-5) 0 0;
  padding: 0;
  list-style: none;
}
.kind {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--ev-label);
  text-transform: uppercase;
  letter-spacing: var(--track-nav);
  color: var(--color-text-muted);
}
.kind-n {
  font-size: var(--text-field);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
  color: var(--color-text-primary);
}

/* ── The plain reading ──────────────────────────────────────────────────────
   What all the figures above actually mean, in three sentences a reader can
   repeat to somebody else. Side by side, because they are one statement in
   three parts rather than a list to work down. */
.plainly {
  display: grid;
  gap: 0;
  margin: 56px 0 0;
  padding: 0;
  list-style: none;
}
@media (min-width: 860px) { .plainly { grid-template-columns: repeat(3, 1fr); } }

.plainly li {
  padding: var(--space-5) 0;
  border-top: 1px solid var(--line-hairline);
  max-width: 42ch;
  font-size: var(--ev-body);
  line-height: 1.7;
  font-weight: 300;
  color: var(--color-text-muted);
}
@media (min-width: 860px) {
  .plainly li { padding-right: var(--space-6); }
  .plainly li + li { border-left: 1px solid var(--line-hairline); padding-left: var(--space-6); }
}
/* The claim leads, in the page's own voice, and the sentence under it is the
   evidence. One bold lead-in reads faster than a heading plus a paragraph and
   costs a line instead of three. */
.plainly b {
  color: var(--color-text-primary);
  font-weight: 700;
}
.plainly span { color: var(--color-text-primary); font-variant-numeric: tabular-nums; }

/* ── The shipped datasets ──────────────────────────────────────────────────
   The figures inside the compiled half, on one line. This was a two-column
   definition list of long sentences, which took a screen to say what a strip
   says at a glance. */
.strip {
  display: grid;
  gap: 0;
  margin: var(--space-6) 0 0;
  padding: 0;
  list-style: none;
}
@media (min-width: 640px) { .strip { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .strip { grid-template-columns: repeat(4, 1fr); } }
/* Three-up, for a strip with three entries in it. Left at four columns a
   three-entry strip leaves a quarter of the line empty, which reads as a
   figure that failed to load rather than as a strip that is simply three
   long. */
@media (min-width: 1000px) { .strip-3 { grid-template-columns: repeat(3, 1fr); } }

.strip li {
  display: grid;
  gap: var(--space-1);
  align-content: start;
  padding: var(--space-4) 0;
  border-top: 1px solid var(--line-hairline);
}
@media (min-width: 640px) { .strip li { padding-right: var(--space-5); } }
/* No rule down the left of a cell that starts a row, which is a different cell
   at two columns than at four. Bounded rather than overridden: an unbounded
   two-column rule wins on specificity over `li + li` and leaves the four-column
   row ruled in the wrong places. */
@media (min-width: 640px) and (max-width: 999px) {
  .strip li:not(:nth-child(2n + 1)) { border-left: 1px solid var(--line-hairline); padding-left: var(--space-5); }
}
@media (min-width: 1000px) {
  .strip li + li { border-left: 1px solid var(--line-hairline); padding-left: var(--space-5); }
}
/* The same size as a ledger figure, deliberately. These are the two halves of
   one total and they are peers; the largest number Velora has is in this strip,
   and setting it two steps down from a four-figure count read as the small
   print of the section that was quoting it. */
.strip b {
  font-family: var(--font-mono);
  font-size: clamp(2rem, 8vw, 2.9rem);
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
  color: var(--color-text-primary);
}
.strip span {
  max-width: 30ch;
  font-size: var(--ev-note);
  line-height: 1.6;
  font-weight: 300;
  color: var(--color-text-muted);
}


/* The freshness stamp under the database section. Script-only, see site.js. */
[data-corpus-stamp] { margin-top: var(--space-8); }

/* A figure that has just been replaced by a live one. A single quiet pulse of
   the sky's ink: enough that a number moving is legible as an update rather
   than as a layout glitch, not enough to be an animation anyone watches. */
@keyframes stat-settle {
  from { color: var(--ink); text-shadow: 0 0 30px var(--ink-glow); }
}
/* A subtotal moves for the same reason a figure inside it did, so it pulses on
   the same terms. The tally itself is excluded: it has its own arrival, the
   count-up, and a figure cannot land twice. */
.js [data-stat][data-live="true"],
.js [data-sum][data-live="true"]:not([data-count-up]) { animation: stat-settle 1.2s var(--ease-atmo) both; }
@media (prefers-reduced-motion: reduce) {
  .js [data-stat][data-live="true"],
  .js [data-sum][data-live="true"] { animation: none; }
}

/* ── The two vectors ────────────────────────────────────────────────────── */
.vectors {
  display: grid;
  gap: 0;
  margin: var(--space-8) 0 0;
}
@media (min-width: 700px) { .vectors { grid-template-columns: 1fr 1fr; } }

.vector {
  padding: var(--space-5) 0;
  border-top: 1px solid var(--line-hairline);
}
@media (min-width: 700px) {
  .vector + .vector { border-left: 1px solid var(--line-hairline); padding-left: var(--space-6); }
  .vector:first-child { padding-right: var(--space-6); }
}

.vector-k {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--ev-label);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--track-nav);
  color: var(--color-text-muted);
}
.vector-n {
  margin: var(--space-1) 0 var(--space-2);
  font-family: var(--font-mono);
  font-size: clamp(2rem, 7vw, 2.6rem);
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  text-shadow: 0 0 30px var(--ink-glow);
  transition: var(--transition-ink), text-shadow 1.5s var(--ease-atmo);
}
.vector-d {
  margin: 0 0 var(--space-4);
  max-width: 40ch;
  font-size: var(--ev-body);
  line-height: 1.7;
  font-weight: 300;
  color: var(--color-text-muted);
}
.vector-d em { font-style: normal; color: var(--color-text-secondary); }

/* The field names, verbatim. Mono and lowercase, because they are identifiers
   and dressing them up as UI labels would be a lie about what they are. */
.chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1-5) var(--space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}
.chips li {
  font-family: var(--font-mono);
  font-size: var(--ev-label);
  letter-spacing: 0.02em;
  color: var(--color-text-faint);
}
.chips li:not(:last-child)::after { content: " ·"; color: var(--color-text-faint); }
.chips .chip-more { color: var(--color-text-muted); }

/* ── The three modes ────────────────────────────────────────────────────── */
.modes {
  display: grid;
  gap: 0;
  margin: var(--space-6) 0 0;
  padding: 0;
  list-style: none;
}
@media (min-width: 860px) { .modes { grid-template-columns: repeat(3, 1fr); } }

.mode {
  padding: var(--space-5) 0;
  border-top: 1px solid var(--line-hairline);
}
@media (min-width: 860px) {
  .mode { padding-right: var(--space-5); }
  .mode + .mode { border-left: 1px solid var(--line-hairline); padding-left: var(--space-5); }
}

.mode-k {
  margin: 0 0 var(--space-2);
  font-family: var(--font-sans);
  font-size: var(--text-choice);
  font-weight: 700;
  letter-spacing: var(--track-tight);
  color: var(--color-text-primary);
}
/* The qualifier belongs to the name, so it sits on the same line rather than
   becoming a second heading. */
.mode-note {
  font-family: var(--font-mono);
  font-size: var(--ev-label);
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: var(--track-nav);
  color: var(--color-text-faint);
  white-space: nowrap;
}
/* .mode-f, the line that used to print each mode's formula, is gone with the
   formulas. See the note at the top of #fit in index.html. */
.mode-d {
  margin: 0;
  max-width: 40ch;
  font-size: var(--ev-body);
  line-height: 1.7;
  font-weight: 300;
  color: var(--color-text-muted);
}

/* ── The engine ─────────────────────────────────────────────────────────────
   The one operable thing on the page. It gets the single exception to the
   no-boxes rule, and earns it: this is an instrument, and an instrument reads
   as one because it has a housing. It is also the only element here a visitor
   can change the state of, so the edge is what says "this part is yours". */
.engine {
  margin: var(--space-8) 0 0;
  border: 1px solid var(--line-hairline);
  border-radius: var(--radius-lg);
  background: linear-gradient(180deg, var(--fill-2), var(--fill-1));
  overflow: hidden;
}

.engine-top {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  justify-content: space-between;
  margin: 0;
  padding: var(--space-3-5) var(--space-5);
  border-bottom: 1px solid var(--line-hairline);
  font-family: var(--font-mono);
  font-size: var(--ev-label);
  text-transform: uppercase;
  letter-spacing: var(--track-nav);
  color: var(--color-text-faint);
}
.engine-city { color: var(--color-text-secondary); }

.engine-body { display: grid; gap: var(--space-6); padding: var(--space-6) var(--space-5); }
@media (min-width: 700px) { .engine-body { padding: var(--space-8); } }
.engine-body .caption { margin-top: 0; }

.slider { display: grid; gap: var(--space-3); }
.slider-k {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
  font-size: var(--ev-body);
  font-weight: 300;
  line-height: 1.45;
  color: var(--color-text-secondary);
  cursor: pointer;
}
/* Words, not a scalar. Both readouts say what the position MEANS, the raw
   0–1 is an internal representation and printing it taught a reader nothing
   they could use. Sans, right-aligned, and allowed to wrap on a narrow phone
   rather than shoving the label off the line. */
.slider-v {
  flex: none;
  max-width: 45%;
  font-family: var(--font-sans);
  font-size: var(--ev-body);
  font-weight: 700;
  letter-spacing: var(--track-tight);
  text-align: right;
  color: var(--color-text-primary);
}

/* A hairline with a handle on it. The track is the same rule used everywhere
   else in this section; the thumb takes the sky's ink like the swatch on the
   sky button does. */
.slider input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: var(--touch-min);
  margin: calc(var(--touch-min) / -2 + 8px) 0;
  background: transparent;
  cursor: pointer;
}
.slider input[type="range"]::-webkit-slider-runnable-track {
  height: 2px;
  border-radius: var(--radius-full);
  background: var(--line-soft);
}
.slider input[type="range"]::-moz-range-track {
  height: 2px;
  border-radius: var(--radius-full);
  background: var(--line-soft);
}
.slider input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 16px;
  height: 16px;
  margin-top: -7px;
  border: 0;
  border-radius: var(--radius-full);
  background: var(--ink);
  box-shadow: 0 0 0 5px var(--ink-soft), 0 1px 3px rgba(0, 0, 0, 0.45);
  transition: box-shadow var(--dur-fast) ease, var(--transition-ink);
}
.slider input[type="range"]::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border: 0;
  border-radius: var(--radius-full);
  background: var(--ink);
  box-shadow: 0 0 0 5px var(--ink-soft), 0 1px 3px rgba(0, 0, 0, 0.45);
}
.slider input[type="range"]:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 5px var(--focus-ring); }
.slider input[type="range"]:focus-visible::-moz-range-thumb     { box-shadow: 0 0 0 5px var(--focus-ring); }
.slider input[type="range"]:focus { outline: none; }
@media (hover: hover) {
  .slider input[type="range"]:hover::-webkit-slider-thumb { box-shadow: 0 0 0 7px var(--ink-soft), 0 1px 3px rgba(0, 0, 0, 0.45); }
}

.readout {
  display: grid;
  gap: 0;
  margin: 0;
  border-top: 1px solid var(--line-hairline);
}
@media (min-width: 760px) { .readout { grid-template-columns: minmax(15rem, 22rem) 1fr; } }

.readout-cell {
  display: grid;
  gap: var(--space-2);
  align-content: start;
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--line-hairline);
}
@media (min-width: 760px) {
  .readout-cell { padding: var(--space-4) var(--space-6) var(--space-4) 0; border-bottom: 0; }
  .readout-cell + .readout-cell { border-left: 1px solid var(--line-hairline); padding-left: var(--space-6); }
}
.readout-cell dt {
  font-family: var(--font-mono);
  font-size: var(--ev-label);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--track-nav);
  color: var(--color-text-faint);
}
/* Words now, not a figure. The exhibit reports a direction and a strength band
   rather than the engine's own numbers, so this is set in sans at a readable
   size instead of as a big mono value, a value is what it is deliberately
   not. See the note above #fit in index.html. */
.readout-cell dd {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 1.15rem;
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: var(--track-tight);
  color: var(--color-text-primary);
}

/* ── The effect meter ───────────────────────────────────────────────────────
   Direction and strength, drawn. It fills right from a centre tick when the
   rule argues for the city and left when it argues against, and collapses to
   nothing when the traveler does not care, which is the entire point the
   exhibit exists to make, and the one thing about it that is worth seeing
   rather than reading. */
.meter {
  position: relative;
  height: 6px;
  margin-top: var(--space-1);
  border-radius: var(--radius-full);
  background: var(--fill-2);
  overflow: hidden;
}
.meter-mid {
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
  width: 1px;
  background: var(--line-strong);
}
.meter-fill {
  position: absolute;
  top: 0;
  bottom: 0;
  /* One rule for both directions: the bar always starts at the centre and
     grows outward on the side --dir picks. */
  left: 50%;
  width: calc(var(--fill, 0) * 50%);
  transform-origin: left center;
  transform: scaleX(var(--dir, 1));
  border-radius: var(--radius-full);
  background: var(--ink);
  box-shadow: 0 0 16px var(--ink-glow);
  transition: width var(--dur-base) var(--ease-travel),
              var(--transition-ink);
}
@media (prefers-reduced-motion: reduce) { .meter-fill { transition: none; } }

/* The reason is a sentence, so it is set as one. Sans rather than the mono
   used for labels around it, because it is something the app will say to a
   traveler, not a value it holds. */
.readout-reason dd {
  font-family: var(--font-sans);
  font-size: var(--ev-body);
  font-weight: 400;
  line-height: 1.45;
  letter-spacing: var(--track-tight);
  color: var(--ink);
  text-shadow: 0 0 26px var(--ink-glow);
  transition: var(--transition-ink), text-shadow 1.5s var(--ease-atmo);
  text-wrap: balance;
}
/* The rule has been dragged out of play. Said, not just dimmed. */
.readout-reason dd[data-idle="true"] {
  font-style: italic;
  color: var(--color-text-faint);
  text-shadow: none;
}

/* ── The honesty stack ──────────────────────────────────────────────────── */
.honesty { margin: var(--space-6) 0 0; display: grid; gap: 0; }
.honesty-row {
  display: grid;
  gap: var(--space-2) var(--space-6);
  padding: var(--space-5) 0;
  border-top: 1px solid var(--line-hairline);
}
@media (min-width: 700px) { .honesty-row { grid-template-columns: 9.5rem 1fr; } }
.honesty-row dt {
  font-family: var(--font-mono);
  font-size: var(--ev-label);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--track-nav);
  color: var(--color-text-secondary);
}
.honesty-row dd {
  margin: 0;
  max-width: 60ch;
  font-size: var(--ev-body);
  line-height: 1.75;
  font-weight: 300;
  color: var(--color-text-muted);
}

/* ── The alternative ────────────────────────────────────────────────────────
   Two columns per row, theirs and ours. Deliberately NOT a tick-and-cross
   table: every left-hand cell is a published measurement with a citation under
   it, and dressing a citation up with a red X would make it look like an
   opinion. The only colour is on the label. */
.versus { margin: var(--space-8) 0 0; padding: 0; list-style: none; display: grid; gap: 0; }

.versus-row {
  display: grid;
  gap: var(--space-5);
  padding: var(--space-6) 0;
  border-top: 1px solid var(--line-hairline);
}
@media (min-width: 760px) { .versus-row { grid-template-columns: 1fr 1fr; gap: var(--space-8); } }

.versus-side { display: grid; gap: var(--space-2); align-content: start; }
.versus-k {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--text-label-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--track-nav);
}
.versus-them .versus-k { color: var(--color-warn); }
.versus-us   .versus-k { color: var(--color-positive-soft); }

.versus-d {
  margin: 0;
  max-width: 46ch;
  font-size: var(--ev-body);
  line-height: 1.7;
  font-weight: 300;
  color: var(--color-text-secondary);
}
.versus-c {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--ev-label);
  line-height: 1.7;
  letter-spacing: 0.02em;
  color: var(--color-text-faint);
}

.sources {
  margin: var(--space-8) 0 0;
  padding: 0 0 0 1.5em;
  display: grid;
  gap: var(--space-2-5);
}
.sources li {
  font-size: var(--ev-note);
  line-height: 1.7;
  font-weight: 300;
  color: var(--color-text-faint);
}
.sources li::marker { font-family: var(--font-mono); }

/* ── The cards ──────────────────────────────────────────────────────────── */
.pair {
  display: grid;
  gap: var(--space-10);
  margin: var(--space-10) 0 0;
}
@media (min-width: 760px) { .pair { grid-template-columns: 1fr 1fr; gap: var(--space-8); } }

.pair-item {
  display: grid;
  gap: var(--space-6);
  justify-items: center;
  margin: 0;
  text-align: center;
}
@media (min-width: 760px) { .pair-item { align-content: start; } }

/* Same phone treatment as the tour, so a screen that has not arrived degrades
   to the same quiet panel rather than a broken-image glyph. */
.pair .shot { max-width: min(250px, 62vw); }
@media (min-width: 900px) { .pair .shot { max-width: 280px; justify-self: center; } }

.pair-copy { display: grid; gap: var(--space-2); max-width: 40ch; }
.pair-k {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--ev-label);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--track-nav);
  color: var(--color-text-muted);
}
.pair-d {
  margin: 0;
  font-size: var(--ev-body);
  line-height: 1.75;
  font-weight: 300;
  color: var(--color-text-secondary);
}
