/*
 * flarehub.co.ke, third pass: the call lands.
 *
 * One object carries the page. The handset is whole in the hero, opens across three viewport
 * heights of held scroll, and the panel that rises out of it becomes the live calls screen. Every
 * frame of that is bound to scroll position rather than to a clock, which is the whole point: the
 * visitor is driving it, and scrolling back up runs it backwards.
 *
 * Read this file in the order the visitor meets it. The last two sections are the ones that matter
 * most, because they are what a visitor who asked for less movement, or whose browser has never
 * heard of a scroll timeline, actually gets.
 *
 * Rendered assets (handset, mascot, objects, spark) are composited with mix-blend-mode: screen.
 * They only work on black. Every panel that holds one stays black in both themes, on purpose.
 */

/* ── the page frame ──────────────────────────────────────────────────────────────────── */

/*
 * No overflow-x: hidden on the body. It reads as a harmless guard and it is not: it makes the body
 * a scroll container, and every position: sticky in the page then sticks to a box that does not
 * scroll. Nothing here overflows sideways; the check script proves it at 1440 and at 380.
 */

.wrap {
  width: 100%;
  max-width: 1440px;
  margin: 0 auto;
  padding-inline: 96px;
}

/*
 * A section that holds a screen-blended asset. Black in both themes, never a surface colour.
 *
 * It deliberately does not clip. This class goes on <main> as well as on sections, and an ancestor
 * that clips is a scroll container: every position: sticky inside one sticks to a box that does not
 * scroll and slides straight past. Whatever needs clipping says so itself, below.
 */
.ink {
  position: relative;
  background: #000;
  color: #f2f1ef;
  isolation: isolate;
}

/*
 * An absolutely positioned element paints above in-flow content whatever the source order says, so
 * a decorative plate dropped in as the first child of a section covers the whole section. Ordering
 * it here once is the fix: decoration behind, content in front, grain over the top of both.
 */
.ink > * {
  position: relative;
  z-index: 1;
}

.ink > .grain {
  z-index: 2;
}

/* Position as well as order: the rule above sets every child relative, including these. */
.ink > .server-plate,
.ink > .page-plate,
.ink > .section-sky,
.costs > .section-sky,
.page-head > .section-sky {
  position: absolute;
  z-index: 0;
}

.grain {
  position: absolute;
  inset: 0;
  background: url('/textures/grain.webp') repeat;
  background-size: 256px;
  opacity: 0.04;
  pointer-events: none;
  z-index: 1;
}

.scr {
  mix-blend-mode: screen;
}

.mono {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

.tag {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.lead {
  font-size: 22px;
  line-height: 1.4;
  color: var(--text-muted);
  max-width: 36ch;
}

/*
 * Display sizes. Fluid rather than stepped, and sized so the headline's own line breaks are the
 * ones that happen: the lines are separate elements because each has to rise on its own, and a
 * size that made one of them wrap would put a half line in the middle of the reveal.
 */
.display {
  font-size: clamp(32px, 5.6vw, 84px);
  line-height: 0.98;
  letter-spacing: -0.045em;
  font-weight: 600;
}

.title {
  font-size: clamp(34px, 4.4vw, 64px);
  line-height: 0.98;
  letter-spacing: -0.04em;
  font-weight: 600;
  text-wrap: balance;
}

.subtitle {
  font-size: clamp(28px, 3.9vw, 56px);
  line-height: 1;
  letter-spacing: -0.035em;
  font-weight: 600;
  text-wrap: balance;
}

/* ── header ──────────────────────────────────────────────────────────────────────────── */

/*
 * An island: detached from the edges, carrying its own ground.
 *
 * A gradient scrim was not enough. Half these pages put the navigation over a photograph of cloud
 * with light in it, and twelve pixel mono over that is unreadable wherever the cloud happens to be
 * bright. A panel of its own settles it once, on every page, whatever is behind it.
 *
 * Fixed rather than absolute, because an island that scrolls away is just a bar with corners, and
 * this site is long.
 */
.site-header {
  position: fixed;
  top: 16px;
  left: 50%;
  transform: translateX(-50%);
  /* As wide as what is in it and no wider: an island that spans the window is a bar. */
  width: max-content;
  max-width: calc(100% - 28px);
  height: 52px;
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 0 8px 0 18px;
  border: 1px solid rgba(242, 241, 239, 0.12);
  border-radius: 999px;
  background: rgba(10, 10, 11, 0.76);
  backdrop-filter: blur(18px) saturate(150%);
  z-index: 40;
  /*
   * The island carries its own two colours rather than the page's. Everything inside reads --text
   * and --text-muted, and on the light pages those resolve to dark ink, which on a dark panel is
   * nothing at all. Setting them here settles it for every descendant in one place, now and for
   * anything added later.
   */
  --text: #f2f1ef;
  --text-muted: #a3a19c;
  color: var(--text);
}

/* Where the blur is not available the panel has to carry the contrast on its own. */
@supports not (backdrop-filter: blur(4px)) {
  .site-header {
    background: rgba(10, 10, 11, 0.95);
  }
}

/*
 * The lockup, at the proportions the product uses in its own sidebar: the mark, "Flare" heavy and
 * tight, then "CRM" small and widely letterspaced. Everything is sized from one --size so the same
 * three rules serve a seventeen pixel header and a two hundred pixel footer.
 */
.brand {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  color: var(--text);
}

.brand img {
  width: 20px;
  height: 20px;
  align-self: center;
}

/* The two halves at the product's own numbers: 18 on 10, extra bold against medium. */
.brand b {
  font-size: 18px;
  font-weight: 800;
  letter-spacing: -0.03em;
  margin-right: -2px;
}

.brand i {
  font-style: normal;
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.22em;
  color: var(--text-muted);
}

.site-nav {
  display: flex;
  align-items: center;
  gap: 22px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  /* Light, not muted. A navigation nobody can read is decoration. */
  color: var(--text);
}

/*
 * The label rolls up and its own copy arrives from underneath, in the accent. The page reveals its
 * headline by having each line rise from under its baseline; this is the same move at a twentieth
 * of the size, so the site has one idea about how things arrive rather than a fade for every mood.
 */
.site-nav a,
.menu-open {
  position: relative;
  display: inline-flex;
  align-items: center;
  line-height: 1.4;
  text-decoration: none;
  color: inherit;
  border: 0;
  background: none;
  font: inherit;
  letter-spacing: inherit;
  padding: 0;
  cursor: pointer;
}

/*
 * The window the roll moves behind, exactly one line tall. It is its own element because the
 * control around it is sometimes a pill with its own height, and a clip that is also the pill
 * shows both copies at once.
 */
.site-nav .clip {
  display: block;
  height: 1.4em;
  overflow: hidden;
}

.site-nav .roll {
  display: block;
  transition: transform 340ms var(--ease-out);
}

.site-nav a:hover .roll,
.site-nav a:focus-visible .roll,
.menu-open:hover .roll,
.menu-open:focus-visible .roll {
  transform: translateY(-1.4em);
}

.site-nav .roll span {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 1.4em;
}

.site-nav .roll span + span {
  color: var(--flare);
}

/* The page it is on, marked once and quietly. */
.site-nav a[aria-current='page'] .roll span:first-child {
  color: var(--flare);
}

/* The one control that is not a page, given the accent ring so it reads as the way in. */
.menu-open {
  height: 36px;
  padding: 0 16px;
  border-radius: 999px;
  background: rgba(242, 241, 239, 0.07);
  transition: background var(--dur-hover) ease-out;
}

.menu-open:hover,
.menu-open:focus-visible {
  background: rgba(255, 106, 61, 0.16);
}

.icon {
  width: 1em;
  height: 1em;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ── buttons ─────────────────────────────────────────────────────────────────────────── */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  height: 56px;
  padding-inline: 26px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  font-size: 17px;
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
  transition:
    background var(--dur-hover) ease-out,
    border-color var(--dur-hover) ease-out,
    color var(--dur-hover) ease-out;
}

.btn-primary {
  background: var(--flare);
  color: var(--on-flare-ink);
}

.btn-primary:hover {
  background: var(--flare-hover);
  color: var(--on-flare-ink);
}

.btn-ghost {
  border-color: #3a3a3d;
  color: #f2f1ef;
}

.btn-ghost:hover {
  border-color: #f2f1ef;
}

.link-more {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 16px;
  font-weight: 500;
  color: var(--flare);
  text-decoration: none;
}

.link-more:hover {
  color: var(--flare-hover);
}

/* ── 1. the preloader ────────────────────────────────────────────────────────────────── */

/*
 * Only ever shown when a script decided it should be: it sets data-preload="on" before the first
 * paint, and never on a return visit or under reduced motion. Without script the attribute is
 * never set and this rule never matches, so a visitor with no JavaScript sees the page directly.
 */
.preloader {
  display: none;
}

/*
 * Matches while the attribute is either "on" or "done", not only "on". Gated on "on" alone the rule
 * stops matching the instant the script hands over, the element goes back to display: none, and the
 * fade below never runs: the charcoal is simply cut away mid-frame.
 */
[data-preload] .preloader {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #17171a;
  /* Quick, so the curtain behind it still has most of its wipe left to be seen. */
  transition:
    opacity 240ms var(--ease-out),
    visibility 0s linear 240ms;
}

[data-preload='done'] .preloader {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

.preloader img {
  width: 72px;
  height: 72px;
  mix-blend-mode: screen;
}

.preloader-count {
  position: absolute;
  left: 48px;
  bottom: 48px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.preloader-count b {
  font-family: var(--font-mono);
  font-size: clamp(56px, 8vw, 96px);
  line-height: 1;
  letter-spacing: -0.04em;
  font-weight: 400;
  font-variant-numeric: tabular-nums;
}

/* ── 2. the curtain ──────────────────────────────────────────────────────────────────── */

/*
 * The hero does not fade in. A solid panel wipes off it and the hero is already finished behind.
 * It runs on its own when there is no preloader to wait for, and is held until the preloader has
 * gone when there is one.
 */
.curtain {
  position: fixed;
  inset: 0;
  z-index: 90;
  background: var(--flare);
  transform-origin: right center;
  animation: curtain-wipe 700ms var(--ease-out) both;
  pointer-events: none;
}

[data-preload='on'] .curtain {
  animation-play-state: paused;
}

[data-preload='done'] .curtain {
  animation-play-state: running;
}

@keyframes curtain-wipe {
  from {
    transform: scaleX(1);
  }
  to {
    transform: scaleX(0);
  }
}

/* The headline arrives line by line from under its own baseline, behind a clip. */
.rise {
  display: block;
  overflow: hidden;
}

.rise > span {
  display: block;
  animation: rise-in 700ms var(--ease-out) both;
  animation-delay: calc(var(--i, 0) * 80ms + var(--hold, 0ms));
}

@keyframes rise-in {
  from {
    transform: translateY(105%);
  }
  to {
    transform: translateY(0);
  }
}

.settle {
  animation: settle-in 500ms var(--ease-out) both;
  animation-delay: calc(240ms + var(--hold, 0ms));
}

@keyframes settle-in {
  from {
    opacity: 0;
    transform: translateY(14px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* ── 3. the hero ─────────────────────────────────────────────────────────────────────── */

/*
 * Sky, then words, then the cast standing in front of it. The three layers are stacked rather than
 * placed, so there is nothing to re-position at a narrower width: the column gets narrower, the
 * cast gets shorter, and the composition holds all the way down to 320.
 */
.hero {
  position: relative;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  /*
   * Centred when there is room and from the top when there is not. "safe" is what makes that one
   * declaration rather than two: it falls back to the start edge the moment the content would
   * overflow, so a tall window has no hole in the middle and a short one never clips the headline.
   * A browser that has not heard of it keeps the line above and starts from the top always.
   */
  justify-content: flex-start;
  justify-content: safe center;
  text-align: center;
  gap: clamp(10px, 1.6vh, 24px);
  padding: clamp(96px, 12vh, 124px) 0 clamp(48px, 7vh, 72px);
  overflow: hidden;
}

.hero-sky {
  position: absolute;
  inset: 0;
  display: block;
  z-index: 0;
}

.hero-sky img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 34%;
  opacity: 0.85;
}

/*
 * Two jobs, and the second is not taste. The radial darkens the middle of the frame until the
 * headline has the contrast it needs over a photograph. The vertical one reaches solid black before
 * the cast does, because everything standing down there is composited with screen: over a sky
 * rather than over black, the near-black each asset was rendered on shows as a pale box around it.
 */
.hero-scrim {
  position: absolute;
  inset: 0;
  z-index: 0;
  background:
    radial-gradient(ellipse 62% 48% at 50% 32%, rgba(0, 0, 0, 0.74), rgba(0, 0, 0, 0) 100%),
    linear-gradient(
      to bottom,
      rgba(0, 0, 0, 0.45),
      rgba(0, 0, 0, 0) 24%,
      rgba(0, 0, 0, 0.3) 42%,
      rgba(0, 0, 0, 0.78) 58%,
      rgba(0, 0, 0, 0.96) 72%,
      #000 84%
    );
  pointer-events: none;
}

.hero-copy {
  position: relative;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(18px, 2.6vh, 32px);
  width: 100%;
  max-width: 1100px;
  margin-inline: auto;
}

.hero-copy .lead {
  max-width: 44ch;
  margin-inline: auto;
}

/* The line the whole page is about, in the one other colour this site has. */
.accent {
  color: var(--flare);
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
}

/*
 * Everybody in the hero stands on one line: the mascot, the object the page is built on, and three
 * of the things that land on a timeline. Sized from the box rather than the viewport, so the group
 * keeps its arrangement as it shrinks instead of coming apart.
 */
/*
 * Given a height and allowed to give it back. Everything inside is placed as a percentage of this
 * box, so when a short window squeezes it the whole group scales together rather than the objects
 * sliding out from under each other. That is what makes one arrangement work from 320 to 2560.
 */
/*
 * Wide enough, the cast stands either side of the words: the mascot coming in from the left, the
 * handset and the things that land on a timeline on the right, the headline between them. Narrow,
 * the two flanks fall under the words in one row, which is the only arrangement that fits a phone.
 */
.hero-stage {
  position: relative;
  /*
   * Explicitly auto, against the rule that gives every child of a dark section z-index 1. A
   * positioned element with a z-index is a stacking context, and mix-blend-mode only blends within
   * one: the cast would have had nothing behind it to blend with and its black backing would have
   * painted as an opaque rectangle over the sky. Nothing between these images and the sky may
   * create a stacking context.
   */
  z-index: auto;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 760px) minmax(0, 1fr);
  align-items: center;
  justify-items: center;
  gap: clamp(10px, 2vw, 40px);
  width: 100%;
  margin-top: clamp(-40px, -3vh, 0px);
}

.flank {
  position: relative;
  width: 100%;
  height: clamp(230px, 38vh, 400px);
}

.flank img {
  position: absolute;
  display: block;
  pointer-events: none;
  transition: translate 700ms cubic-bezier(0.2, 0.8, 0.3, 1);
  translate: calc(var(--drift-x, 0px) * var(--depth, 1)) calc(var(--drift-y, 0px) * var(--depth, 1));
}

/* Left: the mascot, aimed at the words, with one thing above him. */
.f-mascot {
  right: 2%;
  bottom: 12%;
  width: min(80%, 270px);
  height: auto;
  --depth: 1.1;
}

.f-bubble {
  left: 4%;
  top: 6%;
  width: min(27%, 88px);
  height: auto;
  rotate: -10deg;
  --depth: 2.3;
}

/* Right: the object the page is about, largest, with two more around it. */
.f-handset {
  left: 2%;
  bottom: 0;
  height: 88%;
  width: auto;
  max-width: 100%;
  --depth: 0.5;
}

.f-note {
  right: 8%;
  top: 2%;
  width: min(27%, 94px);
  height: auto;
  rotate: 12deg;
  --depth: 2.6;
}

.f-bell {
  right: 2%;
  bottom: 14%;
  width: min(25%, 86px);
  height: auto;
  rotate: -12deg;
  --depth: 1.7;
}

/* ── 4. the pinned stage ─────────────────────────────────────────────────────────────── */

/*
 * The page. Three viewport heights of held scroll; the sticky child is what the visitor sees and
 * the track is what supplies the progress. Every child below reads the same named view timeline,
 * so they cannot drift apart from each other or from the scrollbar.
 */
.stage {
  position: relative;
  height: 300vh;
  background: #000;
  view-timeline-name: --stage;
  view-timeline-axis: block;
}

/*
 * The track must not clip, for the same reason the body must not: an ancestor that clips is a
 * scroll container, and the pin would stick to it rather than to the page and scroll straight past.
 * Everything that needs hiding is hidden by the pin itself, which is the thing being looked at.
 */
.stage-pin {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
  isolation: isolate;
  background: #000;
  color: #f2f1ef;
}

.stage-frames {
  position: absolute;
  left: 50%;
  top: 50%;
  /* Bigger and on the centre now that there is no bar along the bottom to leave room for. */
  width: min(35vh, 360px);
  aspect-ratio: 418 / 627;
  transform: translate(-50%, -50%);
  z-index: 2;
}

.stage-frame {
  position: absolute;
  inset: 0;
  animation: hand-off linear both;
  animation-timeline: --stage;
}

.stage-frame img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

@keyframes hand-off {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}

/*
 * Six frames stacked in one box, the first on top. Each fades out over its own slice of the
 * progress and reveals the one beneath, which is a cross-dissolve with no script and no
 * intermediate state to get wrong. The last frame never fades: it is the floor.
 */
.stage-frame:nth-child(1) {
  z-index: 10;
  animation-range: contain 4% contain 19%;
}

.stage-frame:nth-child(2) {
  z-index: 9;
  animation-range: contain 20% contain 35%;
}

.stage-frame:nth-child(3) {
  z-index: 8;
  animation-range: contain 36% contain 51%;
}

.stage-frame:nth-child(4) {
  z-index: 7;
  animation-range: contain 52% contain 67%;
}

.stage-frame:nth-child(5) {
  z-index: 6;
  animation-range: contain 68% contain 83%;
}

.stage-frame:nth-child(6) {
  z-index: 5;
  animation-name: halves-part;
  animation-range: contain 84% contain 100%;
}

/* The halves drift apart and dim as the panel between them stops being a panel. */
@keyframes halves-part {
  from {
    opacity: 1;
    transform: scale(1);
    clip-path: inset(0 0 0 0);
  }
  to {
    opacity: 0.45;
    transform: scale(1.25);
    clip-path: inset(40% 0 0 0);
  }
}

.stage-shot {
  position: absolute;
  left: 50%;
  top: 50%;
  width: min(56vw, 800px);
  /* The markup carries width and height so the box is reserved before the bytes arrive; once CSS
     sets a width, the height attribute has to be released or the picture stretches. */
  height: auto;
  /* Sits high enough to leave the closing sentence its own floor. */
  transform: translate(-50%, -58%);
  border: 1px solid #3a3a3d;
  border-radius: var(--radius-md);
  z-index: 12;
  opacity: 0;
  animation: shot-arrive linear both;
  animation-timeline: --stage;
  animation-range: contain 86% contain 100%;
}

@keyframes shot-arrive {
  from {
    opacity: 0;
    box-shadow: 0 0 0 rgba(255, 236, 210, 0);
  }
  to {
    opacity: 1;
    box-shadow: 0 0 120px rgba(255, 236, 210, 0.25);
  }
}

/* The sentence that belongs to the state the object is in. Same hand-off as the frames. */
/*
 * A real width, not a max-width. Every child is absolutely positioned, so there is nothing in flow
 * for the box to take its size from: shrink-to-fit resolves to zero and the sentence comes out one
 * word per line.
 */
.stage-lines {
  position: absolute;
  inset: 112px 96px 96px;
  z-index: 3;
  pointer-events: none;
}

/*
 * The frames hand off by stacking, because an opaque picture hides the one beneath it. Text cannot:
 * a transparent line shows every line under it at once. So each sentence is off except inside its
 * own quarter, switched rather than faded, and the whole set shares one range so they cannot drift.
 */
.stage-line {
  position: absolute;
  left: 0;
  top: 0;
  /* A real width, not a max-width: every line is absolutely positioned, so there is nothing in
     flow for the box to size itself from and shrink-to-fit resolves to one word per line. */
  width: min(30ch, 34vw);
  font-size: clamp(19px, 2vw, 28px);
  line-height: 1.15;
  letter-spacing: -0.02em;
  font-weight: 500;
  opacity: 0;
  /*
   * Longhands, not the shorthand. "animation: linear steps(1, end) both" reads like it names a
   * timing function and a fill, and a browser reads it as two timing functions with no name and
   * throws the whole declaration away. The shorthand would also reset the duration to zero.
   */
  animation-timing-function: steps(1, end);
  animation-fill-mode: both;
  animation-timeline: --stage;
  animation-range: contain 0% contain 100%;
}

.stage-line:nth-child(1) {
  animation-name: say-1;
}

.stage-line:nth-child(2) {
  animation-name: say-2;
}

.stage-line:nth-child(3) {
  animation-name: say-3;
}

.stage-line:nth-child(4) {
  animation-name: say-4;
}

/*
 * The closing line is the only one that is not a caption on an object. By then the object has
 * become the product and fills the middle, so the sentence moves out from under it to the floor of
 * the stage and is given the room to be a sentence rather than a column.
 */
.stage-line:nth-child(5) {
  animation-name: say-5;
  top: auto;
  bottom: 0;
  width: min(58ch, 52vw);
}

@keyframes say-1 {
  0% {
    opacity: 1;
  }
  19% {
    opacity: 0;
  }
}

@keyframes say-2 {
  0% {
    opacity: 0;
  }
  19% {
    opacity: 1;
  }
  40% {
    opacity: 0;
  }
}

@keyframes say-3 {
  0% {
    opacity: 0;
  }
  40% {
    opacity: 1;
  }
  60% {
    opacity: 0;
  }
}

@keyframes say-4 {
  0% {
    opacity: 0;
  }
  60% {
    opacity: 1;
  }
  80% {
    opacity: 0;
  }
}

/*
 * The closing keyframe is not decoration. Without a stop at 100 percent the browser synthesises one
 * from the element's own style, which here is opacity zero, so the last sentence vanished at the
 * exact end of the stage: the one frame a visitor stops on.
 */
@keyframes say-5 {
  0% {
    opacity: 0;
  }
  80% {
    opacity: 1;
  }
  100% {
    opacity: 1;
  }
}

/* ── the satellites ──────────────────────────────────────────────────────────────────── */

/*
 * Two elements each, because two things happen to one stamp at different times: it flies in on an
 * arc at its own quarter, and at the end all four move outward to the corners. One transform
 * cannot hold both, so the outer does the corner and the inner does the arrival.
 */
.sat {
  position: absolute;
  z-index: 14;
  animation: sat-out linear both;
  animation-timeline: --stage;
  animation-range: contain 84% contain 100%;
  pointer-events: none;
}

@keyframes sat-out {
  from {
    transform: translate(0, 0);
  }
  to {
    transform: translate(var(--out-x), var(--out-y));
  }
}

.sat-in {
  display: block;
  rotate: var(--rot);
  animation: sat-arrive linear both;
  animation-timeline: --stage;
}

@keyframes sat-arrive {
  0% {
    opacity: 0;
    transform: translate(var(--from-x), var(--from-y)) scale(0.6);
  }
  62% {
    opacity: 1;
    transform: translate(calc(var(--from-x) * 0.22), calc(var(--from-y) * 0.22 - 4vh)) scale(1.05);
  }
  100% {
    opacity: 1;
    transform: translate(0, 0) scale(1);
  }
}

.sat-1 {
  left: 19.4%;
  top: 24.4%;
  --rot: -12deg;
  --from-x: 14vw;
  --from-y: 10vh;
  --out-x: -13.2vw;
  --out-y: -8.9vh;
}

.sat-1 .sat-in {
  animation-range: contain 8% contain 25%;
}

.sat-2 {
  left: 69.4%;
  top: 57.8%;
  --rot: 9deg;
  --from-x: -12vw;
  --from-y: -8vh;
  --out-x: 15.3vw;
  --out-y: 2.2vh;
}

.sat-2 .sat-in {
  animation-range: contain 33% contain 50%;
}

.sat-3 {
  left: 66.7%;
  top: 17.8%;
  --rot: 18deg;
  --from-x: -10vw;
  --from-y: 12vh;
  --out-x: 16.7vw;
  --out-y: -4.4vh;
}

.sat-3 .sat-in {
  animation-range: contain 58% contain 75%;
}

.sat-4 {
  left: 22.9%;
  top: 62.2%;
  --rot: -6deg;
  --from-x: 11vw;
  --from-y: -11vh;
  --out-x: -15.3vw;
  --out-y: -4.4vh;
}

.sat-4 .sat-in {
  animation-range: contain 83% contain 100%;
}

.sat svg {
  width: var(--size, 130px);
  height: var(--size, 130px);
  overflow: visible;
}

.sat circle {
  fill: none;
  stroke: var(--flare);
}

.sat text {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 2.4px;
  fill: #f2f1ef;
}

/* ── 5. going in ─────────────────────────────────────────────────────────────────────── */

.goingin {
  position: relative;
  height: 200vh;
  view-timeline-name: --goingin;
  view-timeline-axis: block;
  background: #000;
}

.goingin-pin {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
}

.goingin-shot {
  position: absolute;
  left: 50%;
  top: 50%;
  /*
   * Starts at a size the screen is still recognisable at and grows past the edges from there. Wider
   * than the viewport at the outset is not immersion, it is a wall of forty-pixel interface.
   */
  width: 86vw;
  max-width: none;
  height: auto;
  opacity: 0.92;
  transform: translate(-50%, -50%) scale(1);
  animation: go-in linear both;
  animation-timeline: --goingin;
  animation-range: contain 0% contain 100%;
}

@keyframes go-in {
  from {
    transform: translate(-50%, -50%) scale(1);
  }
  to {
    transform: translate(-50%, -50%) scale(1.85);
  }
}

.goingin-vignette {
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at 50% 50%, rgba(0, 0, 0, 0) 22%, rgba(0, 0, 0, 0.9) 92%);
  pointer-events: none;
  z-index: 2;
}

.goingin-mascot {
  position: absolute;
  left: 50%;
  top: 36%;
  width: clamp(140px, 17vw, 240px);
  height: auto;
  z-index: 3;
  transform: translateX(-50%);
  animation: descend linear both;
  animation-timeline: --goingin;
  animation-range: contain 0% contain 100%;
}

@keyframes descend {
  from {
    transform: translateX(-50%) translateY(0) scale(1);
  }
  to {
    transform: translateX(-50%) translateY(60vh) scale(0.3);
  }
}

.goingin-note {
  position: absolute;
  left: 96px;
  bottom: 56px;
  z-index: 4;
}

/* ── 6. the places ───────────────────────────────────────────────────────────────────── */

.place {
  position: relative;
  height: clamp(300px, 52vh, 460px);
  overflow: hidden;
  view-timeline-name: --place;
  view-timeline-axis: block;
  border-bottom: 1px solid var(--border);
}

.place img {
  position: absolute;
  left: 0;
  top: -12%;
  width: 100%;
  height: 124%;
  object-fit: cover;
  animation: place-drift linear both;
  animation-timeline: --place;
  animation-range: cover 0% cover 100%;
}

@keyframes place-drift {
  from {
    transform: translateY(-6%);
  }
  to {
    transform: translateY(6%);
  }
}

.place-scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(to right, rgba(0, 0, 0, 0.72), rgba(0, 0, 0, 0.1) 70%);
}

.place p {
  position: absolute;
  left: 96px;
  right: 96px;
  bottom: 64px;
  font-size: clamp(24px, 3.1vw, 44px);
  line-height: 1.08;
  letter-spacing: -0.03em;
  font-weight: 600;
  max-width: 22ch;
  color: #f2f1ef;
}

/* ── 7. what lands on the timeline ───────────────────────────────────────────────────── */

.row {
  position: relative;
  padding: 120px 0;
  view-timeline-name: --row;
  view-timeline-axis: block;
}

.row h2 {
  padding-inline: 96px;
  margin-bottom: 56px;
}

/*
 * Native horizontal scrolling is the base, because it is the one that works with a finger, with a
 * keyboard and with no script at all. The scroll-bound slide is layered on top of it only where
 * there is a real pointer, and that rule is the only place the overflow is taken away.
 */
.row-track {
  overflow-x: auto;
  scrollbar-width: none;
  padding-inline: 96px;
}

.row-track::-webkit-scrollbar {
  display: none;
}

.row-rail {
  display: flex;
  gap: 64px;
  width: max-content;
}

.row-card {
  width: 300px;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.row-card img {
  width: 220px;
  height: 220px;
  object-fit: contain;
}

.row-card h3 {
  font-size: 26px;
  letter-spacing: -0.02em;
  font-weight: 600;
  line-height: 1.1;
}

.row-card p {
  font-size: 15px;
  line-height: 1.5;
  color: var(--text-muted);
}

.row-n {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.14em;
  color: var(--flare);
}

@media (hover: hover) and (pointer: fine) {
  .row-track {
    overflow-x: hidden;
  }

  /*
   * The travel starts late on purpose. Bound to the whole time the section is anywhere in view, the
   * first card has already slid away by the moment the heading reaches the top of the screen, which
   * is the moment somebody actually starts reading it.
   */
  .row-rail {
    animation: row-slide linear both;
    animation-timeline: --row;
    animation-range: cover 45% cover 100%;
  }

  @keyframes row-slide {
    from {
      transform: translateX(0);
    }
    to {
      transform: translateX(-44%);
    }
  }
}

/* ── 8. what it costs ────────────────────────────────────────────────────────────────── */

.costs {
  position: relative;
  padding: 120px 0;
  overflow: hidden;
  isolation: isolate;
}

.costs > .wrap {
  position: relative;
  z-index: 1;
}

.costs-head {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 48px;
  align-items: end;
  margin-bottom: 64px;
}

.costs-head p {
  font-size: 18px;
  line-height: 1.5;
  color: var(--text-muted);
  max-width: 44ch;
}

.plans {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}

.plan {
  padding: 32px 32px 0 0;
  margin-right: 32px;
  border-top: 1px solid #3a3a3d;
  display: flex;
  flex-direction: column;
  gap: 22px;
}

.plan-featured {
  border-top-color: var(--flare);
}

.plan-featured .tag {
  color: var(--flare);
}

.plan h3 {
  font-size: 32px;
  letter-spacing: -0.025em;
  font-weight: 600;
}

.plan p {
  font-size: 16px;
  line-height: 1.55;
  color: var(--text-muted);
}

.plan-price {
  display: flex;
  align-items: baseline;
  gap: 10px;
}

.plan-price b {
  font-family: var(--font-mono);
  font-size: 36px;
  letter-spacing: -0.02em;
  font-weight: 400;
  color: var(--text-muted);
}

.plan-price i {
  flex: 1;
  border-bottom: 1px dashed #3a3a3d;
  height: 30px;
}

/* The five things a plan switches on, under its name, on hairlines rather than in a box. */
.plan-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
}

.plan-list li {
  font-size: 15px;
  line-height: 1.5;
  padding: 10px 0;
  border-top: 1px solid #1c1c1e;
}

/* ── 9. your own server ──────────────────────────────────────────────────────────────── */

.server {
  position: relative;
  min-height: 100vh;
  display: flex;
  align-items: center;
  padding: 120px 0;
  overflow: hidden;
}

/*
 * Three backdrops, and all three are classes on the <picture>, never on the <img> inside it. An
 * image positioned against its own picture is positioned against a box of no height sitting
 * wherever the flow put it, which is not the section anybody meant.
 */
.server-plate {
  position: absolute;
  inset: 0;
  display: block;
}

.server-plate img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 70% 0;
  opacity: 0.55;
}

/*
 * A sky standing behind one section rather than a whole page. Faded at both ends so it arrives and
 * leaves rather than starting on a line, and kept far enough down that it never reaches the type.
 */
.section-sky {
  position: absolute;
  inset: 0;
  display: block;
}

.section-sky img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 40%;
  opacity: 0.5;
  mask-image: linear-gradient(to bottom, transparent, #000 24%, #000 72%, transparent);
}

/*
 * The same picture laid along the bottom of a section instead of filling it.
 *
 * object-position cannot help here: a sky is much wider than it is tall, so in a tall box cover
 * already fills the height and crops the sides, and moving it vertically does nothing. Giving it
 * the full width and its own height puts the band of light where a horizon belongs.
 */
.section-sky.horizon {
  inset: auto 0 0;
  height: auto;
}

.section-sky.horizon img {
  height: auto;
  opacity: 0.42;
  mask-image: linear-gradient(to bottom, transparent, #000 55%);
}

/*
 * The same plate on a page rather than inside one section. It is bounded and faded out at its foot,
 * because stretched over six thousand pixels of page it stops being light and becomes a brown veil
 * over every word.
 */
.page-plate {
  position: absolute;
  inset: 0 0 auto;
  display: block;
  height: min(120vh, 1100px);
}

.page-plate img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 70% 0;
  opacity: 0.5;
  mask-image: linear-gradient(to bottom, #000 40%, transparent 100%);
}

.server-copy {
  position: relative;
  z-index: 3;
  display: flex;
  flex-direction: column;
  gap: 28px;
  max-width: 600px;
}

.server-copy p {
  font-size: 19px;
  line-height: 1.5;
  max-width: 38ch;
}

.server-shot {
  position: absolute;
  right: 0;
  bottom: 0;
  width: min(660px, 48vw);
  height: 440px;
  overflow: hidden;
  border-top: 1px solid #2c2c2f;
  border-left: 1px solid #2c2c2f;
  background: #000;
  z-index: 2;
}

.server-shot img {
  position: absolute;
  /* Shown at its own pixel size and cropped to the table, not to the settings menu beside it. */
  left: -151.5%;
  top: -59%;
  width: 388%;
  max-width: none;
  height: auto;
}

.server-lock {
  position: absolute;
  right: min(720px, 52vw);
  bottom: 80px;
  width: 160px;
  height: 160px;
  z-index: 3;
}

/* ── 10. the footer ──────────────────────────────────────────────────────────────────── */

/*
 * Torn out of the page above rather than stacked under it. The scallop is an inline SVG sitting on
 * the seam, painted in the colour of the section above, so the footer keeps its own background.
 */
/*
 * Black, not the near-black the canvas drew. A lossy encoder lifts the black a rendered asset was
 * rendered on to about ten of two hundred and fifty five; screened onto pure black that is
 * invisible, and screened onto #17171A it is a visible pale box around every object. So the ground
 * stays black and the torn edge carries the lighter ink instead.
 */
.site-footer {
  position: relative;
  background: #000;
  color: #f2f1ef;
  overflow: hidden;
  padding: 120px 0 48px;
  min-height: min(720px, 76vh);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}

.scallop {
  position: absolute;
  left: 0;
  top: -1px;
  width: 100%;
  height: 40px;
  display: block;
  z-index: 3;
  fill: #17171a;
}

.footer-play {
  position: absolute;
  inset: 40px 0 0;
  z-index: 1;
}

.footer-play img {
  position: absolute;
  width: 110px;
  height: 110px;
  will-change: transform;
}

.footer-score {
  position: absolute;
  right: 48px;
  top: 64px;
  z-index: 4;
}

/*
 * The same lockup, at the largest size on the site. The proportions are the product's own, but the
 * relationship is set by hand here rather than by --size: at three hundred pixels the small half
 * of a seventeen pixel lockup is still a hundred and sixty, which is not a small half any more.
 */
.footer-mark {
  position: relative;
  z-index: 2;
  padding-inline: 48px;
  margin-bottom: 64px;
}

.footer-mark .brand {
  align-items: flex-end;
  gap: clamp(14px, 2.4vw, 34px);
  color: var(--text);
}

.footer-mark .brand img {
  width: clamp(44px, 7.6vw, 108px);
  height: auto;
  margin-bottom: clamp(10px, 2vw, 30px);
}

.footer-mark .brand b {
  font-size: clamp(84px, 20vw, 288px);
  line-height: 0.8;
  letter-spacing: -0.06em;
  font-weight: 700;
}

.footer-mark .brand i {
  font-size: clamp(20px, 3.6vw, 52px);
  letter-spacing: 0.2em;
  color: var(--text-muted);
  margin-bottom: clamp(12px, 2.2vw, 34px);
}

.footer-feet {
  position: relative;
  z-index: 4;
  margin-inline: 48px;
  padding-top: 24px;
  border-top: 1px solid #2c2c2f;
  display: flex;
  flex-wrap: wrap;
  gap: 16px 28px;
  justify-content: space-between;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.1em;
  color: var(--text-muted);
}

.footer-feet nav {
  display: flex;
  flex-wrap: wrap;
  gap: 28px;
}

.footer-feet a {
  text-decoration: none;
}

.footer-feet a:hover {
  color: var(--flare);
}

/* ── the menu ────────────────────────────────────────────────────────────────────────── */

/*
 * Opened by :target so it works with no script at all, which also gives it a real URL and a real
 * back button. Script only adds the escape key and takes the hash back off the address bar.
 */
.menu {
  position: fixed;
  inset: 0;
  z-index: 80;
  background: #17171a;
  display: flex;
  flex-direction: column;
  padding: 0 96px 48px;
  visibility: hidden;
  opacity: 0;
  transition:
    opacity var(--dur-panel) ease-out,
    visibility 0s linear var(--dur-panel);
}

.menu:target,
.menu[data-open] {
  visibility: visible;
  opacity: 1;
  transition-delay: 0s;
}

.menu-head {
  height: 72px;
  display: flex;
  align-items: center;
}

.menu-close {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.1em;
  color: #f2f1ef;
  text-decoration: none;
  border: 0;
  background: none;
  cursor: pointer;
  font-weight: 400;
}

.menu nav {
  margin-top: 88px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.menu nav a {
  display: flex;
  align-items: baseline;
  gap: 24px;
  font-size: clamp(40px, 7.2vw, 104px);
  line-height: 1;
  letter-spacing: -0.045em;
  font-weight: 600;
  text-decoration: none;
  transform: translateY(110%);
}

.menu:target nav a,
.menu[data-open] nav a {
  animation: rise-in 600ms var(--ease-out) forwards;
  animation-delay: calc(var(--i) * 60ms);
}

.menu nav a span {
  font-family: var(--font-mono);
  font-size: 14px;
  letter-spacing: 0.14em;
  color: var(--text-muted);
  width: 32px;
  flex-shrink: 0;
  transition: color 220ms var(--ease-out);
}

/*
 * The item steps to the right and takes the accent with it. translate rather than transform,
 * because transform is already carrying the animation that brings the item in and the two would
 * fight over it.
 */
.menu nav a {
  translate: 0;
  transition:
    color 220ms var(--ease-out),
    translate 380ms var(--ease-out);
}

.menu nav a:hover,
.menu nav a:focus-visible {
  color: var(--flare);
  translate: 18px;
}

.menu nav a:hover span,
.menu nav a:focus-visible span {
  color: var(--flare);
}

.menu-mascot {
  position: absolute;
  right: 120px;
  bottom: 0;
  height: min(52vh, 520px);
  width: auto;
  pointer-events: none;
}

.menu-feet {
  margin-top: auto;
  display: flex;
  flex-wrap: wrap;
  gap: 28px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.1em;
  color: var(--text-muted);
  position: relative;
  z-index: 2;
}

/* ── the page transition ─────────────────────────────────────────────────────────────── */

.slats {
  position: fixed;
  inset: 0;
  z-index: 95;
  display: none;
  pointer-events: none;
}

.slats[data-run] {
  display: block;
}

.slats i {
  position: absolute;
  top: 0;
  width: 12.5%;
  height: 100%;
  background: var(--flare);
  transform: translateY(-100%);
}

.slats[data-run] i {
  animation: slat-down 520ms var(--ease-out) both;
  animation-delay: calc(var(--i) * 40ms);
}

@keyframes slat-down {
  from {
    transform: translateY(-100%);
  }
  to {
    transform: translateY(0);
  }
}

/* ── the sign-in dialog ──────────────────────────────────────────────────────────────── */

.signin {
  position: fixed;
  inset: 0;
  z-index: 85;
  display: grid;
  place-items: center;
  padding: 24px;
  background: rgba(0, 0, 0, 0.75);
  visibility: hidden;
  opacity: 0;
  transition:
    opacity var(--dur-panel) ease-out,
    visibility 0s linear var(--dur-panel);
}

.signin:target {
  visibility: visible;
  opacity: 1;
  transition-delay: 0s;
}

.signin-panel {
  width: min(460px, 100%);
  background: #0b0b0c;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 28px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.signin-panel h2 {
  font-size: 24px;
  letter-spacing: -0.02em;
  font-weight: 600;
}

.signin-panel p {
  font-size: 15px;
  line-height: 1.55;
  color: var(--text-muted);
}

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

.field span {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  color: var(--text-muted);
}

.field input,
.field textarea {
  width: 100%;
  background: transparent;
  border: 0;
  border-bottom: 1px solid #3a3a3d;
  color: var(--text);
  font: inherit;
  font-size: 17px;
  padding: 10px 0;
  border-radius: 0;
}

.field input:focus,
.field textarea:focus {
  outline: 0;
  border-bottom-color: var(--flare);
}

.field textarea {
  resize: vertical;
  min-height: 80px;
}

.signin-preview {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-mono);
  font-size: 14px;
  color: var(--text);
}

/* Announced to a screen reader, never drawn: the stage's own heading, which is the object. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* The one control that is not part of the page's argument, so it sits out of the way of it. */
.theme-toggle {
  position: fixed;
  right: 16px;
  bottom: 16px;
  z-index: 70;
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  background: rgba(11, 11, 12, 0.82);
  color: var(--text-muted);
  cursor: pointer;
  backdrop-filter: blur(6px);
  transition:
    color var(--dur-hover) ease-out,
    border-color var(--dur-hover) ease-out;
}

.theme-toggle:hover {
  color: var(--text);
  border-color: var(--text-muted);
}

.theme-toggle .icon {
  width: 17px;
  height: 17px;
}

/* ── the other pages ─────────────────────────────────────────────────────────────────── */

.page-head {
  position: relative;
  padding: 180px 0 90px;
  overflow: hidden;
  isolation: isolate;
}

.page-head > .wrap {
  position: relative;
  z-index: 1;
}

.page-head .title {
  max-width: 16ch;
}

.page-head p {
  margin-top: 20px;
  font-size: 17px;
  line-height: 1.5;
  color: var(--text-muted);
  max-width: 42ch;
}

/* A head that has something to show beside it, rather than half a page of air. */
.head-split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 460px);
  gap: 64px;
  align-items: end;
}

.head-split > div:last-child {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding-bottom: 8px;
}

/*
 * A band with its heading on the left and its substance on the right, opened by a hairline. This
 * is the workhorse for everything the pages actually have to say: what a plan switches on, what it
 * limits, what happens when one lapses, what is in the box, what we need before day one. It is the
 * alternative to a card, and there is no card anywhere on this site.
 */
.slab {
  display: grid;
  grid-template-columns: minmax(0, 340px) minmax(0, 1fr);
  gap: 48px 64px;
  padding-top: 40px;
  border-top: 1px solid #232325;
  margin-top: 96px;
}

.slab > h2 {
  font-size: clamp(26px, 2.6vw, 38px);
  line-height: 1.02;
  letter-spacing: -0.03em;
  font-weight: 600;
  max-width: 14ch;
}

.slab-lead {
  font-size: 17px;
  line-height: 1.55;
  color: var(--text-muted);
  max-width: 58ch;
  margin-bottom: 8px;
}

.slab-body {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

/* Two columns of short lines, for a set rather than a sequence. */
.set {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px 40px;
  font-size: 15px;
  line-height: 1.5;
}

.set span {
  display: block;
  padding-bottom: 10px;
  border-bottom: 1px solid #1c1c1e;
}

.set i {
  font-style: normal;
  color: var(--text-muted);
}

/* One limit per row: the object it counts, its name, and what reaching it does. */
.limits {
  display: flex;
  flex-direction: column;
}

.limit {
  display: grid;
  grid-template-columns: 64px minmax(0, 220px) minmax(0, 1fr);
  gap: 24px;
  align-items: center;
  padding: 18px 0;
  border-bottom: 1px solid #1c1c1e;
}

.limit img {
  width: 64px;
  height: 64px;
  object-fit: contain;
}

.limit b {
  font-size: 18px;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.limit span {
  font-size: 15px;
  line-height: 1.5;
  color: var(--text-muted);
}

/* What still works when a plan lapses, and what does not. Stated, not illustrated. */
.states {
  display: flex;
  flex-direction: column;
}

.state {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 24px;
  justify-content: space-between;
  padding: 14px 0;
  border-bottom: 1px solid #1c1c1e;
  font-size: 16px;
}

.state b {
  font-weight: 400;
}

.state span {
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.state-on span {
  color: var(--success);
}

.state-off span {
  color: var(--warning);
}

.state-none span {
  color: var(--text-muted);
}

/* A closing invitation, on its own, with nothing else competing for the eye. */
.cta-band {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 24px;
  text-align: center;
  padding: 120px 0 140px;
  margin-top: 96px;
  border-top: 1px solid #232325;
}

.cta-band p {
  font-size: 17px;
  line-height: 1.5;
  color: var(--text-muted);
  max-width: 44ch;
}

.cta-band img {
  width: clamp(120px, 14vw, 180px);
  height: auto;
}

/* The three things that are true of every installation, numbered because they are an argument. */
.points {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 40px;
  margin-top: 72px;
}

.point {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding-top: 20px;
  border-top: 1px solid #3a3a3d;
}

.point b {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.14em;
  color: var(--flare);
  font-weight: 400;
}

.point h3 {
  font-size: 20px;
  letter-spacing: -0.02em;
  font-weight: 600;
  line-height: 1.15;
}

.point p {
  font-size: 15px;
  line-height: 1.55;
  color: var(--text-muted);
}

/* Product: a rail with a marker that tracks the scroll, and a screen for each stop. */
.rail {
  position: relative;
  padding-bottom: 120px;
  view-timeline-name: --rail;
  view-timeline-axis: block;
}

.rail-line {
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 120px;
  width: 1px;
  background: #3a3a3d;
}

.rail-marker {
  position: absolute;
  left: calc(50% - 12px);
  top: 0;
  width: 25px;
  height: 25px;
  border-radius: 50%;
  border: 1px solid var(--flare);
  background: var(--flare);
  animation: rail-run linear both;
  animation-timeline: --rail;
  animation-range: cover 10% cover 90%;
  z-index: 2;
}

@keyframes rail-run {
  from {
    top: 0;
  }
  to {
    top: calc(100% - 145px);
  }
}

.rail-stop {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 64px;
  align-items: center;
  padding: 60px 0;
}

.rail-stop:nth-child(even) .rail-stop-copy {
  order: 2;
}

.rail-stop-copy {
  display: flex;
  flex-direction: column;
  gap: 16px;
  max-width: 42ch;
}

.rail-stop-copy h2 {
  font-size: 34px;
  letter-spacing: -0.03em;
  font-weight: 600;
  line-height: 1.05;
}

.rail-stop-copy p {
  font-size: 16px;
  line-height: 1.55;
  color: var(--text-muted);
}

.rail-stop-copy ul {
  margin: 4px 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.rail-stop-copy li {
  font-size: 15px;
  line-height: 1.5;
  padding-bottom: 8px;
  border-bottom: 1px solid #1c1c1e;
}

.rail-object {
  display: flex;
  justify-content: center;
}

.rail-object img {
  width: clamp(140px, 18vw, 240px);
  height: auto;
}

.shot-frame {
  border: 1px solid #3a3a3d;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: #000;
}

.shot-frame img {
  width: 100%;
  height: auto;
}

/* Pricing: the refusal, drawn. */
.refusal {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  border: 1px solid #3a3a3d;
  background: #0b0b0c;
  padding: 16px 18px;
  font-size: 14px;
  line-height: 1.5;
  max-width: 420px;
}

.refusal .icon {
  color: var(--flare);
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  margin-top: 3px;
}

/* Security: a list of what is built, not a badge wall. */
.facts {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding-top: 12px;
  border-top: 1px solid #3a3a3d;
  font-size: 16px;
}

.facts div {
  display: grid;
  grid-template-columns: 20px 1fr;
  gap: 12px;
}

/* The same shape for what we cannot do, which has to be as easy to read as what we can. */
.facts-no {
  border-top-color: #3a3a3d;
  color: var(--text-muted);
}

.facts-no .icon {
  color: var(--text-muted) !important;
}

.facts .icon {
  color: var(--flare);
  width: 16px;
  height: 16px;
  margin-top: 4px;
}

/* The same shape for what we cannot do, which has to be as easy to read as what we can. */
.facts-no {
  border-top-color: #3a3a3d;
  color: var(--text-muted);
}

.facts-no .icon {
  color: var(--text-muted);
}

/* Security: the handset closed, with the lock in front of it. Nothing on that page moves. */
.locked {
  position: relative;
  display: flex;
  justify-content: center;
  padding: 80px 0 120px;
}

.locked-handset {
  height: min(58vh, 520px);
  width: auto;
}

.locked-lock {
  position: absolute;
  left: 50%;
  bottom: 100px;
  width: clamp(110px, 14vw, 180px);
  height: auto;
  transform: translateX(-50%);
}

/* Setup: the handset running backwards, from assembled to at rest. */
.steps {
  display: flex;
  flex-direction: column;
}

.step {
  display: grid;
  grid-template-columns: 72px minmax(0, 200px) minmax(0, 1fr);
  gap: 12px 32px;
  align-items: start;
  border-top: 1px solid #232325;
  padding: 28px 0;
}

.step-note {
  grid-column: 3;
  font-size: 14px;
  line-height: 1.5;
  color: var(--text-muted);
  border-left: 1px solid #3a3a3d;
  padding-left: 16px;
}

.step-featured {
  border-top-color: var(--flare);
}

.step b {
  font-family: var(--font-mono);
  font-size: 36px;
  line-height: 1;
  letter-spacing: -0.03em;
  font-weight: 400;
  color: var(--text-muted);
}

.step-featured b {
  color: var(--text);
}

.step h3 {
  font-size: 22px;
  letter-spacing: -0.02em;
  font-weight: 600;
  line-height: 1.15;
}

.step > p {
  font-size: 16px;
  line-height: 1.55;
  color: var(--text-muted);
}

/* Nudged onto the same optical line as the title beside it, which is set much larger. */
.step-when {
  grid-column: 2;
  padding-top: 6px;
}

.assembly {
  position: relative;
  height: 200vh;
  view-timeline-name: --assembly;
  view-timeline-axis: block;
}

.assembly-pin {
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

.assembly-frames {
  position: relative;
  width: min(34vh, 300px);
  aspect-ratio: 418 / 627;
}

.assembly-frames .stage-frame {
  animation-timeline: --assembly;
}

/* Which of the four steps the object is at, switched on the quarters of the same progress. */
.assembly-say {
  position: relative;
  width: min(46ch, 42vw);
  height: 3.4em;
  margin-top: 40px;
}

.assembly-say p {
  position: absolute;
  inset: 0;
  font-size: clamp(19px, 2vw, 26px);
  line-height: 1.2;
  letter-spacing: -0.02em;
  font-weight: 500;
  opacity: 0;
  animation-timing-function: steps(1, end);
  animation-fill-mode: both;
  animation-timeline: --assembly;
  animation-range: contain 0% contain 100%;
}

.assembly-say p:nth-child(1) {
  animation-name: step-1;
}

.assembly-say p:nth-child(2) {
  animation-name: step-2;
}

.assembly-say p:nth-child(3) {
  animation-name: step-3;
}

.assembly-say p:nth-child(4) {
  animation-name: step-4;
}

@keyframes step-1 {
  0% {
    opacity: 1;
  }
  25% {
    opacity: 0;
  }
}

@keyframes step-2 {
  0% {
    opacity: 0;
  }
  25% {
    opacity: 1;
  }
  50% {
    opacity: 0;
  }
}

@keyframes step-3 {
  0% {
    opacity: 0;
  }
  50% {
    opacity: 1;
  }
  75% {
    opacity: 0;
  }
}

@keyframes step-4 {
  0% {
    opacity: 0;
  }
  75% {
    opacity: 1;
  }
  100% {
    opacity: 1;
  }
}

/* Contact: a form that reaches a person even before there is an endpoint. */
.contact {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 480px;
  gap: 64px;
  align-items: start;
  padding-bottom: 140px;
}

.contact-place {
  position: absolute;
  left: 50%;
  width: 100vw;
  transform: translateX(-50%);
  bottom: 0;
  height: 52%;
  z-index: 0;
}

.contact-place img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 40%;
  /* Faded in at the top, or the photograph starts on a hard horizontal line across the page. */
  mask-image: linear-gradient(to bottom, transparent, #000 22%);
}

.contact-place::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, #000 0%, rgba(0, 0, 0, 0.2) 60%, rgba(0, 0, 0, 0.6) 100%);
}

/*
 * Everything except the place, which is the one child that has to stay out of the grid and behind
 * the rest. Without the exclusion it becomes a column of its own and pushes the form into the row
 * below, which is how a full-bleed photograph turns into a small grey strip.
 */
.contact > *:not(.contact-place) {
  position: relative;
  z-index: 3;
}

.contact-form {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.contact-who {
  display: flex;
  flex-direction: column;
  gap: 24px;
  max-width: 42ch;
}

.contact-who p {
  font-size: 16px;
  line-height: 1.55;
}

/* A note under a form, not a label: sentence case, because a long line of capitals is a wall. */
.form-note {
  font-size: 14px;
  line-height: 1.55;
  color: var(--text-muted);
}

.contact-who .tag {
  display: block;
  margin-bottom: 6px;
}

.contact-who .mono {
  font-size: 16px;
  color: var(--text);
}

.choices {
  border: 0;
  padding: 0;
  margin: 8px 0 0;
  display: flex;
  flex-wrap: wrap;
  gap: 10px 20px;
}

.choices legend {
  padding: 0;
  margin-bottom: 12px;
}

.choice {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 15px;
  cursor: pointer;
}

.choice input {
  accent-color: var(--flare);
  width: 16px;
  height: 16px;
  margin: 0;
}

/* Legal: read by people who are worried. Quiet, wide-leaded, nothing else on the page. */
.legal {
  display: grid;
  grid-template-columns: 240px minmax(0, 68ch);
  gap: 80px;
  padding-bottom: 140px;
}

.legal-toc {
  position: sticky;
  top: 96px;
  align-self: start;
  display: flex;
  flex-direction: column;
  gap: 10px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.06em;
  color: var(--text-muted);
}

.legal-toc a {
  text-decoration: none;
}

.legal-toc a:hover {
  color: var(--flare);
}

.legal-body {
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.legal-body h1 {
  font-size: clamp(34px, 4vw, 52px);
  line-height: 1.02;
  letter-spacing: -0.035em;
  font-weight: 600;
}

.legal-body h2 {
  font-size: 24px;
  letter-spacing: -0.015em;
  font-weight: 600;
  margin-top: 24px;
}

.legal-body p,
.legal-body li {
  font-size: 18px;
  line-height: 1.7;
}

.legal-body > p:first-of-type {
  font-size: 20px;
  line-height: 1.5;
  color: var(--text-muted);
}

.legal-body hr {
  border: 0;
  border-top: 1px solid var(--border);
  margin: 8px 0;
}

.legal-body section {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.legal-body ul {
  margin: 0;
  padding-left: 22px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.legal-body a {
  color: var(--flare-link);
  text-underline-offset: 3px;
}

/* 404 */
.lost {
  position: relative;
  min-height: 100vh;
  display: flex;
  align-items: center;
  padding: 140px 0;
  overflow: hidden;
}

.lost-plate {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center bottom;
  opacity: 0.7;
}

.lost-mascot {
  position: absolute;
  right: 10vw;
  top: 50%;
  height: min(76vh, 760px);
  width: auto;
  transform: translateY(-50%);
  z-index: 2;
}

/* ── the light theme ─────────────────────────────────────────────────────────────────── */

/*
 * Only the pages that are read rather than looked at: privacy, terms and deleting your data.
 *
 * Everywhere else the page is a dark room with objects lit in it, and those objects are composited
 * with mix-blend-mode: screen, which on a light panel is a black rectangle. Lightening half a page
 * and leaving the other half black looked broken, and lightening all of it would black out every
 * object on the site. So the toggle is rendered on the three pages where it changes something and
 * nowhere else: a control that appears to do nothing is worse than one that is not there.
 *
 * And the attribute goes on this element rather than on the document, which is the whole point of
 * the selectors below reading .legal-main[data-theme]. On the document it swapped twenty-six
 * tokens for every panel on the site, including the ones whose dark ground is written into the
 * stylesheet and cannot swap with it: the menu, the sign-in dialog and the footer all became dark
 * ink on a dark panel, which is a page with nothing on it.
 */
.legal-main[data-theme='light'] {
  background: var(--bg);
  color: var(--text);
}

.legal-main[data-theme='light'] .grain {
  opacity: 0.02;
}

.legal-main[data-theme='light'] .legal-body p,
.legal-main[data-theme='light'] .legal-body li {
  color: var(--text);
}

.legal-main[data-theme='light'] .legal-body > p:first-of-type,
.legal-main[data-theme='light'] .legal-toc {
  color: var(--text-muted);
}

.legal-main[data-theme='light'] .legal-body hr {
  border-top-color: var(--border-strong);
}

/*
 * The island stays dark on the light pages too. It is the one piece of the brand that is on every
 * screen, and turning it white on three of ten pages made it a different object on those pages.
 * Its own contrast is settled: light type on a dark panel, wherever it is floating.
 */
/* Opaque over a light page: a dark panel at three quarters over white is grey, not dark. */
body:has(.legal-main[data-theme='light']) .site-header {
  background: #16161a;
  border-color: rgba(242, 241, 239, 0.14);
}

/* ── narrower ────────────────────────────────────────────────────────────────────────── */

@media (max-width: 1100px) {
  .wrap,
  .menu {
    padding-inline: 48px;
  }

  .stage-lines,
  .goingin-note {
    left: 48px;
  }

  .place p {
    left: 48px;
    right: 48px;
  }

  .row h2,
  .row-track {
    padding-inline: 48px;
  }

  .costs-head {
    grid-template-columns: 1fr;
    gap: 24px;
  }

  .plans {
    grid-template-columns: 1fr;
    gap: 8px;
  }

  .plan {
    margin-right: 0;
  }

  .rail-stop,
  .contact {
    grid-template-columns: 1fr;
    gap: 32px;
  }

  .rail-stop:nth-child(even) .rail-stop-copy {
    order: 0;
  }

  .rail-line,
  .rail-marker {
    display: none;
  }

  .slab,
  .head-split {
    grid-template-columns: 1fr;
    gap: 24px;
  }

  .points {
    grid-template-columns: 1fr;
    gap: 24px;
  }

  .step {
    grid-template-columns: 56px minmax(0, 1fr);
  }

  /*
   * The text goes beside the number too. Left to auto-place, it fell into the 56px number column
   * on its own row, one word to a line, which is what the setup page looked like on a phone.
   */
  .step-when,
  .step-note,
  .step > div {
    grid-column: 2;
  }

  .legal {
    grid-template-columns: 1fr;
    gap: 32px;
  }

  .legal-toc {
    position: static;
  }

  .server-shot {
    display: none;
  }

  .server-lock {
    right: 48px;
  }

  /*
   * Below this the side columns are too narrow to hold anybody, so the two flanks come down and
   * stand under the words in one row. It is the same five things, in the only other arrangement
   * they fit into.
   */
  .hero-stage {
    grid-template-columns: 1fr 1fr;
    gap: 0 clamp(8px, 2vw, 32px);
    margin-top: clamp(4px, 1.5vh, 20px);
  }

  .hero-copy {
    grid-column: 1 / -1;
    order: -1;
  }

  .flank {
    height: clamp(170px, 26vh, 260px);
  }

  .f-mascot {
    right: 0;
    bottom: 8%;
    width: min(76%, 230px);
  }

  .f-handset {
    left: 12%;
    height: 94%;
  }

  .f-bubble {
    left: 2%;
    width: min(28%, 76px);
  }

  .f-note {
    right: 2%;
    width: min(28%, 80px);
  }

  .f-bell {
    right: 30%;
    bottom: 0;
    width: min(24%, 68px);
  }
}

@media (max-width: 768px) {
  .wrap,
  .menu {
    padding-inline: 20px;
  }

  .site-header {
    top: 10px;
    height: 48px;
    padding: 0 6px 0 14px;
  }

  .menu-open {
    height: 34px;
    padding: 0 13px;
  }

  .site-nav {
    gap: 14px;
  }

  /* The five destinations live in the menu on a phone; only the way in stays in the bar. */
  .site-nav a {
    display: none;
  }

  .hero {
    padding: 80px 0 40px;
    gap: 18px;
  }

  .hero-actions {
    width: 100%;
  }

  .hero-actions .btn {
    flex: 1 1 auto;
  }

  /* A shorter line than the copy needs, so the cast is what gives when the window is small. */
  .hero-copy .lead {
    font-size: 16px;
    max-width: 32ch;
  }

  .flank {
    height: clamp(150px, 24vh, 220px);
  }

  .f-mascot {
    right: 0;
    width: 78%;
  }

  .f-handset {
    left: 10%;
  }

  .f-bubble {
    left: 0;
    width: 30%;
  }

  .f-note {
    right: 0;
    width: 30%;
  }

  /* One thing too many on a small screen: it lands on the handset. */
  .f-bell {
    display: none;
  }

  /* Two viewport heights of held scroll rather than three: a thumb travels further per stroke. */
  .stage {
    height: 200vh;
  }

  .stage-frames {
    width: min(44vh, 280px);
    transform: translate(-50%, -52%);
  }

  .stage-lines {
    inset: 84px 20px 48px;
  }

  .stage-line,
  .stage-line:nth-child(5) {
    width: 100%;
  }

  /* Four stamps at this width sit on top of the object. Two, at the edges, and the other two go. */
  .sat-2,
  .sat-3 {
    display: none;
  }

  .sat-1 {
    left: 4%;
    top: 26%;
    --from-x: 20vw;
    --out-x: 0vw;
    --out-y: -4vh;
  }

  .sat-4 {
    left: 58%;
    top: 62%;
    --from-x: -20vw;
    --out-x: 0vw;
    --out-y: -4vh;
  }

  .sat svg {
    width: 96px;
    height: 96px;
  }

  .goingin {
    height: 160vh;
  }

  .goingin-shot {
    width: 150vw;
  }

  .goingin-note {
    left: 20px;
    bottom: 32px;
  }

  .place {
    height: clamp(280px, 42vh, 340px);
  }

  .place p {
    left: 20px;
    right: 20px;
    bottom: 24px;
  }

  .place-scrim {
    background: linear-gradient(to top, rgba(0, 0, 0, 0.82), rgba(0, 0, 0, 0.12));
  }

  .row,
  .costs,
  .server {
    padding: 72px 0;
  }

  .row h2 {
    padding-inline: 20px;
    margin-bottom: 32px;
  }

  .row-track {
    padding-inline: 20px;
  }

  .row-rail {
    gap: 28px;
  }

  .row-card {
    width: 190px;
  }

  .row-card img {
    width: 130px;
    height: 130px;
  }

  .row-card h3 {
    font-size: 20px;
  }

  .row-card p {
    font-size: 13px;
  }

  .server {
    min-height: auto;
  }

  .server-lock {
    display: none;
  }

  .set,
  .limit {
    grid-template-columns: 1fr;
  }

  .limit {
    gap: 10px;
  }

  .limit img {
    width: 52px;
    height: 52px;
  }

  .slab {
    margin-top: 64px;
  }

  .cta-band {
    padding: 72px 0 88px;
    margin-top: 64px;
  }

  .assembly {
    height: 160vh;
  }

  .site-footer {
    min-height: 560px;
    padding-top: 88px;
  }

  .footer-mark {
    padding-inline: 20px;
    margin-bottom: 40px;
  }

  .footer-mark .brand {
    gap: 10px;
  }

  .footer-mark .brand i {
    margin-bottom: 8px;
  }

  .footer-score {
    right: 20px;
    top: 56px;
  }

  .footer-feet {
    margin-inline: 20px;
    font-size: 10px;
    gap: 12px;
  }

  .footer-feet nav {
    gap: 14px;
  }

  /*
   * On a phone the objects landed on the footer links and hid them. Here they are smaller and the
   * floor they settle on stops above the links, so the toy stays and the links stay readable and
   * tappable. The script reads each object's size and the yard's height from the layout.
   */
  .footer-play {
    inset: 40px 0 72px;
  }

  .footer-play img {
    width: 72px;
    height: 72px;
  }

  .menu nav {
    margin-top: 48px;
  }

  .menu-mascot {
    right: 0;
    height: 34vh;
    opacity: 0.5;
  }

  .contact-form {
    order: -1;
  }
}

/* ── when the browser has never heard of a scroll timeline ───────────────────────────── */

/*
 * Everything sits on its final frame. The stage shows what it shows at 100 percent: the handset
 * open, the product where the panel was, all four stamps landed in the corners. Nothing is missing
 * from the argument, only from the journey.
 */
@supports not (animation-timeline: view()) {
  .stage {
    height: auto;
  }

  .stage-pin {
    position: static;
    height: 100vh;
  }

  .stage-frame:nth-child(-n + 5) {
    opacity: 0;
  }

  .stage-frame:nth-child(6) {
    opacity: 0.45;
    transform: scale(1.25);
    clip-path: inset(40% 0 0 0);
  }

  .stage-shot {
    opacity: 1;
    box-shadow: 0 0 120px rgba(255, 236, 210, 0.25);
  }

  /* Only the closing sentence, or all five stack on top of each other. */
  .stage-line:nth-child(-n + 4) {
    opacity: 0;
  }

  .stage-line:nth-child(5) {
    opacity: 1;
  }

  .sat {
    transform: translate(var(--out-x), var(--out-y));
  }

  .sat-in {
    opacity: 1;
    transform: none;
  }

  .goingin {
    height: auto;
  }

  .goingin-pin {
    position: static;
  }

  .goingin-shot {
    transform: translate(-50%, -50%) scale(1.3);
  }

  .goingin-mascot {
    transform: translateX(-50%) translateY(20vh) scale(0.6);
  }

  .place img {
    transform: none;
  }

  .row-rail {
    transform: none;
  }

  .row-track {
    overflow-x: auto;
  }

  .rail-marker {
    display: none;
  }

  .assembly {
    height: auto;
  }

  .assembly-pin {
    position: static;
  }

  .assembly-frames .stage-frame:nth-child(-n + 5) {
    opacity: 0;
  }

  .assembly-say p:not(:last-child) {
    opacity: 0;
  }

  .assembly-say p:last-child {
    opacity: 1;
  }
}

/* ── when somebody has asked for less movement ───────────────────────────────────────── */

/*
 * No preloader, no curtain, no parallax, no slats, no falling objects. The stage stops being a
 * held scroll and becomes what it always was underneath: six pictures of one object opening, with
 * the four sentences beside them. It is a worse experience and a complete one, which is the deal.
 */
@media (prefers-reduced-motion: reduce) {
  .preloader,
  .curtain,
  .slats {
    display: none !important;
  }

  .rise > span,
  .settle,
  .menu:target nav a,
  .menu[data-open] nav a {
    animation: none;
    opacity: 1;
    transform: none;
  }

  .flank img,
  .menu nav a,
  .site-nav .roll {
    transition: none;
    translate: none;
  }

  .stage {
    height: auto;
    padding: 96px 0;
  }

  .stage-pin {
    position: static;
    height: auto;
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    gap: 32px 24px;
    align-items: end;
    padding-inline: 96px;
  }

  .stage-frames {
    position: static;
    display: contents;
  }

  .stage-frame {
    position: static;
    inset: auto;
    width: auto;
    opacity: 1 !important;
    transform: none !important;
    clip-path: none !important;
    animation: none;
    aspect-ratio: 418 / 627;
  }

  .stage-shot,
  .sat {
    display: none;
  }

  .stage-lines {
    position: static;
    inset: auto;
    grid-column: 1 / -1;
    display: flex;
    flex-direction: column;
    gap: 12px;
    max-width: 70ch;
  }

  .stage-line,
  .stage-line:nth-child(5) {
    position: static;
    inset: auto;
    width: auto;
    opacity: 1 !important;
    animation: none;
    font-size: 19px;
  }

  .goingin {
    height: auto;
  }

  .goingin-pin {
    position: static;
    height: auto;
    padding: 96px 0;
  }

  .goingin-shot {
    position: static;
    width: 100%;
    transform: none;
    animation: none;
    opacity: 1;
  }

  .goingin-mascot,
  .goingin-vignette {
    display: none;
  }

  .goingin-note {
    position: static;
    margin: 24px 96px 0;
  }

  .place img,
  .row-rail,
  .rail-marker {
    animation: none;
    transform: none;
  }

  .row-track {
    overflow-x: auto;
  }

  .footer-play {
    display: none;
  }

  /* Nothing falls here now, so the room the falling needed goes as well. */
  .site-footer {
    min-height: 0;
    padding-top: 96px;
  }

  .assembly {
    height: auto;
  }

  .assembly-pin {
    position: static;
    height: auto;
    padding: 48px 0;
  }

  .assembly-frames {
    width: min(34vh, 260px);
  }

  .assembly-frames .stage-frame:nth-child(-n + 5) {
    display: none;
  }

  .assembly-say {
    height: auto;
    display: flex;
    flex-direction: column;
    gap: 10px;
  }

  .assembly-say p {
    position: static;
    inset: auto;
    opacity: 1 !important;
    animation: none;
  }
}

@media (prefers-reduced-motion: reduce) and (max-width: 1100px) {
  .stage-pin {
    grid-template-columns: repeat(3, 1fr);
    padding-inline: 48px;
  }

  .goingin-note {
    margin-inline: 48px;
  }
}

@media (prefers-reduced-motion: reduce) and (max-width: 768px) {
  .stage-pin {
    grid-template-columns: repeat(2, 1fr);
    padding-inline: 20px;
  }

  .goingin-note {
    margin-inline: 20px;
  }
}
